Spring源碼解讀(4)AOP-代理的建立

一、概述

    Spring AOP的核心是基於代理實現的,代理有jdk基於接口的動態代理和基於asm實現的容許類沒有接口的cglib代理,上一小結,已經分析過Spring封裝了使用了@Aspect註解的類,並將切面方法封裝成實現了AbstractAspectJAdvice的類放入緩存中,等待建立代理對象時使用,AbstractAspectJAdvice有三個參數:java

//切面方法 諸如使用了@Before這類的方法
Method aspectJAdviceMethod
//封裝了expression表達式,給定一個類和方法名,能返回是否匹配
AspectJExpressionPointcut pointcut
//能夠建立一個切面類,而後經過反射執行切面方法
AspectInstanceFactory aspectInstanceFactory
複製代碼

    經過這個對象就能夠攔截全部的類的建立找出符合條件的bean建立代理執行加強操做,這也是spring的實現原理。git

二、jdk代理

    jdk代理是基於接口的代理,因此被代理的對象必須是有接口實現的類,代理建立時經過Proxy.newProxyInstance實現的,這個方法有三個參數:github

//指定要使用的類加載器
ClassLoader loader,
//被代理的類所實現的接口,加強接口的方法
Class<?>[] interfaces,
//方法處理器,會攔截全部方法,而後執行加強參數。
InvocationHandler inoker
複製代碼

    簡單實例redis

    訂單操做接口spring

public interface OrderUpdateService {
    /** * 訂單付款 * @param orderAmt */
    void payOrder(String orderAmt);
}
複製代碼

    訂單操做實現類express

@Slf4j
public class OrderUpdateServiceImpl implements OrderUpdateService {

    public void payOrder(String orderAmt) {
        log.info("訂單付款中.....");
    }
}
複製代碼

    訂單代理類數組

@Slf4j
public class OrderUpdateServiceImplProxy {
    //目標類 要加強的類
    private OrderUpdateService updateService;

    public OrderUpdateServiceImplProxy(OrderUpdateService updateService) {
        this.updateService = updateService;
    }

    public OrderUpdateService getProxy() {
        return (OrderUpdateService) Proxy.newProxyInstance(OrderUpdateServiceImplProxy.class.getClassLoader(), new Class[]{OrderUpdateService.class}, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("payOrder".equals(method.getName())) {
                    log.info("付款前處理");
                    method.invoke(updateService, args);
                    log.info("付款後處理");
                }
                return proxy;
            }
        });
    }
}
複製代碼

    demo驗證緩存

public static void main(String[] args) {
    OrderUpdateService orderUpdateService = new OrderUpdateServiceImpl();
    OrderUpdateServiceImplProxy proxy = new OrderUpdateServiceImplProxy(orderUpdateService);
    orderUpdateService = proxy.getProxy();
    orderUpdateService.payOrder("100");
}
複製代碼

    驗證結果架構

11:18:07.859 [main] INFO  s.a.e.p.OrderUpdateServiceImplProxy - 付款前處理
11:18:07.890 [main] INFO  s.a.e.p.s.OrderUpdateServiceImpl - 訂單付款中.....
11:18:07.890 [main] INFO  s.a.e.p.OrderUpdateServiceImplProxy - 付款後處理
複製代碼

    payOrder方法的確時被加強了。app

三、cglib代理

    Cglib是一個強大的、高性能的代碼生成包,它普遍被許多AOP框架使用,爲他們提供方法的攔截。以下圖所示Cglib與Spring等應用的關係。

  • 最底層的是字節碼Bytecode,字節碼是Java爲了保證「一次編譯、處處運行」而產生的一種虛擬指令格式,例如iload_0、iconst_一、if_icmpne、dup等
  • 位於字節碼之上的是ASM,這是一種直接操做字節碼的框架,應用ASM須要對Java字節碼、Class結構比較熟悉
  • 位於ASM之上的是CGLIBGroovyBeanShell,後兩種並非Java體系中的內容而是腳本語言,它們經過ASM框架生成字節碼變相執行Java代碼,這說明在JVM中執行程序並不必定非要寫Java代碼----只要你能生成Java字節碼,JVM並不關心字節碼的來源,固然經過Java代碼生成的JVM字節碼是經過編譯器直接生成的,算是最「正統」的JVM字節碼
  • 位於CGLIBGroovyBeanShell之上的就是HibernateSpring AOP這些框架了,這一層你們都比較熟悉
  • 最上層的是Applications,即具體應用,通常都是一個Web項目或者本地跑一個程序

    因此,Cglib的實現是在字節碼的基礎上的,而且使用了開源的ASM讀取字節碼,對類實現加強功能的。

    Cglib能夠經過Callback回調函數完成對方法的加強,經過CallbackFilter函數過濾符合條件的函數,Spring也是基於這兩個接口完成bean的加強功能的,因此能夠猜想前面Advice參數的pointCut方法匹配器就是在CallbackFilter中起做用的。下來看看cglib的簡單使用:

    下面的驗證是攔截GameServiceplayGames方法,在playGames方法以前,執行TransactionManager類的startcommit方法。

@Test
public void testProxyFilter() throws Exception {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(GameService.class);
    // NoOp.INSTANCE 表示沒有被匹配的方法不執行任何操做 若是CallbackFilter返回0
    //表明使用callbacks集合中的第一個元素執行方法攔截策略 1 則使用第二個。
    Callback[] callbacks = {new TransactionInterceptor(), NoOp.INSTANCE};
    //設置回調函數集合
    enhancer.setCallbacks(callbacks);
    //根據回調過濾器返回指定回調函數索引
    enhancer.setCallbackFilter(new TransactionFilter());
    GameService gameService = (GameService) enhancer.create();
    Person person = new Person("lijinpeng", 26, false, new UserDao());
    gameService.setPerson(person);
    gameService.playGames();
}
複製代碼

    執行結果:playGames獲得了加強。

[2019-06-03 15:13:59] [INFO ][s.r.beans.models.aop.TransactionManager][15][start][]start tx
[2019-06-03 15:13:59] [INFO ][s.road.beans.models.scan.GameService][29][playGames][]person-name:lijinpeng play games
[2019-06-03 15:13:59] [INFO ][s.r.beans.models.aop.TransactionManager][20][commit][]commit tx
複製代碼

    方法加強器

@Slf4j
public class TransactionManager {
    public void start() {
        log.info("start tx");
        MessageTracerUtils.addMessage("start tx");
    }

    public void commit() {
        log.info("commit tx");
        MessageTracerUtils.addMessage("commit tx");
    }

    public void rollback() {
        log.info("rollback tx");
        MessageTracerUtils.addMessage("rollback tx");
    }

    public Object getAspectInstance() {
        return new TransactionManager();
    }
}
複製代碼

    方法攔截器

//方法攔截器 會攔截 全部方法 ,因此須要加判斷 cglib 還提供了filter過濾器 能夠用於過濾指定方法
public class TransactionInterceptor implements MethodInterceptor {
    
    TransactionManager tx = new TransactionManager();
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        tx.start();
        Object value = methodProxy.invokeSuper(o, objects);
        tx.commit();
        return value;
    }
}
複製代碼

    經過CallbackFilter接口,能夠自定義攔截加強策略,返回的是前面設置的Callbacks集合中的回調函數索引,這裏的含義是,若是方法名是playGames則使用Callbacks集合中索引爲0的回調函數即TransactionInterceptor,該回調函數就是要在playGames前執行TransactionManagerstart()方法,playGames方法後執行commit(),不然使用NoOp.INSTANCE表示什麼也不作。

public class TransactionFilter implements CallbackFilter {
    //返回回調函數在集合中的索引
    public int accept(Method method) {
        if ("playGames".equals(method.getName())) {
            return 0;
        } else {
            return 1;
        }
    }
}
複製代碼

    cglib代理經過Callback方法控制目標方法的加強邏輯,經過CallbackFilter用來指定適配的方法使用不通的回調函數完成不通的功能加強,cglib還能夠爲類動態得建立新得方法,不過知道cglib如何實現代理對Spring AOP的源碼學習來講已經足夠了。

四、代理對象的校驗

    在Spring中,並非全部的bean都須要建立代理,在Spring Aop中,只有被咱們配置的Aspect切面類的PointCut表達式匹配的類纔會建立代理,因此在建立代理以前須要對要建立bean進行校驗以判斷該類是否須要被建立爲代理對象。上一篇的結尾處分析過,當解析完@Aspect註解的切面類後,就會調用BeanPostProcessorpostProcessAfterInitialization的方法去建立一個代理對象,這個方法會調用wrapIfNecessary方法,這個方法會完成bean是否須要被建立爲代理的校驗,在這個方法裏面,有一個核心的方法getAdvicesAndAdvisorsForBean,這個方法會返回一個Advisor數組集合,這個數組集合的Advisor即表示要建立的bean是否能被Advisor的Advice中的PointCut匹配到,若是能夠匹配到,則爲bean建立代理。AbstractAdvisorAutoProxyCreator完成這個方法的功能實現:

@Override
	protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
         //查找全部須要做用於beanClass的Advisor
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
             //直接返回null
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}
複製代碼

    核心方法是 findEligibleAdvisors(beanClass, beanName)

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        //核心方法1:查找容器中全部已經生成了的Advisor,這是第二次執行
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
        //核心方法2:從容器中全部的Advisro找出可以做用於beanClass的Advisor
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        //爲Advisor提供鉤子方法 方便擴展Advisor
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
             //根據Order排序Advisor
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}
複製代碼

    這個方法首先又再次調用了findCandidateAdvisors方法,這個方法在解析切面類的時候也調用了一次,目的是爲切面類的切面方法生成Advisor,並根據註解爲每一個Advisor再生成一個Advice對象。這裏第二次調用的目的是獲取容器中全部生成過的Advisor,而後調用findAdvisorsThatCanApply方法找出全部可以做用於目標bean的AdvisorextendAdvisors方法是爲了方便擴展AdvisorsortAdvisors是基於Order接口排序Advisor的。接下來主要來看下前兩個方法:     AnnotationAwareAspectJAutoProxyCreator重寫了findCandidateAdvisors方法,在重寫了的方法裏面,先調用了父類的,而後調用了基於註解的獲取Advisor的方法。

@Override
protected List<Advisor> findCandidateAdvisors() {
   //查找構建全部基於XML配置的切面類的切面方法
   List<Advisor> advisors = super.findCandidateAdvisors();
   //查找構建全部基於註解的切面類的切面方法
   advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   return advisors;
}
複製代碼

    此次咱們來看看基於xml的切面類是如何獲取容器中的Advisor的。

public List<Advisor> findAdvisorBeans() {
		String[] advisorNames = null;
		synchronized (this) {
             //這個階段若是xml配置了切面類 cachedAdvisorBeanNames 應該已經包含了切面方法生成的切面類的 //beanName了
			advisorNames = this.cachedAdvisorBeanNames;
			if (advisorNames == null) {
			    //Aspect 多是基於FactroyBean建立的,這裏不實例化Aspect,仍是應該交給代理實現
				advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
						this.beanFactory, Advisor.class, true, false);
				this.cachedAdvisorBeanNames = advisorNames;
			}
		}
		if (advisorNames.length == 0) {
			return new LinkedList<Advisor>();
		}

		List<Advisor> advisors = new LinkedList<Advisor>();
		for (String name : advisorNames) {
			if (isEligibleBean(name)) {
				if (this.beanFactory.isCurrentlyInCreation(name)) {
					if (logger.isDebugEnabled()) {
						logger.debug("Skipping currently created advisor '" + name + "'");
					}
				}
				else {
					try {
                          //直接經過切面方法生成的BeanDefinition 獲取bean實例
						advisors.add(this.beanFactory.getBean(name, Advisor.class));
					}
					catch (BeanCreationException ex) {
		      		//異常處理
                        ......       
		}
		return advisors;
	}
複製代碼

    從上面的代碼看獲取xml配置的切面方法生成的Advisor很簡單,其實否則,真正複雜的邏輯是讀取xml生成BeanDefinitiongetBean()的過程,這裏就不展開分析了。 這個方法過程就是找出由XMl配置的切面類生成的全部切面方法的Advisor,而後遍歷beanName,生成實例對象,然會返回生成後的Advisor實例對象。

4.1 獲取全部Advisor

    接下來再來看看基於註解的Advisor是如何獲取的。

public List<Advisor> buildAspectJAdvisors() {
		List<String> aspectNames = null;

		synchronized (this) {
			aspectNames = this.aspectBeanNames;
			if (aspectNames == null) {
				//此時的aspectNames已經不爲空了,省略以前生成Advisor的部分
                  .......
				return advisors;
			}
		}
		if (aspectNames.isEmpty()) {
			return Collections.emptyList();
		}
		List<Advisor> advisors = new LinkedList<Advisor>();
		for (String aspectName : aspectNames) {
            //獲取全部基於註解生成Advisor
			List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
			if (cachedAdvisors != null) {
				advisors.addAll(cachedAdvisors);
			}
			else {
                 //若是當時生成的Advisor是由工廠生成的,這個時候從工廠獲取
				MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
				advisors.addAll(this.advisorFactory.getAdvisors(factory));
			}
		}
		return advisors;
	}
複製代碼

    這個方法有兩部分,第一部分省略了,內容是上一篇分析過的生成基於註解的Advisor的過程,第二部分是獲取已經生成過的Advisor的過程,因爲基於註解生成的Advisor已經在建立的同時完成了bean的初始化,因此這裏直接從緩存中獲取便可,若是Advisor是由工廠類生成,則此時須要經過工廠類獲取Advisor

4.2 獲取beanClass的Advisor

    上面分析過程已經從xml和註解的方式獲取到了容器中的全部Advisor,接下來會執行第二步操做,校驗容器中的Advisor是否可以做用與要建立的bean上,回頭看核心方法2:findAdvisorsThatCanApply

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
   if (candidateAdvisors.isEmpty()) {
      return candidateAdvisors;
   }
   List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
   for (Advisor candidate : candidateAdvisors) {
       //核心點1:經過Introduction實現的advice
      if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
         eligibleAdvisors.add(candidate);
      }
   }
   boolean hasIntroductions = !eligibleAdvisors.isEmpty();
   for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor) {
         // already processed
         continue;
      }
       //核心點2:匹配可以做用於clazz的Advice
      if (canApply(candidate, clazz, hasIntroductions)) {
         eligibleAdvisors.add(candidate);
      }
   }
   return eligibleAdvisors;
}
複製代碼

    這個方法的核心點1作了一次匹配,這個匹配是經過IntroductionAdvisor實現的,首先簡單瞭解下這個問題的背景:

    若是須要對一些組件新增一些功能,好比在付款接口完成後,須要記錄這筆交易耗時時間,可是此時根據業務不一樣,可能會有不少個付款實現類,這個時候的作法就是新增一個統計耗時的接口,這樣作雖然能夠解決問題,可是污染了業務代碼,並且每一個類中都要實現一遍,這就違反了面向對象的開放封閉原則,這個問題經過裝飾者模式解決,Spring爲了可以完成這個功能,使用Introduction+advice實現的,Spring經過一個特殊的攔截器IntroductionInterceptor實現。與PointCut做用於接口層次上不一樣,這種方式做用於類的層次,可是很不建議在生產環境使用這種粗粒度的實現,只須要知道Spring中有這種實現的方式就能夠了,這裏也不做爲重點分析。

    咱們來看核心點2,canApply(candidate, clazz, hasIntroductions)從方法參數能夠看出參數candidate是一個Advisor,claszz是目標類對象,因爲Advisor實現類裏面有PointCut,因此就能夠匹配clazz類的方法是否屬於切入方法,須要被加強。

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
   if (advisor instanceof IntroductionAdvisor) {
       //由於做用於類 直接匹配類就好了。 
      return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
   }
   else if (advisor instanceof PointcutAdvisor) {
      //轉換成成PointCut接口
      PointcutAdvisor pca = (PointcutAdvisor) advisor;
      return canApply(pca.getPointcut(), targetClass, hasIntroductions);
   }
   else {
      //若是advisor沒有一個pointcut,默認它對全部的bean都是生效的
      return true;
   }
}
複製代碼

    上面的方法首先校驗了advisor是不是IntroductionAdvisor,上面分析過,IntroductionAdvisor是基於類型的,因此這裏直接針對這種bean直接調用了ClassFilter接口去匹配,而後若是advisor是一個PointcutAdvisor,則轉換成PointcutAdvisor再調用canApply方法,最後若是Advisor並無配置成一個PointcutAdvisor,就默認對全部的bean都是生效的,進入canApply方法繼續看

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
   Assert.notNull(pc, "Pointcut must not be null"); 
   if (!pc.getClassFilter().matches(targetClass)) {
      return false;
   }
    //前面說過 PointCut接口getMethodMatcher的返回一個方法匹配器
   MethodMatcher methodMatcher = pc.getMethodMatcher();
   IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
   if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
      introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
   }
    //獲取目標類得全部接口接口
   Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
   classes.add(targetClass);
   for (Class<?> clazz : classes) {
       //獲取目標類及其接口的全部方法
      Method[] methods = clazz.getMethods();
      for (Method method : methods) {
         if ((introductionAwareMethodMatcher != null &&
               introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
               methodMatcher.matches(method, targetClass)) {
            return true;
         }
      }
   }
   return false;
}
複製代碼

    這個方法其實很明確了,就是經過PointCut獲取到一個方法匹配器MethodMatcher,這個匹配器經過match方法能夠判斷當前的方法是否可以被匹配到,PointCut的封裝其實就是aspectj的,但咱們關注點並不在這,就不進入裏面分析了, 而後獲取目標類的全部接口,循環全部的接口而後獲取各個接口的全部方法,調用MethodMatchermatch方法用來判斷當前方法是否可以被匹配,只要有一個方法被匹配到,則但會true,代表當前的advisor可以做用於當前bean。

    Spring在bean的建立過程當中,就是經過上面的過程查找可以做用於bean的Advisor的,若是可以找到,就爲bean建立代理,不然就當普通的bean建立,通過上面的判斷,咱們案例中的bean確定是有符合條件的Advisor,因此接下來看看Spring是如何建立代理的。

五、代理模式的查找

再來回憶下這個方法:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //若是bean是經過TargetSource接口獲取 則直接返回
   if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
    //若是bean是切面類 直接返回
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
    //若是bean是Aspect 並且容許跳過建立代理, 加入advise緩存 返回
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }
   //若是前面生成的advisor緩存中存在可以匹配到目標類方法的Advisor 則建立代理
   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;
}
複製代碼

    通過前面的分析,經過getAdvicesAndAdvisorsForBean方法能夠獲取到容器中全部能被做用於當前bean的Advisor,此時的specificInterceptors確定是不爲null,因此就會執行createProxy建立代理的方法了。

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

   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
       //若是是基於IntroductionAdvisor,直接指定代理的目標類便可
      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 {
         //將beanClass的接口設置到代理工廠上
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }
   //構建可以做用於beanClass類的Advisor集合
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   for (Advisor advisor : advisors) {
      proxyFactory.addAdvisor(advisor);
   }

   proxyFactory.setTargetSource(targetSource);
   customizeProxyFactory(proxyFactory);
   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }
   //建立代理對象
   return proxyFactory.getProxy(getProxyClassLoader());
}
複製代碼

    因爲IntroductionAdvisor這樣特殊的攔截器已經指定了加強的類對象,也就是說該類實現了IntroductionInterceptor攔截器,定義過了類的方法須要如何加強,因此上面的方法第一步就是給他指定了加強類,而後建立ProxyFactory代理工廠,先校驗代理工廠是否可以直接代理目標類及其接口,若是不能則evaluateProxyInterfaces方法會將beanClass的接口設置到代理工廠上,接下來經過buildAdvisors方法構建一個可以做用於beanClass類的Advisor集合放入到代理工廠中,以便後續建立代理時使用,最後調用proxyFactory.getProxy方法一個代理對象,在這裏buildAdvisors方法沒可看的,就是將Advisor封裝到一個集合裏等待備用:

protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
   Advisor[] commonInterceptors = resolveInterceptorNames();
   List<Object> allInterceptors = new ArrayList<Object>();
   if (specificInterceptors != null) {
       //將全部Advisor放入到allInterceptors集合中
      allInterceptors.addAll(Arrays.asList(specificInterceptors));
      if (commonInterceptors.length > 0) {
         if (this.applyCommonInterceptorsFirst) {
            allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
         }
         else {
            allInterceptors.addAll(Arrays.asList(commonInterceptors));
         }
      }
   }
   if (logger.isDebugEnabled()) {
      int nrOfCommonInterceptors = commonInterceptors.length;
      int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
   Advisor[] advisors = new Advisor[allInterceptors.size()];
   for (int i = 0; i < allInterceptors.size(); i++) {
       //根據Advice類型轉換成不用的Advisor實現
      advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
   }
   return advisors;
}
複製代碼

下面看看Spring時如何建立代理的

public Object getProxy(ClassLoader classLoader) {
    //createAopProxy方法建立一個代理對象
   return createAopProxy().getProxy(classLoader);
}
複製代碼

    createAopProxy方法建立一個代理對象,這個方法由DefaultAopProxyFactory類實現:

@Override
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);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}
複製代碼

    這個方法決定了是使用jdk代理仍是cglib代理,上面的判斷由三個校驗組成,只要有任何一個結果爲true都會進入else使用jdk代理,來看下這個三個判斷條件:

//自定義指定配置
config.isOptimize()
//是否使用了proxy-target-class="true"
config.isProxyTargetClass()
//目標類是否實現了接口 並且接口不能與SpringProxy類相同
hasNoUserSuppliedProxyInterfaces(config)) 
複製代碼

    這三個方法中的config.isOptimize()config.isProxyTargetClass()默認都會返回false,針對最後一個方法是判斷目標類是否沒有接口,當目標類有接口實現的時候就會走到最後一個else,使用JdkDynamicAopProxy建立代理,也就是jdk代理。

private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
   Class<?>[] ifcs = config.getProxiedInterfaces();
    //若是目標類是實現了接口 會直接返回false
   return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
複製代碼

    而後再來看config.isOptimize(),這個屬性的初始值是false,我並無在源碼中發現有哪裏能夠修改這個值,可是若是咱們自定義一個實現了AbstractAdvisorAutoProxyCreator類的處理器,像下面這樣配置,就能夠設置這個值爲true了。這個應該是基於Sping的擴展的,但這裏對普通註解bean都是false的,因此不須要太糾結。

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
        <property name="optimize" value="true"/>
    </bean>
複製代碼

    而後再看一個比較關鍵的判斷config.isProxyTargetClass(),這個配置實際上就是aop基於自動註解配置裏面的一個屬性

<aop:aspectj-autoproxy proxy-target-class="true">
複製代碼

    這個值就是proxy-target-class的值,這個值的注入是註冊AnnotationAwareAspectJAutoProxyCreator時候設置的,來看下面的代碼,這部分是註冊AnnotationAwareAspectJAutoProxyCreator時候,設置proxyTargetClass屬性的

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
   if (sourceElement != null) {
     // PROXY_TARGET_CLASS_ATTRIBUTE就是proxy-target-class屬性
      boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
      if (proxyTargetClass) {
          //主要執行這個方法
         AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
      }
      boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
      if (exposeProxy) {
         AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
      }
   }
}
複製代碼
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
   if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
       //若是proxy-target-class屬性配置的是true 直接設置這個值的類型爲true
      definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
   }
}
複製代碼

    因此說若是咱們經過<aop:aspectj-autoproxy proxy-target-class="true">方式若是配置了proxy-target-class爲true,則就會直接使用cglib代理了,固然下面還會有一些校驗,不過最終仍是會走到cglib代理,此次先來看看cglib代理吧

六、Cglib模式的代理

6.1 Enhancer的建立

    cglib代理就是由ObjenesisCglibAopProxy這個類實現的,獲取代理對象直接調用其父類CglibAopProxy的getProxy方法獲取。

@Override
public Object getProxy(ClassLoader classLoader) {
   try {
      Class<?> rootClass = this.advised.getTargetClass();
      Class<?> proxySuperClass = rootClass;
      if (ClassUtils.isCglibProxyClass(rootClass)) {
         proxySuperClass = rootClass.getSuperclass();
         Class<?>[] additionalInterfaces = rootClass.getInterfaces();
         for (Class<?> additionalInterface : additionalInterfaces) {
            this.advised.addInterface(additionalInterface);
         }
      }
      //校驗類
      validateClassIfNecessary(proxySuperClass, classLoader);
      //開始建立cglib代理
      Enhancer enhancer = createEnhancer();
      if (classLoader != null) {
         enhancer.setClassLoader(classLoader);
         if (classLoader instanceof SmartClassLoader &&
               ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
            enhancer.setUseCache(false);
         }
      }
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
      //核心點1:獲取回調集合
      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }
      //核心點2:設置過濾器
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);

      // 建立代理對象
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   catch (Exception ex) {
      //異常捕獲拋出
       .........
   }
  
}
複製代碼

    cglib代理的使用方式和咱們以前作的簡單案例的使用是同樣的:

首先建立一個Enhancer,setSuperclass設置要代理的對象,

setInterfaces能夠不設置,由於是基於cglib代理的,不須要接口實現,這裏的含義是指定建立的代理繼承AopProxy的一些接口。

setNamingPolicy設置cglib的命名策略,這裏是以BySpringCGLIB爲前綴:

***getCallbacks***方法用於獲取加強器,會詳細解析。

***enhancer.setCallbackFilter()***用於過濾器用於過濾不須要加強的方法的,也會詳細解析

最後經過***createProxyClassAndInstance***建立一個代理實例對象。

    從上面單獨對cglib單例的使用講解能夠知道,cglib代理是基於asm在字節碼上的實現,能夠動態的建立一個繼承於目標類的代理對象,而後執行加強操做,此外cglib還提供了Callback接口用於執行對方法的攔截,由MethodInterceptor接口的intercept方法攔截,而後cglib還能夠經過CallbackFilter類來指定對不一樣的類或者方法執行不一樣的加強操做,accept方法會返回一個索引,這個索引是設置的CallbackFilter集合中集合的索引,經過accpet方法若是返回0就會使用Callback集合中第一個加強攔截器,下面會針對Spring設置的Callback集合和CallbackFilter分別分析。

6.2 Callback攔截器集合

    首先來看下Spring是如何獲取CallbackFilter集合。

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
   // Parameters used for optimisation choices...
   boolean exposeProxy = this.advised.isExposeProxy();
   boolean isFrozen = this.advised.isFrozen();
   boolean isStatic = this.advised.getTargetSource().isStatic();
   Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
   Callback targetInterceptor;
   if (exposeProxy) {
      //這個方法是表示處理嵌套的,也就是在代理方法中調用本次的其餘方法 那麼針對該方法的加強效果將會失效
      ........
   }
   else {
      targetInterceptor = isStatic ?
            new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
            new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
   }
   Callback targetDispatcher = isStatic ?
         new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
   //這個事核心點,從這裏能夠看出Spring裏面總共設置了6個方法加強器
   //其中第一個就是咱們咱們配置的加強器
   Callback[] mainCallbacks = new Callback[] {
         aopInterceptor,  // for normal advice
         targetInterceptor,  // invoke target without considering advice, if optimized
         new SerializableNoOp(),  // no override for methods mapped to this
         targetDispatcher, this.advisedDispatcher,
         new EqualsInterceptor(this.advised),
         new HashCodeInterceptor(this.advised)
   };
   Callback[] callbacks;
   if (isStatic && isFrozen) {
      .........
   }
   else {
      callbacks = mainCallbacks;
   }
   return callbacks;
}
複製代碼

    首先Spring將建立的的ProxyFactroy對象封裝到DynamicAdvisedInterceptor,這個代理類裏面有以前匹配到的Advisor集合,而後判斷exposeProxy是不是true,這個屬性也是在xml配置文件中配置的,背景就是若是在事務A中使用了代理,事務A調用了目標類的的方法a,在方法a中又調用目標類的方法b,方法a,b同時都是要被加強的方法,若是不配置exposeProxy屬性,方法b的加強將會失效,若是配置exposeProxy,方法b在方法a的執行中也會被加強了。接下來再看mainCallbacks這個集合,這個集合裏面封裝了6個是實現了CallbackMethodInterceptor接口的類,其中第一個aopInterceptor就是封裝了目標類的要加強的邏輯也就是Advisor集合,第二個是針對以前配置的optimize屬性使用的,後面的四個基本上不會作太多業務邏輯上的攔截,Spring最終將這6個加強器集合返回做爲cglib的攔截器鏈,以後經過CallbackFilteraccpet方法返回的索引從這個集合中返回對應的攔截加強器執行加強操做。

6.3 CallbackFilter 過濾器

    Spring實現的是經過ProxyCallbackFilter實現CallbackFilter接口,而後賦值給代理對象,上面分析過,這個接口主要是經過accpet接口實現的,來看看是如何匹配的:

@Override
public int accept(Method method) {
   if (AopUtils.isFinalizeMethod(method)) {
      logger.debug("Found finalize() method - using NO_OVERRIDE");
       //不使用代理
      return NO_OVERRIDE;
   }
   if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
         method.getDeclaringClass().isAssignableFrom(Advised.class)) {
      if (logger.isDebugEnabled()) {
         logger.debug("Method is declared on Advised interface: " + method);
      }
      return DISPATCH_ADVISED;
   }
   // We must always proxy equals, to direct calls to this.
   if (AopUtils.isEqualsMethod(method)) {
      logger.debug("Found 'equals' method: " + method);
      return INVOKE_EQUALS;
   }
   // We must always calculate hashCode based on the proxy.
   if (AopUtils.isHashCodeMethod(method)) {
      logger.debug("Found 'hashCode' method: " + method);
      return INVOKE_HASHCODE;
   }
   Class<?> targetClass = this.advised.getTargetClass();
   // Proxy is not yet available, but that shouldn't matter.
   List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
   boolean haveAdvice = !chain.isEmpty();
   boolean exposeProxy = this.advised.isExposeProxy();
   boolean isStatic = this.advised.getTargetSource().isStatic();
   boolean isFrozen = this.advised.isFrozen();
   if (haveAdvice || !isFrozen) {
      // If exposing the proxy, then AOP_PROXY must be used.
      if (exposeProxy) {
         if (logger.isDebugEnabled()) {
            logger.debug("Must expose proxy on advised method: " + method);
         }
         //使用封裝了Advisor的攔截加強器
         return AOP_PROXY;
      }
      String key = method.toString();
      // Check to see if we have fixed interceptor to serve this method.
      // Else use the AOP_PROXY.
      if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
         // We know that we are optimising so we can use the FixedStaticChainInterceptors.
         int index = this.fixedInterceptorMap.get(key);
         return (index + this.fixedInterceptorOffset);
      }
      else {
         if (logger.isDebugEnabled()) {
            logger.debug("Unable to apply any optimisations to advised method: " + method);
         }
         //使用封裝了Advisor的攔截加強器
         return AOP_PROXY;
      }
   }
   else {
      if (exposeProxy || !isStatic) {
         return INVOKE_TARGET;
      }
      Class<?> returnType = method.getReturnType();
      if (targetClass == returnType) {
         return INVOKE_TARGET;
      }
      else if (returnType.isPrimitive() || !returnType.isAssignableFrom(targetClass)) {
         return DISPATCH_TARGET;
      }
      else {
         return INVOKE_TARGET;
      }
   }
}
複製代碼

    這個方法裏面,咱們只須要關心AOP_PROXY這個值就能夠了,這個值是0,也就是對應上面生成的Callback集合中的第一個攔截加強器,也就是aopInterceptor,這個攔截器裏面封裝了全部可以做用於目標類的Advisor加強類,因此就會調用到切面類定義的切面方法來進行加強操做了。

6.4 Advisor調用鏈

    上面分析過,Spring使用的是aopInterceptor做爲攔截加強器,這個加強器被封裝進了DynamicAdvisedInterceptor類中,這個類實現了MethodInterceptor方法,因此被攔截到的方法會進入intercept方法中,因爲這個類裏面封裝了前面咱們匹配到的全部可以做用於這個類的Advisor集合,可是可能針對這些Advisor可能執行順序有些疑問,雖然Spring容許經過繼承Order接口實現排序,可是好比@Before@After,@Round等是須要在某個方法的不一樣時機執行的,因此這須要構造一個調用鏈,廢話很少,仍是先來看aopInterceptor的intercept方法是如何作的:

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   try {
      if()
      {
          //省略不重要代碼
          ........
      }
      else {
         //建立一個方法執行器 主要邏輯在這裏
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      if (target != null) {
         releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}
複製代碼

    在這個方法裏面,咱們只須要關心CglibMethodInvocationproceed方法,調用鏈就是在這裏構建的

@Override
public Object proceed() throws Throwable {
   //首先獲取到全部的Advisor 而後去除一個下標-1
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }
   //獲取當前Advisor
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    //若是是動態方法匹配器
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         //匹配失敗直接跳過,再次執行proceed方法
         return proceed();
      }
   }
   else {
      //調用攔截器加強集合
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}
複製代碼

    這個方法是構造調用鏈的實現原理,首先獲取到Advisor集合,而後用currentInterceptorIndex表示當前集合剩餘未執行的AdvisorcurrentInterceptorIndex=0的時候,集合中全部的Advisor已經執行完畢了,這種方法仍是很巧妙的,咱們從總體上看下是如何造成調用鏈的,該方法最終調用最後一行代碼,也就是MethodInterceptor類的invoke方法,把當前對象做爲參數傳進去,當前對象也就是aopInterceptor,這裏面封裝了全部符合條件的Advisor(每次都要強調這個類裏面的數據就是讓咱們始終能保持在主線上),該類實現了ProxyMethodInvocation接口間接實現了MethodInvocation接口,最後一行的代碼調用invoke方法,也就是MethodInterceptor接口的方法,來看下這個方法的定義:

public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation invocation) throws Throwable;
}
複製代碼

    invoke的參數是MethodInvocation,因爲aopInterceptorMethodInvocation的實現類,因此能夠將該類做爲參數往下傳遞,前面分析過了切面的註解方法註解對應的類爲:

@Before MethodBeforeAdviceInterceptor
@After AspectJAfterReturningAdvice
@Around AspectJAroundAdvice
@AfterThrowing AspectJAfterThrowingAdvice

    這些生成的Advice切面類毫無疑問都實現了MethodInterceptor方法,而MethodInterceptorinvoke方法須要接收一個類型爲MethodInvocation的對象,正好aopInterceptor實現了MethodInvocation,因此能夠做爲參數在多個Advisor間流轉,每執行完一個Advisor就進入aopInterceptorprocess方法再次校驗,直至執行完全部的Advisor`。

    假設此時的aopInterceptor裏面包含了兩個切面方法MethodBeforeAdviceInterceptorAspectJAfterReturningAdvice,此時先取出AspectJAfterReturningAdvice,而後執行invoke方法,看下AspectJAfterReturningAdviceinvoke方法:

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
   try {
      //此時的mi對象就是 aopInterceptor ,又回到了proceed方法
      return mi.proceed();
   }
   finally {
       //自身的方法最後執行
      invokeAdviceMethod(getJoinPointMatch(), null, null);
   }
}
複製代碼

    AspectJAfterReturningAdviceinvoke方法會首先調用參數 mi 的proceed方法,此時mi就是aopInterceptor對象(這裏面封裝了全部適用於目標類對象的切面方法類),因此會再次返回到攔截器集合裏,取出MethodBeforeAdviceInterceptor對象,調用它的invoke方法,最後自調用AspectJAfterReturningAdvice封裝的切面方法也就是經過@After註解修飾的方法。再來看看AspectJAfterReturningAdvice的invoke方法

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

    這裏會先調用經過@Before註解修飾的方法,而後再次調用aopInterceptor攔截器鏈,雖然這兩個註解是最簡單的,可是確時邏輯最清晰的,也是最容易理解的,其餘的切面註解方法的調用過程也是這樣,最終完成這個攔截器鏈的調用。

    通過上面的步驟,就能夠建立一個代理對象了, 而後Spring將代理對象放入緩存中,咱們使用時獲得的是一個代理對象,調用目標方法,就會執行到加強器方法,進而找到全部的Advisor,構造一個調用鏈,這樣就完成業務的加強了。我相信若是對照着源碼看,多調試幾遍代碼,應該會大體瞭解Spring實現Cglib的代理的一個流程。關於Spring時如何實現cglib代理的暫時就分析到這裏。

七、JDK模式的代理

    jdk模式的代理是經過JdkDynamicAopProxy這個類實現的,因爲jdk的代理的實現比cglib代理稍微簡單一點,我這裏暫時就先不分析了,關於jdk代理模式的解析估計有不少資料,之後有時間我補上這部份內容

八、總結

    Spring源碼很複雜,若是想把每一個細節都搞明白,幾乎是不可能的,並且無疑會浪費不少時間,咱們只須要搞懂經常使用的功能的實現方式,遇到問題能夠從源碼的角度解決問題就好了,說的更高一點,若是咱們能學到做者的一些架構思路以及對代碼的功能抽象,之後能夠對Spring進行擴展,那就再好不過了,廢話很少了,總結下一些這部份內容究竟在說些什麼吧。

    這一部份內容是Spring AOP的第二部份內容,是基於上一步解析@Aspect註解,將注入@Before等註解方法生成Advice切面方法類的過程,這一部分則是使用這些切面方法類,而後找出可以做用於目標類的Advisor,而後建立代理對象,具體流程是:

一、從Advisor緩存中獲取第一步生成的全部Advisor(包括經過xml配置和註解配置的)

二、遍歷Advisor集合,從Advisor裏面獲取PointCut,獲取方法匹配器MethodMatcher,經過match方法匹配一個當前Advisor是否可以做用於目標類,若是能,則放入集合中

三、若是第二部獲取的集合爲空,則直接返回,不須要建立代理,若是不爲空,也就是Advisor緩存中有可以做用於目標類的Advisor,則根據配置選擇不一樣的代理的方法(cglib 仍是 jdk 代理)

四、假如是cglib代理,須要構建Callback攔截加強器集合,而後建立一個CallbackFilter過濾器,選擇合適的攔截加強器對bean實現加強處理

五、返回建立的代理對象。

    寫完這一篇,我終於把這兩個月看Spring的源碼對其的理解記錄下來了, 即使如此,我感受我對Spring仍是知之甚少,還須要鞏固,我認爲若是能經過Spring封裝本身寫的框架,就像Sping-redis,Spring-kafka 那樣直接經過註解或者配置就能夠拿來使用了,確定對Spring有一個更高層次的認識,爲了加深對Spring的理解,經過劉欣老師的講解,我也跟着寫了一遍稍微簡單的Spring實現,那段代碼花了我將近一個月的時間,可是寫完再看Spring的源碼就會容易理解的多,源碼地址是:

github.com/StringBuild…

感興趣的能夠拉下來看看。

相關文章
相關標籤/搜索