Spring AOP源码深入分析
作者:程序员小潘
1. 前言
Spring除了IOC和DI,还有另一个杀手锏功能——Spring AOP。AOP是一种面向切面的编程思想,它的关注点是横向的,不同于OOP的纵向。面向对象编程时,如果我们要给多个类引入同一个行为,唯一的方式就是使用继承,否则就要在这些类里面加入大量重复的代码,如此一来程序将不利于维护。于是,AOP横空出世,它弥补了OOP编程的弊端。Spring内部也有大量特性是通过AOP来实现的,比如我们熟知的数据库事务。
2. 术语
查看源码前,先了解一下AOP的相关术语。
连接点(Joinpoint)
Advice执行的位置,比如:方法前、方法后、发生异常时等等,Spring仅支持方法的连接点。
切点(Pointcut)
连接点的过滤条件,AOP通过切点定位到具体的连接点。
增强/通知(Advice)
应用在连接点的行为,增强的逻辑代码,分为:前置、后置、环绕、异常增强。
增强器(Advisor)
通知器由一个切点(Pointcut)和一个增强(Advice)组成。
切面(Aspect)
由切点(Pointcut)和增强(Advice)组成。
织入(Weaving)
将增强应用到目标连接点的过程,可以静态织入,也可以运行时织入。
目标(Target)
被增强的对象(方法)。
代理(Proxy)
向Target应用Advice之后创建的代理对象。
3. 示例
Spring AOP使用起来非常简单,这里提供一个小示例。
1、定义切面
@Aspect @Component public class MyAspectConfig { @Pointcut("execution(* com.javap.aop.*.*(..))") public void pointCut() { } /** * 每一个增强方法,都会被封装成Advisor对象 * * @see Advisor */ @Before("pointCut()") public void before() { System.err.println("before"); } @After("pointCut()") public void after() { System.err.println("after"); } @Around("pointCut()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.err.println("Around before"); Object result = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs()); System.err.println("Around after"); return result; } @AfterReturning(value = "pointCut()", returning = "result") public void afterReturning(JoinPoint joinPoint, String result) throws Throwable { System.err.println("afterReturning: " + result); } @AfterThrowing(value = "pointCut()", throwing = "e") public void afterThrowing(Exception e) throws Throwable { System.err.println("afterThrowing: " + e.getMessage()); } }
2、定义bean,也就是需要被增强的目标对象
@Component public class Person { public String say() { System.err.println("say..."); return "aabbccdd"; } /** * final方法,子类无法重写,因此不能被增强 */ public final void eat() { System.err.println("eat..."); } }
3、加上@EnableAspectJAutoProxy
注解,就可以启用AOP了。
@Configuration @ComponentScan("com.javap.aop") @EnableAspectJAutoProxy public class AopApplication { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AopApplication.class); Person person = context.getBean(Person.class); person.say(); person.eat(); } }
控制台输出:
Around before
before
say...
Around after
after
afterReturning: aabbccdd
eat...
4. @EnableAspectJAutoProxy
问题:为什么在启动类上,加上**@EnableAspectJAutoProxy**
注解就可以开启AOP呢???
答案当然要在注解本身找了,该注解上有一个@Import
注解,引入了AspectJAutoProxyRegistrar
类。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { /** * 是否强制使用类代理模式:CGLIB代理 */ boolean proxyTargetClass() default false; /** * 是否暴露代理对象,以通过AopContext获取 */ boolean exposeProxy() default false; }
AspectJAutoProxyRegistrar
类实现了Spring提供的ImportBeanDefinitionRegistrar
接口并重写了registerBeanDefinitions()
方法,如此一来Spring在启动时,就会触发AspectJAutoProxyRegistrar#registerBeanDefinitions()
方法,该方法会向容器内注册一个特别重要的类AnnotationAwareAspectJAutoProxyCreator
。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { /** * 注册AnnotationAwareAspectJAutoProxyCreator类 */ AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); // 获取注解信息 AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { /** * 将注解属性写入到BeanDefinition * proxyTargetClass:是否通过CGLIB类代理模式 * exposeProxy:是否暴露代理类,以通过AopContext获取 */ if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } }
AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor接口,属于Spring的扩展点之一,Spring在实例化bean实例后,会触发该扩展点,对bean做扩展和增强,也就是返回织入了Advice后的代理对象。
5. AbstractAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator既然实现了BeanPostProcessor接口,那么必然要重写postProcessAfterInitialization()
方法来返回增强后的bean,重写方法在父类AbstractAutoProxyCreator里。
@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 循环依赖,提前暴露bean时,代理对象可能已经生成 if (this.earlyProxyReferences.remove(cacheKey) != bean) { // 生成增强后的代理对象,如果有需要 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
Spring会调用wrapIfNecessary()
方法来返回增强后的bean。**所有的bean都会被BeanPostProcessor处理,但并不是所有的bean都需要增强,Spring会根据切点表达式判断beanClass是否需要被增强。**这个判断过程是比较耗时的,因为要解析beanClass所有的方法去和切点表达式进行匹配,所以Spring加了一个Map缓存解析过的beanClass。
判断bean是否要增强也很简单,通过getAdvicesAndAdvisorsForBean()
方法去解析能应用到beanClass的所有增强器Advisor,如果没有增强器可用于该beanClass,也就不需要增强,直接原样返回bean即可,否则基于Advisor去创建增强后的代理对象。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { // 已增强 return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { // 已经解析过beanClass,且判定为无需增强 return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { // 无需增强 this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } /** * 获取beanClass可用的Advisor */ Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 基于Advisor,创建代理对象 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } // beanClass无可用的Advice,无需增强 this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
6. 构建Advisor
bean是否要增强,是通过beanClass是否有可用的Advisor来判断的。那么,Advisor是怎么来的呢?
查找bean可用的Advisor的方法在AbstractAdvisorAutoProxyCreator#findEligibleAdvisors()
,先从容器内找出所有的Advisor,也就是我们在@Aspect
类里定义的各种增强方法,然后根据切点表达式过滤出可以应用到当前beanClass的Advisor。然后将一个特殊的拦截器ExposeInvocationInterceptor插入到首位,最后将拦截器排个序返回。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { /** * 查找容器内所有的Advisor:@Aspect类里定义的各种增强方法 */ List<Advisor> candidateAdvisors = findCandidateAdvisors(); /** * 根据切点表达式,过滤出可以应用到beanClass的Advisor */ List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); /** * 将ExposeInvocationInterceptor拦截器插入到第一个 */ extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
构建Advisor的职责交给了BeanFactoryAspectJAdvisorsBuilder
类的buildAspectJAdvisors()
方法,它首先从容器内找出所有加了@Aspect注解的bean,然后解析bean所有加了@Pointcut
、@Around
、@Before
、@After
、@AfterReturning
、@AfterThrowing
注解的方法,将它们封装成Advisor对象。
/** *将bean里面加了指定注解的方法封装成对应的Advice *@Before -> AspectJMethodBeforeAdvice * Around -> AspectJAroundAdvice *@After -> AspectJAfterAdvice *....... */ List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
容器内的Advisor对象并不能都应用到目标bean对象,所以Spring还会调用findAdvisorsThatCanApply()
方法过滤出当前beanClass可用的Advisor。
7. 创建代理对象
如果当前bean有可用的Advisor,也就意味着需要对bean做增强,此时会调用createProxy()
方法创建增强后的代理对象。
问题:使用JDK代理还是CGLIB代理?
我们可以通过属性proxyTargetClass
强制使用CGLIB代理,如果不指定,Spring的规则是:如果beanClass实现了接口,且接口至少有一个自定义方法,那么就用JDK代理,否则用CGLIB代理。
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) { // 获取beanClass实现的所有接口 Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader()); boolean hasReasonableProxyInterface = false; /** * 遍历接口 * 1.非配置的回调接口,即Spring内置的接口不算,例如InitializingBean、Aware等接口 * 2.非内部语言接口,例如GroovyObject * 3.接口至少有一个自定义方法 */ for (Class<?> ifc : targetInterfaces) { if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) && ifc.getMethods().length > 0) { hasReasonableProxyInterface = true; break; } } if (hasReasonableProxyInterface) { // Must allow for introductions; can't just set interfaces to the target's interfaces only. for (Class<?> ifc : targetInterfaces) { proxyFactory.addInterface(ifc); } } else { // 没有实现有效接口,使用CGLIB代理 proxyFactory.setProxyTargetClass(true); } }
最终,Spring会通过ProxyFactory去创建代理对象。
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 proxyFactory = new ProxyFactory(); // 属性赋值给ProxyFactory proxyFactory.copyFrom(this); /** * 判断使用JDK代理还是CGLIB代理 * 如果beanClass实现了接口,且接口至少有一个自定义方法,则使用JDK代理 * 否则CGLIB代理 */ 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); // 是否冻结,也就是Advisor不可再变更 proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 创建动态代理 两种方式 JDK CGlib return proxyFactory.getProxy(getProxyClassLoader()); }
AopProxy有两种实现,分别是**JdkDynamicAopProxy**
和**CglibAopProxy**
**,前者使用JDK动态代理的方式将Advice织入bean,后者通过CGLIB生成子类的方式将Advice织入bean。**我们以CGLIB为例,创建代理对象的方法是CglibAopProxy#getProxy()
。
@Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); } try { Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); Class<?> proxySuperClass = rootClass; if (ClassUtils.isCglibProxyClass(rootClass)) { proxySuperClass = rootClass.getSuperclass(); Class<?>[] additionalInterfaces = rootClass.getInterfaces(); for (Class<?> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } // Validate the class, writing log messages as necessary. validateClassIfNecessary(proxySuperClass, classLoader); // Configure CGLIB Enhancer... Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); /** * 会实现接口 * @see org.springframework.aop.SpringProxy * @see org.springframework.aop.framework.Advised */ enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader)); /** * 获取方法回调,AOP主要看 * @see DynamicAdvisedInterceptor */ Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types); // Generate the proxy class and create a proxy instance. return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException | IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } }
Spring会通过Enhancer去创建子类对象来增强目标bean,生成的子类默认会实现org.springframework.aop.SpringProxy
接口用来标记它是一个Spring生成的代理类。我们重点看给Enhancer对象设置的Callback,因为它会对方法做拦截,也就是说,我们调用子类的增强方法,其实就是在调用Callback#intercept()
。Spring考虑的比较全面,会针对main
、equals
、hashCode
等方法做处理,针对AOP我们主要看AopInterceptor
即可。
/** * Spring考虑了很多情况, *比如对main、equals、hashCode方法的拦截 *AOP主要看aopInterceptor */ Callback[] mainCallbacks = new Callback[]{ aopInterceptor,ll for normal advice targetInterceptor,// invoke target without considering advice,if optimized new SerializableNo0p(),l/ no override for methods mapped to this targetDispatcher, this.advisedDispatcher, new EqualsInterceptor(this.advised), new HashCodeInterceptor(this.advised) };
aopInterceptor是DynamicAdvisedInterceptor子类对象,用于处理AOP方法调用,也就是说,AOP增强逻辑就在DynamicAdvisedInterceptor#intercept()
里。
// Choose an "aop" interceptor (used for AOP calls). Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
8. DynamicAdvisedInterceptor
调用CGLIB增强子类的方法,其实会触发DynamicAdvisedInterceptor#intercept()
方法,看看Spring增强的逻辑。
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); try { /** * 是否要暴露代理对象,以通过AopContext获取 * 写入ThreadLocal */ if (this.advised.exposeProxy) { oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } /** * 获取目标Bean实例 * 如果是prototype 此时会创建新的实例 */ target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); /** * 获取方法能应用到的Advice,拦截器调用链 * @see org.springframework.aop.interceptor.ExposeInvocationInterceptor * @see org.springframework.aop.aspectj.AspectJAfterThrowingAdvice * @see org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor * @see org.springframework.aop.aspectj.AspectJAfterAdvice * @see org.springframework.aop.aspectj.AspectJAroundAdvice * @see org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor */ List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { /** * 没有Advice,直接调用父类方法 */ Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } // 处理一下返回结果的类型 retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } }
方法拦截器主要做了三件事:
- 判断是否要暴露代理对象,如果要则写入ThreadLocal
- 获取方法能应用到的Advice,构建拦截器调用链
- 触发拦截器调用链
如果方法没有可用的Advice,也就不需要增强,直接调用父类方法即可。反之方法需要增强,Spring会new一个CglibMethodInvocation对象,触发拦截器调用链。
9. CglibMethodInvocation
CglibMethodInvocation#proceed()
方法会触发拦截器调用链,Spring通过一个int变量currentInterceptorIndex
来记录当前执行的拦截器索引,通过和总的拦截器数量判断是否调用完所有的拦截器,如果是则通过invokeJoinpoint()
方法去调用目标方法,反之则去执行Advice增强。
public Object proceed() throws Throwable { /** * currentInterceptorIndex 当前执行的拦截器索引 * interceptorsAndDynamicMethodMatchers.size() 所有拦击器个数 * 执行完最后一个拦截器,就要执行目标方法了 */ if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { // 最终反射调用目标方法 return invokeJoinpoint(); } // 触发Advice增强 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { return proceed(); } } else { return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
10. Advice子类
Spring AOP提供了五种Advice增强,分别是通过@Around
、@Before
、@After
、@AfterReturning
、@AfterThrowing
注解标记的方法。这五种Advice分别对应五个Advice子类实现。
1、AspectJAfterThrowingAdvice
先走下一个拦截器,只有在发生异常时,才触发的Advice。
@Override public Object invoke(MethodInvocation mi) throws Throwable { try { // 先走下一个拦截器 return mi.proceed(); } catch (Throwable ex) { if (shouldInvokeOnThrowing(ex)) { /** * 只有发生异常了,才会执行@AfterThrowing Advice */ invokeAdviceMethod(getJoinPointMatch(), null, ex); } throw ex; } }
2、AfterReturningAdviceInterceptor
走剩下所有的拦截器,拿到返回结果。正常执行完毕才触发的Advice,如果发生异常则不触发。
@Override public Object invoke(MethodInvocation mi) throws Throwable { // 直接走剩下的拦截器,拿到返回结果 Object retVal = mi.proceed(); /** * 正常执行完毕,才触发 @AfterReturning Advice * 如果期间发生异常,则不触发 */ this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; }
3、AspectJAfterAdvice
先走下一个拦截器,执行完毕再最终调用的Advice,不论是否发生异常。
@Override public Object invoke(MethodInvocation mi) throws Throwable { try { /** * 先走下一个拦截器,执行完毕再最终调用@After Advice */ return mi.proceed(); } finally { /** * 执行@After Advice方法 * @After Advice一定会被调用,不管是否发生异常,因为在finally */ invokeAdviceMethod(getJoinPointMatch(), null, null); } }
4、AspectJAroundAdvice
直接触发Advice,是否继续调用后续Advice由我们自己决定。
@Override public Object invoke(MethodInvocation mi) throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); } /** * 直接触发Advice,是否继续调用后续Advice,由@Around Advice自己决定 */ ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); return invokeAdviceMethod(pjp, jpm, null, null); }
5、MethodBeforeAdviceInterceptor
先触发当前Advice,再调用下一个Advice。
@Override public Object invoke(MethodInvocation mi) throws Throwable { /** * 先触发 @Before Advice * 再调用下一个Advice */ this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); return mi.proceed(); }
到此这篇关于Spring AOP源码深入分析的文章就介绍到这了,更多相关Spring AOP内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!