1. 程式人生 > 其它 >springboot中定義切面

springboot中定義切面

技術標籤:springbootspringaop

1.基礎概念
1、切面(Aspect)
首先要理解‘切’字,需要把物件想象成一個立方體,傳統的面向物件變成思維,類定義完成之後(封裝)。每次例項化一個物件,對類定義中的成員變數賦值,就相當於對這個立方體進行了一個定義,定義完成之後,那個物件就在那裡,不卑不亢,不悲不喜,等著被使用,等著被回收。

面向切面程式設計則是指,對於一個我們已經封裝好的類,我們可以在編譯期間或在執行期間,對其進行切割,把立方體切開,在原有的方法裡面新增(織入)一些新的程式碼,對原有的方法程式碼進行一次增強處理。而那些增強部分的程式碼,就被稱之為切面,如下面程式碼例項中的通用日誌處理程式碼,常見的還有事務處理、許可權認證等等。

2、切入點(PointCut)
要對哪些類中的哪些方法進行增強,進行切割,指的是被增強的方法。即要切哪些東西。

3、連線點(JoinPoint)
我們知道了要切哪些方法後,剩下的就是什麼時候切,在原方法的哪一個執行階段加入增加程式碼,這個就是連線點。如方法呼叫前,方法呼叫後,發生異常時等等。

4、通知(Advice)
通知被織入方法,改如何被增強。定義切面的具體實現。那麼這裡面就涉及到一個問題,空間(切哪裡)和時間(什麼時候切,在何時加入增加程式碼),空間我們已經知道了就是切入點中定義的方法,而什麼時候切,則是連線點的概念,如下面例項中,通用日誌處理(切面),@Pointcut規則中指明的方法即為切入點,@Before、@After是連線點,而下面的程式碼就是對應通知。

@Before(“cutMethod()”)
public void begin() {
System.out.println("@Before lingyejun blog logger : begin");
}
5、目標物件(Target Object)
被一個或多個切面所通知的物件,即為目標物件。

6、AOP代理物件(AOP Proxy Object)
AOP代理是AOP框架所生成的物件,該物件是目標物件的代理物件。代理物件能夠在目標物件的基礎上,在相應的連線點上呼叫通知。

7、織入(Weaving)
將切面切入到目標方法之中,使目標方法得到增強的過程被稱之為織入。

2.例項
(1)依賴包

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

(2)自定義註解

@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    //模組
    String title() default "";
    //業務
    BusinessType businessType() default BusinessType.OTHER;
    //操作人類別
    OperatorType operatorType() default OperatorType.MANAGE;
    //是否儲存請求的引數
    boolean isSaveRequestData() default true;

}

(3)切面

@Component
@Aspect
@Slf4j
public class LogAspect {

    @Pointcut("@annotation(com.mysuper.demo.annotation.Log)")
    public void logPointCut(){

    }

    //處理完請求後執行
    @AfterReturning(pointcut = "logPointCut()",returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint,Object jsonResult){
        log.info("注意,這個模組有所操作");
        handleLog(joinPoint,null,jsonResult);
    }

    @Before("logPointCut()")
    public void doBefore(){
        log.info("before");
    }

    public void handleLog(final JoinPoint joinPoint,final Exception e, Object jsonResult){

        Log controllerLog = getAnnotation(joinPoint);
        if(controllerLog == null){
            return;
        }

        //獲取當前登入使用者
        LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());

        //資料庫日誌
        SysOperLog operLog = new SysOperLog();
        operLog.setStatus(BusinessStatus.SUCCESS.ordinal());

        //請求的地址
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        operLog.setOperIp(ip);

        //返回引數
        operLog.setJsonResult(JSON.toJSONString(jsonResult));
        operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());

        if(loginUser != null){
            operLog.setOperName(loginUser.getUsername());
        }

        if(e != null){
            operLog.setStatus(BusinessStatus.FAIL.ordinal());
            operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
        }

        //設定方法名稱
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        operLog.setMethod(className + "." + methodName);

        //設定請求方式
        operLog.setRequestMethod(ServletUtils.getRequest().getMethod());

        //處理設定註解上的引數
        getControllerMethodDescription(joinPoint, controllerLog, operLog);
        // 儲存資料庫
        AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
    }

    //獲取註解中對方法得描述資訊
    public void getControllerMethodDescription(JoinPoint joinPoint,Log log,SysOperLog operLog){

        //設定action動作
        operLog.setBusinessType(log.businessType().ordinal());
        //設定標題
        operLog.setTitle(log.title());
        //設定操作人類別
        operLog.setOperatorType(log.operatorType().ordinal());
        // 是否需要儲存request,引數和值
        if (log.isSaveRequestData())
        {
            // 獲取引數的資訊,傳入到資料庫中。
            setRequestValue(joinPoint, operLog);
        }

    }

    //獲取註解
    public Log getAnnotation(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if(method != null){
            return method.getAnnotation(Log.class);
        }
        return null;
    }

    //獲取請求的引數,存放到log中
    private void setRequestValue(JoinPoint joinPoint,SysOperLog operLog){
        String requestMethod = operLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
        {
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
        }
        else
        {
            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
        }
    }

    /**
     * 引數拼裝
     */
    private String argsArrayToString(Object[] paramsArray)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (int i = 0; i < paramsArray.length; i++)
            {
                if (!isFilterObject(paramsArray[i]))
                {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    params += jsonObj.toString() + " ";
                }
            }
        }
        return params.trim();
    }

    /**
     * 判斷是否需要過濾的物件。
     *
     * @param o 物件資訊。
     * @return 如果是需要過濾的物件,則返回true;否則返回false。
     */
    public boolean isFilterObject(final Object o)
    {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}

(4)應用

@RestController
public class LoginController {

    @Autowired
    private ISysLoginService sysLoginService;


    @Log(title = "登入模組",businessType = BusinessType.OTHER)
    @RequestMapping("/login")
    public AjaxResult login(@RequestBody LoginBody loginBody){
        AjaxResult ajaxResult = new AjaxResult();
        String token =  sysLoginService.Login(loginBody.getUsername(),loginBody.getPassword(),loginBody.getCode(),loginBody.getUuid());
        ajaxResult.put(Constants.TOKEN,token);
        return ajaxResult;
    }

}