1. 程式人生 > >Spring中AOP功能在實踐中具體應用

Spring中AOP功能在實踐中具體應用

spring以秋風掃落葉的姿態進入java開發的方方面面,特別是在大型的企業級開發上面,所依靠的主要武器就是ioc和aop,關於ioc和aop的具體含義和意義,網上已經說得相當明白了.

通俗來說ioc就相當於一箇中介,它可以幫你管理各個類之間呼叫等關係,使用時直接用@Autowired引用,而不用再自己通過new來建立物件.aop則是相當於你的祕書,你自己只需要專注核心業務邏輯,而一些邊緣和不重要的功能統統可以交給他,比如說,統計每個方法的執行時間,操作日誌等等,都可以交給aop.

有了ioc和aop這2個利器,在大型企業級的開發中,將大大降低專案的複雜度,使得非常方便的在後期進行修改和優化.

這篇部落格主要講解aop的使用流程,aop的基本知識,大家網上一搜全是,這次主要講解aop在專案中的真實應用.具體場景就是:將專案中的操作日誌寫入到資料,比如說是:在2018-10-16 18:59:23張三查詢了來訪記錄的歷史資料,這樣有利於後期劃定責任

1,想要使用aop的功能需要在配置檔案進行如下的配置

<aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true"/>

2,第一步先寫個列舉類(enum),有利於後面的使用,程式碼如下

public enum OperateEnum {
    OPERATE_ADD("operate_add","新增操作"),
    OPERATE_INQUIRE("operate_inquire","查詢操作"),
    OPERATE_UPDATE("operate_update","更新操作"),
    OPERATE_DELETE("operate_delete","刪除操作");
    
    private String code;  
    
    private String msg;  
  
    private OperateEnum(String code, String msg) {  
        this.code = code;  
        this.msg = msg;  
    }  
  
    public String getCode() {  
        return code;  
    }  
  
    public void setCode(String code) {  
        this.code = code;  
    }  
  
    public String getMsg() {  
        return msg;  
    }  
  
    public void setMsg(String msg) {  
        this.msg = msg;  
    }  
  
      
    public static String getByValue(String code) {  
        for (OperateEnum operateEnum : values()) {  
            if (operateEnum.getCode().equals(code)) {  
                return operateEnum.getMsg();  
            }  
        }  
        return null;  
    } 
}

3,自定義一個註解類,用於每一個需要記錄的方法上面,程式碼如下

@Target({ElementType.PARAMETER, ElementType.METHOD})    
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperateLog {
	OperateEnum operate() default OperateEnum.INQUIRE;
	String description()  default "";
}

至於各個註解的具體解釋,大家可以自行百度,都解釋的很清楚

4,將註解類配置到你要控制的方法上面,如下所示

@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(value="/save",method =RequestMethod.POST)
    @ResponseBody
    @OperateLog(operate = OperateEnum.OPERATE_ADD,description="新增操作")
    public RespData save(HttpServletRequest request, HttpServletResponse response,User user){
		RespData respData = userService.save(user);
		return respData;
    }
}

5,編寫切面類,切面類是具體負責處理的類,這樣分割核心業務和邊緣業務的好處就是,前期可以專注於編寫核心業務,而不用考慮邊緣業務,後期可以隨意新增,這樣就將兩者分割開了,程式碼如下:

@Component
@Aspect
public class OperateAspect {
	@Pointcut("@annotation(com.lgg.aspect.OperateLog)")//括號內為註解類OperateLog的具體路徑
	public  void  operatePointCut(){}//宣告一個切入點
	
	@AfterReturning(pointcut = "operatePointCut()",returning = "result")
	public  void  save(JoinPoint joinPoint,Object result){
		//獲取引數
        Object[] objs=joinPoint.getArgs();
        //獲取返回值
        Object obj=objs[0];
        Map<String ,Object> inMap = getParameter(obj);
        Map<String ,Object> outMap = getParameter(result);
		// 獲取註解自定義引數
		OperateLog operateLog = getOperateLog(joinPoint);
		//接下來將inMap,outMap,operateLog按照需要寫入到資料即可
	}
	//拓展日誌的功能,對攔截的入參進行反射獲取資訊
    private Map<String, Object> getParameter(Object obj) {
        try {
            //反射物件中的屬性
            Class clazz=obj.getClass();
            Field[] fields= clazz.getDeclaredFields();
            Map<String,Object> resultMap=new java.util.HashMap<>();
            //遍歷並返回
            for(Field field:fields){
                String fieldName=field.getName();
                PropertyDescriptor pd=new PropertyDescriptor(fieldName,clazz);
                Method readMethod = pd.getReadMethod();
                Object resultObj= readMethod.invoke(obj);
                resultMap.put(fieldName,resultObj);
            }
            return resultMap;
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
	
    private static OperateLog getOperateLog(JoinPoint joinPoint) throws Exception{
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;  
        Method method = methodSignature.getMethod();
        if(method != null)
        {
            return method.getAnnotation(OperateLog.class);
        }
        return null;
    }
}