縱觀整個Spring的發展歷史,註解的配置正逐步替代xml的配置,到SpringBoot時代,徹底能夠用註解的配置替換繁雜的xml配置,例如咱們須要開啓AOP功能只要在代碼上配置上@EnableAspectJAutoProxy。廢話很少說,咱們這節來分析下@EnableAspectJAutoProxy註解的背後的實現。java
先看下@EnableAspectJAutoProxy屬性詳情:spring
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; }
@EnableAspectJAutoProxy有2個配置參數proxyTargetClass和@Import,@Import是個固定配置,寫死成AspectJAutoProxyRegistrar類型,Spring在解析此註解配置時會建立AspectJAutoProxyRegistrar並調用registerBeanDefinitions方法。數組
在Spring中,動態代理有2種實現方式:緩存
基於CGLIB來實現框架
基於Java原生的Proxy實現,這種方式原類必需要定義接口。ide
這個參數就是表示動態代理實現方式,若是值設置true,表示須要代理類都基於CGLIB來實現;默認狀況下值是設置成false表示若是原類若是定義了接口則經過Proxy實現不然基於CGLIB來實現。post
在Spring中@Import能夠配置3種類型:ui
在基於@Configuration的類上引入beanthis
這個配置比較簡單,直接在配置了@Configuration的類上配置@Import引入bean便可,舉個例子:lua
@Configuration @Import(value={Bean.class}) public class Config { }
以上例子就會把Bean加入到Spring容器。
若是引入類須要通過註解上的參數來決定可使用此方式。
public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); }
AnnotationMetadata類型能夠獲取註解上的參數配置。@EnableTransactionManagement就是經過次方式配置,來看下他配置:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; }
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: return null; } } }
以上例子就是經過獲取AnnotationMetadata裏的配置來決定引入那些bean。
在解析@Import配置傳入AnnotationMetadata和BeanDefinitionRegistry兩個參數到registerBeanDefinitions方法。
這種方式更加靈活,能夠直接經過BeanDefinitionRegistry將本身想要的bean加入到Spring容器。
@EnableAspectJAutoProxy註解就是經過這種方式將代理建立器AnnotationAwareAspectJAutoProxyCreator加入到到Spring容器。
Spring的AOP的實現是基於Aspectj項目的註解及註解的解析實現,其核心的組件仍是Spring本身的實現包括Advisor(切面),Pointcut(切點),Advice(加強)。
切面就是將非邏輯代碼抽離到一個指定位置,讓編寫邏輯代碼的人感受不到非邏輯代碼的存在,實際執行卻能讓非邏輯代碼發揮效果,說白了就是切點和加強的組合
切點是對邏輯代碼加強的位置,好比在邏輯代碼執行前加強。
加強就是對切點位置的具體的實現,好比在邏輯代碼執行前記錄操做日誌,而記錄操做日誌這個操做的具體實現就是加強
舉個例子:
@Aspect //聲明一個切面 public class LogAspect { @After(value="@annotation(com.just.spring4.ch1.aop.TestAction)") //經過@after註解聲明一個建言,並使用@Pointcut定義的切點 public void after(JoinPoint joinPoint){ System.out.println("記錄日誌"); } }
@Aspect註解的LogAspect類就是切面。
@After註解的並匹配上的方法就是切點。
而System.out.println("記錄日誌")輸出就是加強。
Advisor是一個接口它的實現表明了切面,切面包含了切點和加強,先看下Advisor接口的定義:
public interface Advisor { Advice getAdvice(); boolean isPerInstance(); }
getAdvice()方法返回了一個加強組件Advice。isPerInstance()方法在Spring框架中暫未被使用。
根據上面定義Advisor接口其實少了一個切點組件返回,因此Advisor通常不會被直接實現,Spring定義了2個接口來擴展Advisor的實現:
public interface IntroductionAdvisor extends Advisor, IntroductionInfo { ClassFilter getClassFilter(); void validateInterfaces() throws IllegalArgumentException; }
getClassFilter()方法返回了一個切點組件ClassFilter,它是Class的切點,來匹配Class是否知足條件來加強實現。validateInterfaces()方法的功能是Advice是否實現指定的接口。
IntroductionAdvisor主要作Class的匹配而不關心Method匹配狀況。
public interface PointcutAdvisor extends Advisor { Pointcut getPointcut(); }
getPointcut()返回了一個切點組件Pointcut,Pointcut和ClassFilter不一樣Pointcut包裝了ClassFilter和MethodMatcher,也就是說Pointcut即匹配Class也匹配Method,2者同時知足狀況下才能加強實現。
Pointcut表示切入的位置,在Spring中Pointcut接口是作一個匹配的功能包括Class和Method的匹配,只有匹配上才能作進一步加強。Pointcut接口以下
public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); Pointcut TRUE = TruePointcut.INSTANCE; }
getClassFilter()返回的ClassFilter是Class匹配器,getMethodMatcher()返回的MethodMatcher是Class匹配器,但不必定每一個匹配器都會有做用,舉個例子:
AnnotationMatchingPointcut是註解的切面類,它能夠匹配Class上的註解或者Method的上的註解,看下它的構造方法:
public AnnotationMatchingPointcut( Class<? extends Annotation> classAnnotationType, Class<? extends Annotation> methodAnnotationType) { Assert.isTrue((classAnnotationType != null || methodAnnotationType != null), "Either Class annotation type or Method annotation type needs to be specified (or both)"); if (classAnnotationType != null) { this.classFilter = new AnnotationClassFilter(classAnnotationType); } else { this.classFilter = ClassFilter.TRUE; } if (methodAnnotationType != null) { this.methodMatcher = new AnnotationMethodMatcher(methodAnnotationType); } else { this.methodMatcher = MethodMatcher.TRUE; } }
構造方法有2個參數classAnnotationType表示Class上的註解,methodAnnotationType表示Method上的註解,2參數必傳一個若是其中一個不傳,會設置ClassFilter.TRUE或MethodMatcher.TRUE表示全類型匹配,這種匹配通常對整個類或單個方法進行加強。
StaticMethodMatcherPointcut是抽象類,自己是作一種規範,其規範必需要實現MethodMatcher的匹配邏輯來匹配Method,ClassFilter可不實現,不實現會全類型匹配。
Advice是加強的接口,Spring提供不少加強的實現。舉例以下:
AspectJMethodBeforeAdvice
對應AspectJ中的@Before註解的加強實現,在方法執行前加強。
AspectJAfterAdvice
對應AspectJ中的@After註解的加強實現,在方法執行後加強。
AspectJAfterReturningAdvice
對應AspectJ中的@AfterReturning註解的加強實現,在方法執行後並獲取返回值,能夠根據返回值作加強。
AspectJAfterThrowingAdvice
對應AspectJ中的@AfterThrowing註解的加強實現,在方法執行後並獲取執行錯誤信息,能夠根據錯誤信息作加強。
AspectJAroundAdvice
對應AspectJ中的@Around註解的加強實現,在方法執行先後均可以作加強。
實現AOP關鍵有2個類AspectJAutoProxyRegistrar和AnnotationAwareAspectJAutoProxyCreator。
前面提到Spring經過@EnableAspectJAutoProxy的@Import配置在解析註解時會建立AspectJAutoProxyRegistrar並調用registerBeanDefinitions方法。
看下它的實現
public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } }
代碼的功能分2部分,第1部分將AnnotationAwareAspectJAutoProxyCreator類型包裝成BeanDefinition註冊到Spring容器。第2部分將proxyTargetClass的配置設置到此BeanDefinition裏。
這裏AnnotationAwareAspectJAutoProxyCreator類是實現AOP的核心後面詳細說明。
先看下AnnotationAwareAspectJAutoProxyCreator的結構
能夠看到AnnotationAwareAspectJAutoProxyCreator實現了BeanPostProcessor接口,這個接口作什麼用的呢?
先看下這個接口的定義:
public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
在Spring容器中BeanDefinition被建立成bean後會依次調用實現了這個接口的類裏postProcessBeforeInitialization和postProcessAfterInitialization,這2個方法做用能夠對bean作修改就是能夠對bean作代理或對bean作屬性修改,而2這個方法先後執行分別對應bean初始化先後:
好了,咱們知道AnnotationAwareAspectJAutoProxyCreator就是利用BeanPostProcessor來作代理。
AnnotationAwareAspectJAutoProxyCreator代理是放在postProcessAfterInitialization方法裏處理,因此對代理對象自己的初始化不受影響。
來看下它的實現:
@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
先查看earlyProxyReferences緩存判斷是否已經建立代理。(在循環引用狀況下會調用getEarlyBeanReference提早建立代理),若是還未建立調用wrapIfNecessary方法去建立代理,咱們看下其實現:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (beanName != null && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } 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; }
前2個if先判斷是否已經建立再判斷是否須要建立代理對象, 若是須要則調用getAdvicesAndAdvisorsForBean方法獲取切面Advisor,再根據Advisor調用createProxy來建立代理對下,咱們分兩部分來說。
getAdvicesAndAdvisorsForBean方法的實如今AbstractAdvisorAutoProxyCreator類中實現,委託給了findEligibleAdvisors方法去獲取,看下它的實現:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { List<Advisor> candidateAdvisors = findCandidateAdvisors(); List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
第一步調用findCandidateAdvisors方法先獲取註冊到Spring容器中的Advisor,在獲取並解析註解了@Aspect的bean中的全部Advisor。
@Override protected List<Advisor> findCandidateAdvisors() { List<Advisor> advisors = super.findCandidateAdvisors(); advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); return advisors; }
this.aspectJAdvisorsBuilder.buildAspectJAdvisors()這塊代碼就是利用了aspectj來解析註解了@Aspect。
第二步調用findAdvisorsThatCanApply來過濾Advisor,Advisor(切點)包含了Pointcut(切點)和Advice(加強),findAdvisorsThatCanApply方法的過濾就是利用Advisor中Pointcut匹配Class或Method來達到過濾的目的。
第三步調用extendAdvisors方法,extendAdvisors在AnnotationAwareAspectJAutoProxyCreator做用就是在全部的advisors節點最前面插入一個Advisor(有advisors節點前提下),此Advisor比較特殊它的Pointcut是全類型匹配的(匹配全部Class和Method),它主要功能是在於它的Advice(加強),它的Advice實現是ExposeInvocationInterceptor類,看類的名稱就知道,對外暴露的類,就是全部Advice調用鏈的第一環,ExposeInvocationInterceptor做用就是將調用信息存在ThreadLocal實現的上下文信息裏,供調用鏈後續的Advice獲取使用,能夠看下它實現:
第四步若是存在advisors節點則調用sortAdvisors對其排序,這排序規則是根據Advisor裏的order字段排序,固然若是存在第三步所說的特殊Advice它會排在最前面。
回到wrapIfNecessary方法獲取到advisors接下去就是建立代理了。
建立代理對象調用的createProxy方法,來看下它的實現:
protected Object createProxy( Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { 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); 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()); }
代理對象的建立主要依託於代理工廠ProxyFactory類來建立,看下它的繼承關係:
它和AnnotationAwareAspectJAutoProxyCreator都是繼承了ProxyConfig類。
createProxy方法第一步先經過ProxyConfig裏的copyFrom方法將AnnotationAwareAspectJAutoProxyCreator裏的配置拷貝至ProxyFactory。
第二步從新設置proxyTargetClass,Java的Proxy代理能對象的前提是此對象必須實現了接口,這步若是原先proxyTargetClass設置false,須要先判斷其是否實現了接口而且其接口非InitializingBean,DisposableBean,Aware,Spring自帶接口。
第三步設置Advisors,而且凍結設置使後面不能修改Advisors。
最後調用ProxyFactory裏的getProxy方法去代理對象。
ProxyFactory裏代理的是有2種:
基於CGLIB來實現
基於Java原生的Proxy實現
主要取決於proxyTargetClass參數。
不管是CGLIB建立代理的CglibAopProxy仍是Java原生的Proxy實現建立JdkDynamicAopProxy都是基於接口AopProxy:
public interface AopProxy { Object getProxy(); Object getProxy(ClassLoader classLoader); }
AopProxy有2個方法,基於默認ClassLoader建立代理和基於用戶自定義建立。
以JdkDynamicAopProxy實現爲例看下實現。
JdkDynamicAopProxy實現了InvocationHandler,也就是代理方法的調用,會分發到自己。
看下其getProxy方法若是建立代理:
@Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
先獲取須要代理的接口,而後標記equals或hashCode方法是是否被覆蓋,供調用時用,最後建立代理對象。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ... try { ... target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); if (chain.isEmpty()) { retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); } else { invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed(); } ... return retVal; } ... }
先獲取代理的advice(加強器)數組(這裏命名chain),若是advice直接調用被代理對象的方法,不然調用invocation.proceed()方法。invocation.proceed()的調用過程先是鏈式調用advice,最後執行其被代理對象的方法。看下proceed的實現:
public Object proceed() throws Throwable { if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } 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 { return proceed(); } } else { return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
此方法的advice的鏈式調用的原理是遞歸調用:
proceed() -> invoke(this) -> proceed() ->...->invoke(this)->proceed()->invokeJoinpoint()
每作一次代碼的加強,currentInterceptorIndex指針加1,直到全部的advice被調用完成,才執行其被代理對象的方法。