1. 程式人生 > >SpringBoot之整合Spring AOP

SpringBoot之整合Spring AOP

package com.zkn.learnspringboot.aop;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Enumeration;
import java.util.Map;

/**
 * Created by zkn on 2016/11/18.
 */
@Component
@Aspect
public class WebControllerAop {

    //匹配com.zkn.learnspringboot.web.controller包及其子包下的所有類的所有方法
    @Pointcut("execution(* com.zkn.learnspringboot.web.controller..*.*(..))")
    public void executeService(){

    }

    /**
     * 前置通知,方法呼叫前被呼叫
     * @param joinPoint
     */
    @Before("executeService()")
    public void doBeforeAdvice(JoinPoint joinPoint){
        System.out.println("我是前置通知!!!");
        //獲取目標方法的引數資訊
        Object[] obj = joinPoint.getArgs();
        //AOP代理類的資訊
        joinPoint.getThis();
        //代理的目標物件
        joinPoint.getTarget();
        //用的最多 通知的簽名
        Signature signature = joinPoint.getSignature();
        //代理的是哪一個方法
        System.out.println(signature.getName());
        //AOP代理類的名字
        System.out.println(signature.getDeclaringTypeName());
        //AOP代理類的類(class)資訊
        signature.getDeclaringType();
        //獲取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //從獲取RequestAttributes中獲取HttpServletRequest的資訊
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        //如果要獲取Session資訊的話,可以這樣寫:
        //HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
        Enumeration<String> enumeration = request.getParameterNames();
        Map<String,String> parameterMap = Maps.newHashMap();
        while (enumeration.hasMoreElements()){
            String parameter = enumeration.nextElement();
            parameterMap.put(parameter,request.getParameter(parameter));
        }
        String str = JSON.toJSONString(parameterMap);
        if(obj.length > 0) {
            System.out.println("請求的引數資訊為:"+str);
        }
    }

    /**
     * 後置返回通知
     * 這裡需要注意的是:
     *      如果引數中的第一個引數為JoinPoint,則第二個引數為返回值的資訊
     *      如果引數中的第一個引數不為JoinPoint,則第一個引數為returning中對應的引數
     * returning 限定了只有目標方法返回值與通知方法相應引數型別時才能執行後置返回通知,否則不執行,對於returning對應的通知方法引數為Object型別將匹配任何目標返回值
     * @param joinPoint
     * @param keys
     */
    @AfterReturning(value = "execution(* com.zkn.learnspringboot.web.controller..*.*(..))",returning = "keys")
    public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){

        System.out.println("第一個後置返回通知的返回值:"+keys);
    }

    @AfterReturning(value = "execution(* com.zkn.learnspringboot.web.controller..*.*(..))",returning = "keys",argNames = "keys")
    public void doAfterReturningAdvice2(String keys){

        System.out.println("第二個後置返回通知的返回值:"+keys);
    }

    /**
     * 後置異常通知
     *  定義一個名字,該名字用於匹配通知實現方法的一個引數名,當目標方法丟擲異常返回後,將把目標方法丟擲的異常傳給通知方法;
     *  throwing 限定了只有目標方法丟擲的異常與通知方法相應引數異常型別時才能執行後置異常通知,否則不執行,
     *      對於throwing對應的通知方法引數為Throwable型別將匹配任何異常。
     * @param joinPoint
     * @param exception
     */
    @AfterThrowing(value = "executeService()",throwing = "exception")
    public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
        //目標方法名:
        System.out.println(joinPoint.getSignature().getName());
        if(exception instanceof NullPointerException){
            System.out.println("發生了空指標異常!!!!!");
        }
    }

    /**
     * 後置最終通知(目標方法只要執行完了就會執行後置通知方法)
     * @param joinPoint
     */
    @After("executeService()")
    public void doAfterAdvice(JoinPoint joinPoint){

        System.out.println("後置通知執行了!!!!");
    }

    /**
     * 環繞通知:
     *   環繞通知非常強大,可以決定目標方法是否執行,什麼時候執行,執行時是否需要替換方法引數,執行完畢是否需要替換返回值。
     *   環繞通知第一個引數必須是org.aspectj.lang.ProceedingJoinPoint型別
     */
    @Around("execution(* com.zkn.learnspringboot.web.controller..*.testAround*(..))")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("環繞通知的目標方法名:"+proceedingJoinPoint.getSignature().getName());
        try {//obj之前可以寫目標方法執行前的邏輯
            Object obj = proceedingJoinPoint.proceed();//呼叫執行目標方法
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}
完整的Controller類程式碼如下: