Spring切面編程之AOP

  AOP 是OOP 的延續,是Aspect Oriented Programming 的縮寫,意思是面向切面編程。能夠經過預編譯方式和運行期動態代理實如今不修改源代碼的狀況下給程序動態統一添加功能的一種技術。AOP設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP 能夠說也是這種目標的一種實現。咱們如今作的一些非業務,如:日誌、事務、安全等都會寫在業務代碼中(也便是說,這些非業務類橫切於業務類),但這些代碼每每是重複,複製——粘貼式的代碼會給程序的維護帶來不便,AOP 就實現了把這些業務需求與系統需求分開來作。這種解決的方式也稱代理機制。web

AOP 中必須明白的幾個概念

一、切面(Aspect)spring

  官方的抽象定義爲「 一個關注點的模塊化,這個關注點可能會橫切多個對象」 。「 切面」在ApplicationContext 中<aop:aspect>來配置。鏈接點(Joinpoint) :程序執行過程當中的某一行爲,例如,MemberService .get 的調用或者MemberService .delete 拋出異常等行爲。express

二、通知(Advice)編程

  「切面」對於某個「鏈接點」所產生的動做。其中,一個「切面」能夠包含多個「Advice」。設計模式

三、切入點(Pointcut)緩存

  匹配鏈接點的斷言,在AOP 中通知和一個切入點表達式關聯。切面中的全部通知所關注的鏈接點,都由切入點表達式來決定。安全

四、目標對象(Target Object)session

  被一個或者多個切面所通知的對象。例如,AServcieImpl 和BServiceImpl,固然在實際運行時,SpringAOP 採用代理實現,實際AOP 操做的是TargetObject 的代理對象。app

五、AOP 代理(AOP Proxy)ide

  在Spring AOP 中有兩種代理方式,JDK 動態代理和CGLib 代理。默認狀況下,TargetObject 實現了接口時,則採用JDK 動態代理,例如,AServiceImpl;反之,採用CGLib 代理,例如,BServiceImpl。強制使用CGLib 代理須要將<aop:config>的proxy-target-class 屬性設爲true。通知(Advice)類型:

六、前置通知(Before Advice)

  在某鏈接點(JoinPoint)以前執行的通知,但這個通知不能阻止鏈接點前的執行。ApplicationContext中在<aop:aspect>裏面使用<aop:before>元素進行聲明。例如,TestAspect 中的doBefore 方法。

七、後置通知(After Advice)

  當某鏈接點退出的時候執行的通知(不管是正常返回仍是異常退出)。ApplicationContext 中在<aop:aspect>裏面使用<aop:after>元素進行聲明。例如,ServiceAspect 中的returnAfter 方法,因此Teser 中調用UserService.delete 拋出異常時,returnAfter 方法仍然執行。

八、返回後通知(After Return Advice)

  在某鏈接點正常完成後執行的通知,不包括拋出異常的狀況。ApplicationContext 中在<aop:aspect>裏面使用<after-returning>元素進行聲明。

九、環繞通知(Around Advice)

  包圍一個鏈接點的通知,相似Web 中Servlet 規範中的Filter 的doFilter 方法。能夠在方法的調用先後完成自定義的行爲, 也能夠選擇不執行。ApplicationContext 中在<aop:aspect> 裏面使用<aop:around>元素進行聲明。例如,ServiceAspect 中的around 方法。

十、異常通知(After Throwing Advice)

  在方法拋出異常退出時執行的通知。ApplicationContext 中在<aop:aspect> 裏面使用<aop:after-throwing>元素進行聲明。例如,ServiceAspect 中的returnThrow 方法。注:能夠將多個通知應用到一個目標對象上,便可以將多個切面織入到同一目標對象。使用Spring AOP 能夠基於兩種方式,一種是比較方便和強大的註解方式,另外一種則是中規中矩的xml配置方式。

  以下是基於springBoot的註解方式

@Component
//聲明這是一個切面Bean
@Aspect
public class AnnotaionAspect {
    private final static Logger log = LoggerFactory.getLogger(AnnotaionAspect.class);

    //配置切入點,該方法無方法體,主要爲方便同類中其餘方法使用此處配置的切入點
    @Pointcut("execution(* com.wuzz.demo.aop.web..*(..))")
    public void aspect() {
    }

    /*
     * 配置前置通知,使用在方法aspect()上註冊的切入點
     * 同時接受JoinPoint 切入點對象,能夠沒有該參數
     */
    @Before("aspect()")
    public void before(JoinPoint joinPoint) {
        //獲取目標方法的參數信息
        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 =  new HashMap();
        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);
        }
        log.info("before 通知" + joinPoint);
    }

    //配置後置通知,使用在方法aspect()上註冊的切入點
    @After("aspect()")
    public void after(JoinPoint joinPoint) {
        log.info("after 通知" + joinPoint);
    }

    //配置環繞通知,使用在方法aspect()上註冊的切入點
    @Around("aspect()")
    public Object around(JoinPoint joinPoint) {
        long start = System.currentTimeMillis();
        try {
            Object proceed = ((ProceedingJoinPoint) joinPoint).proceed();
            long end = System.currentTimeMillis();
            log.info("around 通知" + joinPoint + "\tUse time : " + (end - start) + " ms!");
            return proceed;
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            log.info("around 通知" + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
        }
        return null;
    }

    //配置後置返回通知,使用在方法aspect()上註冊的切入點
    @AfterReturning("aspect()")
    public void afterReturn(JoinPoint joinPoint) {
        log.info("afterReturn 通知" + joinPoint);
    }

    //配置拋出異常後通知,使用在方法aspect()上註冊的切入點
    @AfterThrowing(pointcut = "aspect()", throwing = "ex")
    public void afterThrow(JoinPoint joinPoint, Exception ex) {
        log.info("afterThrow 通知" + joinPoint + "\t" + ex.getMessage());
    }
}

  應該說學習Spring AOP 有兩個難點,第一點在於理解AOP 的理念和相關概念,第二點在於靈活掌握和使用切入點表達式。概念的理解一般不在一朝一夕,慢慢浸泡的時間長了,天然就明白了,下面咱們簡單地介紹一下切入點表達式的配置規則吧。一般狀況下,表達式中使用」execution「就能夠知足大部分的要求。表達式格式以下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?

 

  • modifiers-pattern:方法的操做權限
  • ret-type-pattern:返回值
  • declaring-type-pattern:方法所在的包
  • name-pattern:方法名
  • parm-pattern:參數名
  • throws-pattern:異常

  其中, 除ret-type-pattern 和name-pattern 以外, 其餘都是可選的。上例中, execution(* com.wuzz.demo.aop.web..*(..))表示com.wuzz.demo.aop.web 包下,返回值爲任意類型;方法名任意;參數不做限制的全部方法。最後說一下通知參數,能夠經過args 來綁定參數,這樣就能夠在通知(Advice)中訪問具體參數了。例如,<aop:aspect>配置以下:

<aop:config>
  <aop:aspect ref="xmlAspect">
    <aop:pointcut id="simplePointcut"
      expression="execution(* com.gupaoedu.aop.service..*(..)) and args(msg,..)" />
    <aop:after pointcut-ref="simplePointcut" Method="after"/>
  </aop:aspect>
</aop:config>

  上面的代碼args(msg,..)是指將切入點方法上的第一個String 類型參數添加到參數名爲msg 的通知的入參上,這樣就能夠直接使用該參數啦。在上面的Aspect 切面Bean 中已經看到了,每一個通知方法第一個參數都是JoinPoint。其實,在Spring中,任何通知(Advice)方法均可以將第一個參數定義爲org.aspectj.lang.JoinPoint 類型用以接受當前鏈接點對象。JoinPoint 接口提供了一系列有用的方法, 好比getArgs() (返回方法參數)、getThis() (返回代理對象)、getTarget() (返回目標)、getSignature() (返回正在被通知的方法相關信息)和toString() (打印出正在被通知的方法的有用信息)。

Spring AOP 源碼

  Spring 的AOP 是經過接入BeanPostProcessor 後置處理器開始的,它是Spring IOC 容器常用到的一個特性,這個Bean 後置處理器是一個監聽器,能夠監聽容器觸發的Bean 聲明週期事件。後置處理器向容器註冊之後,容器中管理的Bean 就具有了接收IOC 容器事件回調的能力。BeanPostProcessor 的使用很是簡單,只須要提供一個實現接口BeanPostProcessor 的實現類,而後在Bean 的配置文件中設置便可。

一、BeanPostProcessor 源碼

public interface BeanPostProcessor {
    //爲在Bean 的初始化前提供回調入口
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    //爲在Bean 的初始化以後提供回調入口
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}

  這兩個回調的入口都是和容器管理的Bean 的生命週期事件緊密相關,能夠爲用戶提供在Spring IOC容器初始化Bean 過程當中自定義的處理操做。

二、AbstractAutowireCapableBeanFactory 類對容器生成的Bean 添加後置處理器

  BeanPostProcessor 後置處理器的調用發生在Spring IOC 容器完成對Bean 實例對象的建立和屬性的依賴注入完成以後,在對Spring 依賴注入的源碼分析過程當中咱們知道,當應用程序第一次調用getBean()方法(lazy-init 預實例化除外)向Spring IOC 容器索取指定Bean 時觸發Spring IOC 容器建立Bean 實例對象並進行依賴注入的過程, 其中真正實現建立Bean 對象並進行依賴注入的方法是AbstractAutowireCapableBeanFactory 類的doCreateBean()方法,主要源碼以下:

//真正建立Bean 的方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        ......
        try {
            //將Bean 實例對象封裝,而且Bean 定義中配置的屬性值賦值給實例對象
            //,對Bean 屬性的依賴注入進行處理。
            populateBean(beanName, mbd, instanceWrapper);
            //初始化Bean 對象
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }

       ......

        return exposedObject;
}

  從上面的代碼中咱們知道,爲Bean 實例對象添加BeanPostProcessor 後置處理器的入口的是initializeBean()方法。

三、initializeBean()方法爲容器產生的Bean 實例對象添加BeanPostProcessor 後置處理器

  一樣在AbstractAutowireCapableBeanFactory 類中,initializeBean()方法實現爲容器建立的Bean實例對象添加BeanPostProcessor 後置處理器,源碼以下:

//初始容器建立的Bean 實例對象,爲其添加BeanPostProcessor 後置處理器
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        //JDK 的安全機制驗證權限
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            //爲Bean 實例對象包裝相關屬性,如名稱,類加載器,所屬容器等信息
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            //對BeanPostProcessor 後置處理器的postProcessBeforeInitialization
            //回調方法的調用,爲Bean 實例初始化前作一些處理
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            //調用Bean 實例對象初始化的方法,這個初始化方法是在Spring Bean 定義配置
            //文件中經過init-Method 屬性指定的
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            //對BeanPostProcessor 後置處理器的postProcessAfterInitialization
            //回調方法的調用,爲Bean 實例初始化以後作一些處理
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
}
//調用BeanPostProcessor 後置處理器實例對象初始化以前的處理方法
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {

   Object result = existingBean;
   //遍歷容器爲所建立的Bean 添加的全部BeanPostProcessor 後置處理器
   for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
      //調用Bean 實例全部的後置處理中的初始化前處理方法,爲Bean 實例對象在
      //初始化以前作一些自定義的處理操做
      Object current = beanProcessor.postProcessBeforeInitialization(result, beanName);
      if (current == null) {
         return result;
      }
      result = current;
   }
   return result;
}
//調用BeanPostProcessor 後置處理器實例對象初始化以後的處理方法
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {

   Object result = existingBean;
   for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
      Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
      if (current == null) {
         return result;
      }
      result = current;
   }
   return result;
}

  BeanPostProcessor 是一個接口,其初始化前的操做方法和初始化後的操做方法均委託其實現子類來實現,在Spring 中,BeanPostProcessor 的實現子類很是的多,分別完成不一樣的操做,如:AOP 面向切面編程的註冊通知適配器、Bean 對象的數據校驗、Bean 繼承屬性、方法的合併等等,咱們以最簡單的AOP 切面織入來簡單瞭解其主要的功能。下面咱們來分析其中一個建立AOP 代理對象的子類AbstractAutoProxyCreator 類。該類重寫了postProcessAfterInitialization()方法。

選擇代理策略

  之後置處理器爲例,進入postProcessAfterInitialization()方法,咱們發現調到了一個很是核心的方法wrapIfNecessary(),其源碼以下:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        // 判斷是否不該該代理這個bean
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        /*判斷是不是一些InfrastructureClass 或者是否應該跳過這個bean。
         所謂InfrastructureClass 就是指Advice/PointCut/Advisor 等接口的實現類。
         shouldSkip 默認實現爲返回false,因爲是protected 方法,子類能夠覆蓋。
         */
        //掃描全部的相關的方法  pointCut原始方法
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        // 獲取這個bean 的advice
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // 建立代理
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
}

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {

if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);

if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}

Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
  //獲取代理工廠
return proxyFactory.getProxy(getProxyClassLoader());
}

  整個過程跟下來,我發現最終調用的是proxyFactory.getProxy()方法。到這裏咱們大概可以猜到proxyFactory 有JDK 和CGLib 的,那麼咱們該如何選擇呢?最終調用的是DefaultAopProxyFactory的createAopProxy()方法:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

    @Override
  //建立AOP代理
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); }//Cglib代理 return new ObjenesisCglibAopProxy(config); } else {
        //JDK代理
return new JdkDynamicAopProxy(config); } } /** * Determine whether the supplied {@link AdvisedSupport} has only the * {@link org.springframework.aop.SpringProxy} interface specified * (or no proxy interfaces specified at all). */ private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class<?>[] ifcs = config.getProxiedInterfaces(); return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]))); } }

   調用代理方法,分析調用邏輯以前先上類圖,看看Spring 中主要的AOP 組件:

  上面咱們已經瞭解到Spring 提供了兩種方式來生成代理方式有JDKProxy 和CGLib。下面咱們來研究一下Spring 如何使用JDK 來生成代理對象,具體的生成代碼放在JdkDynamicAopProxy 這個類中,直接上相關代碼:

/**
     * 獲取代理類要實現的接口,除了Advised 對象中配置的,還會加上SpringProxy, Advised(opaque=false)
     * * 檢查上面獲得的接口中有沒有定義equals 或者hashcode 的接口
     * * 調用Proxy.newProxyInstance 建立代理對象
     */
    public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
        Assert.notNull(config, "AdvisedSupport must not be null");
        if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
            throw new AopConfigException("No advisors and no TargetSource specified");
        }
        this.advised = config;
    }

  經過註釋咱們應該已經看得很是明白代理對象的生成過程,此處再也不贅述。下面的問題是,代理對象生成了,那切面是如何織入的?咱們知道InvocationHandler 是JDK 動態代理的核心,生成的代理對象的方法調用都會委託到InvocationHandler.invoke()方法。而從JdkDynamicAopProxy 的源碼咱們能夠看到這個類其實也實現了InvocationHandler,下面咱們分析Spring AOP 是如何織入切面的,直接上源碼看invoke()方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation;
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.targetSource;
        Object target = null;

        try {
            //eqauls()方法,具目標對象未實現此方法
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                return equals(args[0]);
            }
            //hashCode()方法,具目標對象未實現此方法
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            //Advised 接口或者其父接口中定義的方法,直接反射調用,不該用通知
            else if (method.getDeclaringClass() == DecoratingProxy.class) {
                // There is only getDecoratedClass() declared -> dispatch to proxy config.
                return AopProxyUtils.ultimateTargetClass(this.advised);
            }
            else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal;

            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }//得到目標對象的類
            target = targetSource.getTarget();
            Class<?> targetClass = (target != null ? target.getClass() : null);

            // Get the interception chain for this method.
            //獲取能夠應用到此方法上的Interceptor 調用鏈列表
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//若是沒有能夠應用到此方法的通知(Interceptor),此直接反射調用Method.invoke(target, args)
            if (chain.isEmpty()) {
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            else {//建立MethodInvocation
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                retVal = invocation.proceed();
            }
            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target &&
                    returnType != Object.class && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                retVal = proxy;
            }
            else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException(
                        "Null return value from advice does not match primitive return type for: " + method);
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // Must have come from TargetSource.
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

  主要實現思路能夠簡述爲:首先獲取應用到此方法上的通知鏈(Interceptor Chain)。若是有通知,則應用通知,並執行JoinPoint;若是沒有通知,則直接反射執行JoinPoint。而這裏的關鍵是通知鏈是如何獲取的以及它又是如何執行的呢?如今來逐一分析。首先,從上面的代碼能夠看到,通知鏈是經過Advised.getInterceptorsAndDynamicInterceptionAdvice()這個方法來獲取的,咱們來看下這個方法的實現邏輯:

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
        MethodCacheKey cacheKey = new MethodCacheKey(method);
        List<Object> cached = this.methodCache.get(cacheKey);
        if (cached == null) {
            cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                    this, method, targetClass);
            this.methodCache.put(cacheKey, cached);
        }
        return cached;
}

  經過上面的源碼咱們能夠看到, 實際獲取通知的實現邏輯實際上是由AdvisorChainFactory 的getInterceptorsAndDynamicInterceptionAdvice()方法來完成的,且獲取到的結果會被緩存。下面來分析getInterceptorsAndDynamicInterceptionAdvice()方法的實現:

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
            Advised config, Method method, @Nullable Class<?> targetClass) {

        // This is somewhat tricky... We have to process introductions first,
        // but we need to preserve order in the ultimate list.
        List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        //查看是否包含IntroductionAdvisor
        boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
        //這裏實際上註冊一系列AdvisorAdapter,用於將Advisor 轉化成MethodInterceptor
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

        for (Advisor advisor : config.getAdvisors()) {
            if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    //這個地方這兩個方法的位置能夠互換下
                    //將Advisor 轉化成Interceptor
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    //檢查當前advisor 的pointcut 是否能夠匹配當前方法
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                        if (mm.isRuntime()) {
                            // Creating a new object instance in the getInterceptors() method
                            // isn't a problem as we normally cache created chains.
                            for (MethodInterceptor interceptor : interceptors) {
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                            }
                        }
                        else {
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
            }
            else if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
            else {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }

        return interceptorList;
}

  這個方法執行完成後,Advised 中配置可以應用到鏈接點(JoinPoint)或者目標類(Target Object)的Advisor 所有被轉化成了MethodInterceptor,接下來咱們再看下獲得的攔截器鏈是怎麼起做用的。

//若是沒有能夠應用到此方法的通知(Interceptor),此直接反射調用Method.invoke(target, args)
if (chain.isEmpty()) {

  Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
  retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
  // We need to create a method invocation...
  //建立MethodInvocation
  invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
  // Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}

  從這段代碼能夠看出, 若是獲得的攔截器鏈爲空, 則直接反射調用目標方法, 不然建立MethodInvocation,調用其proceed()方法,觸發攔截器鏈的執行,來看下具體代碼:

 

public Object proceed() throws Throwable {
        //    We start with an index of -1 and increment early.
        //若是Interceptor 執行完了,則執行joinPoint
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }

        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            //若是要動態匹配joinPoint
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            //動態匹配:運行時參數是否知足匹配條件
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            else {
                // Dynamic matching failed.
                // Skip this interceptor and invoke the next in the chain.
                //動態匹配失敗時,略過當前Intercetpor,調用下一個Interceptor
                return proceed();
            }
        }
        else {
            // It's an interceptor, so we just invoke it: The pointcut will have
            // been evaluated statically before this object was constructed.
            //執行當前Intercetpor
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

  至此,通知鏈就完美地造成了。咱們再往下來看invokeJoinpointUsingReflection()方法,其實就是反射調用:

public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
            throws Throwable {

        // Use reflection to invoke the method.
        try {
            ReflectionUtils.makeAccessible(method);
            return method.invoke(target, args);
        }
  .......
}

觸發通知

  在爲AopProxy 代理對象配置攔截器的實現中,有一個取得攔截器的配置過程,這個過程是由DefaultAdvisorChainFactory 實現的,這個工廠類負責生成攔截器鏈,在它的getInterceptorsAndDynamicInterceptionAdvice 方法中,有一個適配器和註冊過程,經過配置Spring 預先設計好的攔截器,Spring 加入了它對AOP 實現的處理。

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
            Advised config, Method method, @Nullable Class<?> targetClass) {
        // This is somewhat tricky... We have to process introductions first,
        // but we need to preserve order in the ultimate list.
        List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        //查看是否包含IntroductionAdvisor
        boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
        //這裏實際上註冊一系列AdvisorAdapter,用於將Advisor 轉化成MethodInterceptor
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

        for (Advisor advisor : config.getAdvisors()) {
            if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    //這個地方這兩個方法的位置能夠互換下
                    //將Advisor 轉化成Interceptor
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    //檢查當前advisor 的pointcut 是否能夠匹配當前方法
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                        if (mm.isRuntime()) {
                            // Creating a new object instance in the getInterceptors() method
                            // isn't a problem as we normally cache created chains.
                            for (MethodInterceptor interceptor : interceptors) {
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                            }
                        }
                        else {
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
            }
            else if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
            else {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }

        return interceptorList;
    }

   GlobalAdvisorAdapterRegistry 負責攔截器的適配和註冊過程。而GlobalAdvisorAdapterRegistry 起到了適配器和單例模式的做用,提供了一個DefaultAdvisorAdapterRegistry,它用來完成各類通知的適配和註冊過程。

public abstract class GlobalAdvisorAdapterRegistry {

    /**
     * Keep track of a single instance so we can return it to classes that request it.
     */
    private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();

    /**
     * Return the singleton {@link DefaultAdvisorAdapterRegistry} instance.
     */
    public static AdvisorAdapterRegistry getInstance() {
        return instance;
    }

    /**
     * Reset the singleton {@link DefaultAdvisorAdapterRegistry}, removing any
     * {@link AdvisorAdapterRegistry#registerAdvisorAdapter(AdvisorAdapter) registered}
     * adapters.
     */
    static void reset() {
        instance = new DefaultAdvisorAdapterRegistry();
    }

}

  DefaultAdvisorAdapterRegistry 設置了一系列的是配置,正是這些適配器的實現,爲Spring AOP 提供了編織能力。

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

    private final List<AdvisorAdapter> adapters = new ArrayList<>(3);


    /**
     * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
     */
    public DefaultAdvisorAdapterRegistry() {
        registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }


    @Override
    //包裝
    public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
        if (adviceObject instanceof Advisor) {
            return (Advisor) adviceObject;
        }
        if (!(adviceObject instanceof Advice)) {
            throw new UnknownAdviceTypeException(adviceObject);
        }
        Advice advice = (Advice) adviceObject;
        if (advice instanceof MethodInterceptor) {
            // So well-known it doesn't even need an adapter.
            return new DefaultPointcutAdvisor(advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // Check that it is supported.
            if (adapter.supportsAdvice(advice)) {
                return new DefaultPointcutAdvisor(advice);
            }
        }
        throw new UnknownAdviceTypeException(advice);
    }

    @Override
    //獲取方法攔截器,即咱們配置的切面方法處理
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<>(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor) advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            if (adapter.supportsAdvice(advice)) {
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }
        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
    }

    @Override
    //註冊通知適配器
    public void registerAdvisorAdapter(AdvisorAdapter adapter) {
        this.adapters.add(adapter);
    }

}

  下面以MethodBeforeAdviceAdapter 爲例,看具體的實現:

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }

}

  Spring AOP 爲了實現advice 的織入,設計了特定的攔截器對這些功能進行了封裝。咱們接着看MethodBeforeAdviceInterceptor 如何完成封裝的?

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

    private MethodBeforeAdvice advice;
    /**
     * Create a new MethodBeforeAdviceInterceptor for the given advice.
     * @param advice the MethodBeforeAdvice to wrap
     */
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
        return mi.proceed();
    }

}

  能夠看到,invoke 方法中,首先觸發了advice 的before 回調,而後纔是proceed。AfterReturningAdviceInterceptor 的源碼:

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

    private final AfterReturningAdvice advice;


    /**
     * Create a new AfterReturningAdviceInterceptor for the given advice.
     * @param advice the AfterReturningAdvice to wrap
     */
    public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }


    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object retVal = mi.proceed();
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    }
}

   ThrowsAdviceInterceptor 的源碼:

@Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        try {
            return mi.proceed();
        }
        catch (Throwable ex) {
            Method handlerMethod = getExceptionHandler(ex);
            if (handlerMethod != null) {
                invokeHandlerMethod(mi, ex, handlerMethod);
            }
            throw ex;
        }
    }

    private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable {
        Object[] handlerArgs;
        if (method.getParameterCount() == 1) {
            handlerArgs = new Object[] { ex };
        }
        else {
            handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex};
        }
        try {
            method.invoke(this.throwsAdvice, handlerArgs);
        }
        catch (InvocationTargetException targetEx) {
            throw targetEx.getTargetException();
        }
    }

  其實就是在調用方法的時候,前後調用通知的方法攔截器以達到加強方法的功能,最後來一張時序圖

相關文章
相關標籤/搜索