spring-AOP(二)實現原理之AspectJ註解方式
在上一篇 AOP%25EF%25BC%2588%25E4%25B8%2580%25EF%25BC%2589%25E5%25AE%259E%25E7%258E%25B0%25E5%258E%259F%25E7%2590%2586%2F" rel="nofollow,noindex">spring-AOP(一)實現原理 我們瞭解瞭如何使用ProxyFactory來建立AOP代理物件,但其過程需要實現一些介面,並且需要一些比較複雜的配置。因此,在spring2.0之後,提供了一種較為便利的方式。 使用@Aspect註解宣告一個切面類,之後通過@EnableAspectJAutoProxy註解來註冊代理生成類AnnotationAwareAspectJAutoProxyCreator。下面我們來看一下其原理
spring中如何使用@AspectJ
織入方式
手動織入
- 首先需要定義一個Aspect
package com.luhc.springaop.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; /** * @author luhuancheng * @date 2018/11/20 */ @Aspect public class PerformanceTraceAspect { @Pointcut("execution(* *..*method1()) || execution(* *..*method2())") public void pointcutName(){} @Pointcut("@annotation(AnyJoinpointAnnotation)") public void matchPointcut(){} @Before("matchPointcut()") public void before() { System.out.println("+++++++++@annotation++++++++++"); } @Around("pointcutName()") public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); try { return joinPoint.proceed(); } finally { System.out.println(String.format("cost time %s", System.currentTimeMillis() - start)); } } } 複製程式碼
- 通過AspectJProxyFactory手動織入
private static void manualWeaver() { // 手動織入 AspectJProxyFactory weaver = new AspectJProxyFactory(); weaver.setProxyTargetClass(true); // 宣告目標物件 weaver.setTarget(new Foo()); // 宣告切面 weaver.addAspect(PerformanceTraceAspect.class); // 獲取代理 Object proxy = weaver.getProxy(); // 執行已經織入切面邏輯的方法 ((Foo) proxy).method1(new FlyImpl()); ((Foo) proxy).method2(); } 複製程式碼
自動織入
自動織入方式需要AnnotationAwareAspectJAutoProxyCreator類的支援,通過將AnnotationAwareAspectJAutoProxyCreator和需要的Aspect切面、以及目標物件宣告在IOC/">IOC容器中,在容器啟動期間,AnnotationAwareAspectJAutoProxyCreator將自動為目標物件生成織入切面邏輯的代理物件
- 宣告配置
package com.luhc.springaop.aspect; import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author luhuancheng * @date 2018/11/21 */ @Configuration public class AspectConfigure { /** * 自動織入器 * @return */ @Bean public AnnotationAwareAspectJAutoProxyCreator proxyCreator() { AnnotationAwareAspectJAutoProxyCreator proxyCreator = new AnnotationAwareAspectJAutoProxyCreator(); // 預設為false,如果目標物件未實現介面的話,其代理物件也是通過cglib生成 proxyCreator.setProxyTargetClass(false); return proxyCreator; } /** * 未實現介面的目標物件 * @return */ @Bean public Foo foo() { return new Foo(); } /** * 切面 * @return */ @Bean public PerformanceTraceAspect performanceTraceAspect() { return new PerformanceTraceAspect(); } } 複製程式碼
- 從IOC容器中取出織入切面後的代理物件
private static void autoWeaver() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfigure.class); Foo foo = context.getBean(Foo.class); // 此時的foo物件,是以及經過AnnotationAwareAspectJAutoProxyCreator處理後的代理物件 foo.method1(new FlyImpl()); foo.method2(); } 複製程式碼
@AspectJ形式的Pointcut宣告方式
Spring AOP支援以下的Pointcut表示式
// 任意包下的具有任意引數、任意返回值的任意方法 // @Pointcut("execution(* *..*(..))") // com.luhc.springaop的任意子包下的任意類,springAOP只支援方法級別的JoinPoint,因此這個表示式將匹配指定類所宣告的所有方法執行 // @Pointcut("within(com.luhc.springaop..*)") // 匹配代理物件型別為Foo的所有方法級的JoinPoint // @Pointcut("this(Foo)") // 匹配目標物件型別為Fly的所有方法級的JoinPoint // @Pointcut("target(Fly)") // 匹配傳入引數型別為Fly和Foo的所有方法執行的JoinPoint,不關心方法在哪個類中定義 // @Pointcut("args(Fly,Foo)") // @within@target的區別在於@within是靜態匹配、@target是在執行時動態匹配 // 匹配所有被註解AnyJoinpointAnnotation標註了的類的所有方法級的JoinPoint // @Pointcut("@within(AnyJoinpointAnnotation)") // 匹配所有目標物件唄註解AnyJoinpointAnnotation標註了的類的所有方法級的JoinPoint // @Pointcut("@target(AnyJoinpointAnnotation)") // 匹配方法引數型別被註解AnyJoinpointAnnotation標註了的所有方法級的JoinPoint // @Pointcut("@args(AnyJoinpointAnnotation)") // 匹配方法被註解AnyJoinpointAnnotation標註了的所有方法級的JoinPoint // @Pointcut("@annotation(AnyJoinpointAnnotation)") // 可以使用 || 和 && 來表達pointcut之間的邏輯運算 // @Pointcut("execution(* *..*method1()) || execution(* *..*method2())") 複製程式碼
剖開@AspectJ在SpringAOP中的真相
@AspectJ形式的Pointcut
AnnotationAwareAspectJAutoProxyCreator通過反射獲取到@Pointcut註解的資訊,在內部例項化為AspectJExpressionPointcut物件。 AspectJExpressionPointcut實現了ClassFilter、MethodMatcher,其內部實現邏輯代理給了PointcutParser,最終生成為PointcutExpression(PointcutExpressionImpl實現類)例項
@AspectJ形式的Advice
在Advice定義中訪問Joinpoint處的方法引數
使用org.aspectj.lang.JoinPoint
將Advice方法的第一個引數宣告為org.aspectj.lang.JoinPoint型別,我們可以通過呼叫org.aspectj.lang.JoinPoint相關方法獲取需要的資料
@Before("matchPointcut()") public void before(org.aspectj.lang.JoinPoint joinPoint) { // 獲取方法名 System.out.println(joinPoint.getSignature().getName()); System.out.println("+++++++++@annotation++++++++++"); } 複製程式碼
使用args標誌符繫結
// 可以同時使用識別符號和JoinPoint,但是JoinPoint必須放在第一個引數位置上 @Before("matchPointcut() && args(name)") public void before(JoinPoint joinPoint, String name) { System.out.println("獲取到Joinpoint上的入參:" + name); System.out.println("獲取到Joinpoint的方法名: " + joinPoint.getSignature().getName()); } 複製程式碼
捕獲異常@AfterThrowing
@AfterThrowing(pointcut = "matchPointcut()", throwing = "e") public void afterThrowing(JoinPoint joinPoint, RuntimeException e) { System.out.println("方法:" + joinPoint.getSignature().getName() + "發生異常:" + e.getMessage()); } 複製程式碼
捕獲返回值@AfterReturning
@AfterReturning(pointcut = "pointcutName()", returning = "result") public void afterReturning(JoinPoint joinPoint, String result) { System.out.println("方法:" + joinPoint.getSignature().getName() + "獲得返回值:" + result); } 複製程式碼
方法正常執行完成後(未丟擲異常)@After
@After("pointcutName()") public void after(JoinPoint joinPoint) { System.out.println("方法:" + joinPoint.getSignature().getName() + ": 執行完畢"); } 複製程式碼
@Around環繞方法
@Around與其他幾個Advice註解不同,在@Around方法中,第一個引數必須為org.aspectj.lang.ProceedingJoinPoint
@Around("pointcutName()") public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); try { return joinPoint.proceed(); } finally { System.out.println(String.format("cost time %s", System.currentTimeMillis() - start)); } } 複製程式碼
公開當前呼叫的代理物件
// 在目標物件方法中,通過此方法可以獲取當前目標物件的代理物件 AopContext.currentProxy() 複製程式碼
@AspectJ形式的Spring AOP代理自動生成原理
一個例子
我們使用註解配置的一個spring aop的demo(新版本的spring中推薦使用註解來配置容器)
// 假設這是一個業務介面 public interface Fly { void fly(); } // 業務介面實現 public class FlyImpl implements Fly { @Override public void fly() { System.out.println("++++++++++++++++ Fly ++++++++++++++++"); } } // 宣告一個切面 @Aspect public class PerformanceTraceAspect { // 匹配任意返回值、任意包下的、任意引數的fly方法。在這個demo中,將匹配到com.luhc.springaop.aspect.FlyImpl#fly這個方法 @Pointcut("execution(* *..*fly(..))") public void pointcutName(){} // 宣告切入的前置邏輯 @Before("pointcutName()") public void before(JoinPoint joinPoint) { // 可以通過JoinPoint獲取到pointcut方法的詳細資訊 System.out.println("Before --> 獲取到Joinpoint的方法名: " + joinPoint.getSignature().getName()); } // 宣告切入的後置邏輯 @After("pointcutName()") public void after(JoinPoint joinPoint) { // 可以通過JoinPoint獲取到pointcut方法的詳細資訊 System.out.println("After --> 獲取到Joinpoint的方法名: " + joinPoint.getSignature().getName()); } // 宣告切入的環繞邏輯(即在方法執行前後切入邏輯) @Around("pointcutName()") public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); try { // 呼叫執行鏈 return joinPoint.proceed(); } finally { System.out.println(String.format("cost time %s", System.currentTimeMillis() - start)); } } } // 配置類 @Configuration // 啟用AOP @EnableAspectJAutoProxy public class AspectConfigure { /** * 實現介面的目標物件 * @return */ @Bean public Fly fly() { return new FlyImpl(); } /** * 切面 * @return */ @Bean public PerformanceTraceAspect performanceTraceAspect() { return new PerformanceTraceAspect(); } } // 執行應用 public class AspectJDemo { // 使用配置類,初始化容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfigure.class); // 從容器中獲取業務介面(此時已經是被處理過的代理物件,即已經切入了切面邏輯) Fly fly = context.getBean(Fly.class); fly.fly(); } 複製程式碼
剖析demo執行機制
從@EnableAspectJAutoProxy註解開始解開神祕面紗
註解EnableAspectJAutoProxy的定義
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { // 是否使用cglib來生成代理 boolean proxyTargetClass() default false; // 是否將代理繫結到ThreadLocal,後續在目標類中可以使用AopContext.currentProxy()來獲取代理物件 boolean exposeProxy() default false; } 複製程式碼
AspectJAutoProxyRegistrar配置類
重點在其元註解@Import上(有機會再分析一下關於spring的@Import註解匯入機制),其匯入了配置類AspectJAutoProxyRegistrar
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { /** * Register, escalate, and configure the AspectJ auto proxy creator based on the value * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing * {@code @Configuration} class. */ @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 重點!!此處向容器注入了AnnotationAwareAspectJAutoProxyCreator類 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } 複製程式碼
註冊過程
public abstract class AopConfigUtils { /** * Stores the auto proxy creator classes in escalation order. */ private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<Class<?>>(); /** * Setup the escalation list. * 在spring中,預設存在三個代理生成類。優先級別從上到下排序,越往後優先順序越高 */ static { APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); // 最高優先順序 } private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { // 如果容器當前已經註冊了代理生成器類,則比較其與AnnotationAwareAspectJAutoProxyCreator的優先順序。取優先順序最高的那個作為代理生成器註冊在容器中。 // 顯然AnnotationAwareAspectJAutoProxyCreator被註冊到容器中 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; } } 複製程式碼
註冊流程總結
- @EnableAspectJAutoProxy註解匯入了配置類AspectJAutoProxyRegistrar
- 配置類AspectJAutoProxyRegistrar呼叫了AopConfigUtils來註冊代理生成器AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator如何做到自動生成代理
類結構

主流程

原始碼分析
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { Object cacheKey = getCacheKey(beanClass, beanName); if (beanName == null || !this.targetSourcedBeans.contains(beanName)) { if (this.advisedBeans.containsKey(cacheKey)) { return null; } if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } // Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. if (beanName != null) { TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { this.targetSourcedBeans.add(beanName); Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } } return null; } // 生成代理物件 @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; } 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; } // Create proxy if we have advice. // 獲取切面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; } protected Object createProxy( Class<?> beanClass, String beanName, 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 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()); } } 複製程式碼
總結
- 描述了@AspectJ形式的Spring AOP如何使用
- spring AOP可以使用的@AspectJ識別符號,如execution、within、this、target、@annotation等
- 描述了spring內部是如何使用@EnableAspectJAutoProxy註解來啟用Spring AOP功能的,以及代理生成器AnnotationAwareAspectJAutoProxyCreator的內部流程時如何根據@Aspect類,來自動生成代理的