1,AOP 的入口
上一节我们在分析解析 AOP 标签的时候, 第一步就是注册了一个类 AspectJAwareAdvisorAutoProxyCreator, 我们说它是 AOP 的入口类. 为什么这样说呢? 来看它父类的父类 AbstractAutoProxyCreator, 它继承了 BeanPostProcessor 接口. 那么, 有两个方法肯定要被调用到 postProcessBeforeInitialization,postProcessAfterInitialization. 一个在依赖注入完成之前调用, 一个在之后调用.
- 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;
- }
wrapIfNecessary 方法则是真正产生代理的地方, 我们先看下它的内部实现.
- protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
- // Create proxy if we have advice.
- // 先看它的注释, 大意说: 如果有通知, 就创建代理.
- // 其实就是在 bean.getClass() 找到所有的通知和 advisor
- // 这里面其实又分为两个步骤:
- // 第一, 在 Bean 工厂找到所有的 Advisor 第二, 根据 beanClass 和 Pointcut 去做匹配
- Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
- if (specificInterceptors != DO_NOT_PROXY) {
- this.advisedBeans.put(cacheKey, Boolean.TRUE);
- // 真正创建代理并返回, 这时候返回的就是代理类了, 把真实的 bean 替换掉
- 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;
- }
看到上面的源码, 整个过程就分为了两步, 匹配和创建返回. 这样就完成了代理类的替换, 是不是有点偷梁换柱的感觉.
匹配
先是找到所有的 Advisor, 这个比较简单. 因为我们知道, 在解析的时候就把配置的通知封装成 advisor 注册了进去. 首先拿到 beanDefinitionNames 容器所有 beanName, 循环判断 bean 的类型是不是 advisor 接口的类型, 符合条件返回.
- public List<Advisor> findAdvisorBeans() {
- // Determine list of advisor bean names, if not cached already.
- String[] advisorNames = null;
- // 先拿到 beanName
- advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
- this.beanFactory, Advisor.class, true, false);
- List<Advisor> advisors = new LinkedList<Advisor>();
- for (String name : advisorNames) {
- if (isEligibleBean(name)) {
- try {
- // 再从 Bean 工厂中拿 bean 的实例
- advisors.add(this.beanFactory.getBean(name, Advisor.class));
- }
- }
- }
- // 返回的 advisors 就是配置文件中所有的 advice 和 advisor
- return advisors;
- }
找到 advisors 并未结束, 还要跟 pointcut 做匹配, 看这些 advisor 符不符合表达式条件. 它最终调用到 Pointcut 的 match 方法. 这个方法嵌套的太深, 就不贴代码了, 核心思想就是判断 class 和 class 对应的 method 是否与 pointcut 表达式匹配.
创建代理
经过上面查找匹配后, 确定当前的 bean 确实需要代理, 就调用 createProxy 方法.
设置代理工厂
- protected Object createProxy(Class<?> beanClass,
- String beanName, Object[] specificInterceptors, TargetSource targetSource) {
- // 创建代理工厂
- ProxyFactory proxyFactory = new ProxyFactory();
- // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
- proxyFactory.copyFrom(this);
- // 将 beanClass 上的接口设置到代理工厂
- evaluateProxyInterfaces(beanClass, proxyFactory);
- // 设置 Advisor 到代理工厂
- Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
- for (Advisor advisor : advisors) {
- proxyFactory.addAdvisor(advisor);
- }
- // 设置目标对象
- proxyFactory.setTargetSource(targetSource);
- return proxyFactory.getProxy(this.proxyClassLoader);
- }
创建
创建代理分为 JDK 的代理和 Cglib 的代理, 这里我们先关注 JDK 的代理. getProxy 方法就调用到 JdkDynamicAopProxy 类的方法. 这个类还实现了 InvocationHandler 接口, 说明它同时还是调用处理程序. 即在调用代理类的 invoke 方法时, 实际上就会调用到 JdkDynamicAopProxy.invoke().
- public Object getProxy(ClassLoader classLoader) {
- // 这里又给代理工厂加了两个接口 SpringProxy 和 Advised
- Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
- // 这个方法就比较熟悉了, 正是 JDK 动态代理的方法
- // 这里的 this 就是 JdkDynamicAopProxy 实例.
- return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
- }
2, 代理类的调用
在实例化 Bean 和完成依赖注入后, 会判断当前的 Bean 是否需要代理, 如果需要, 就生成代理类把原始类替换掉. 在业务方法里面调用的时候, 就会调用到 JdkDynamicAopProxy.invoke().
在执行 invoke 的时候, 我们又可以分为两个步骤...-_-||, 是不是跟二很有缘, 每次都是两个步骤..
获取方法的拦截链
首先从代理工厂中拿到所有的 advisor, 然后判断是不是 PointcutAdvisor 类型, 其次先 matches 一下 targetClass, 再 matches 一下 method, 证明这个类的方法在 pointcut 范围内, 加入 interceptorList, 最后返回.
- public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
- Advised config, Method method, Class<?> targetClass) {
- List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
- //config 就是代理工厂的实例
- for (Advisor advisor : config.getAdvisors()) {
- if (advisor instanceof PointcutAdvisor) {
- PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
- if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
- MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
- MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
- if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
- interceptorList.addAll(Arrays.asList(interceptors));
- }
- }
- }
- }
- return interceptorList;
- }
调用
拿到方法的拦截链, 然后调用. 调用的时候有个地方比较有意思. 它先创建了一个对象 ReflectiveMethodInvocation. 这个对象有两个参数
- currentInterceptorIndex // 当前调用的拦截器的索引, 默认值 - 1
- interceptorsAndDynamicMethodMatchers // 拦截器的列表
然后看它的调用方法.
- public Object proceed() throws Throwable {
- // 如果俩个变量相等, 说明已经调用完了所有的拦截器
- if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
- // 执行被代理方法
- return invokeJoinpoint();
- }
- // 从 - 1 开始, 每次累加 1.interceptorOrInterceptionAdvice 就是对应的每一个通知
- // 比如 before,after,after-returning...
- // 对应的实例类分别是:
- //MethodBeforeAdviceInterceptor,AspectJAfterAdvice,AfterReturningAdviceInterceptor
- Object interceptorOrInterceptionAdvice =
- this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
- // 在调用具体通知的 invoke 方法时, 把当前对象当参数传了过去.
- // 因为在具体的通知执行之后还要回调回来, 执行当前对象的 proceed().
- // 这样就形成了一个通知调用链, 当所有的通知执行完毕, 调用上面的 invokeJoinpoint()
- return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
- }
看完它的调用流程, 我们还有一个疑问. 通知一共有 5 种类型, 前置通知, 后置通知, 方法返回后通知, 环绕通知, 异常通知. 那么, 它是怎么保证调用顺序的呢? 我们来看两个通知类里面具体的实现.
前置通知
- public Object invoke(MethodInvocation mi) throws Throwable {
- // 这个比较简单, 执行完 before 就回调
- this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
- return mi.proceed();
- }
后置通知
- public Object invoke(MethodInvocation mi) throws Throwable {
- // 这个就比较有趣了, 它先执行回调
- // 通过 finally 机制再执行自己的通知方法
- try {
- return mi.proceed();
- }
- finally {
- invokeAdviceMethod(getJoinPointMatch(), null, null);
- }
- }
环绕通知
- // 环绕通知会调用到切面类的自定义方法
- public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
- System.out.println("环绕通知之前");
- // 可以自己判断还要不要回调回去.
- // 回调回去的话, 接着循环调用链
- // 如果不再回调, 就要看链的顺序了, 在它之后的就不再执行
- Object result = joinPoint.proceed();
- System.out.println("环绕通知之后");
- return result;
- }
3, 基于注解的 AOP
如果想使用注解 AOP, 需要开启一个配置.<aop:aspectj-autoproxy />.Spring 解析配置标签的时候, 调用到 AspectJAutoProxyBeanDefinitionParser.parse(). 这个方法主要就干了一件事: 注册入口类 AnnotationAwareAspectJAutoProxyCreator. xml 配置方式的 AOP,Spring 注册的入口类叫做 AspectJAwareAdvisorAutoProxyCreator.
它们的调用流程是一样的, 都是在实例化之后调用爷爷类的 AbstractAutoProxyCreator.postProcessAfterInitialization(). 回忆一下, wrapIfNecessary 方法是真正产生代理的地方. 它先获取所有的通知并与当前的 bean class 匹配, 如果有, 就说明当前的 Bean 需要代理, 则产生代理类. 我们知道, 在 xml 配置方式的 AOP 中, Spring 把配置的通知都封装成 advisor 注册到容器里, 所以在获取的时候, 直接在 Bean 工厂中匹配 Advisor 类型的 Bean 就行. 但是, 在解析注解 AOP 的时候, 我们看到它只是注册了一个入口类而已呀, 并没有注册 advisor, 那么, 在这里怎么获取呢?
目光回到查询 advisor 的方法.
- protected List<Advisor> findCandidateAdvisors() {
- // Add all the Spring advisors found according to superclass rules.
- // 这个是查询 xml 配置方式的 advoisor
- List<Advisor> advisors = super.findCandidateAdvisors();
- // Build Advisors for all AspectJ aspects in the bean factory.
- // 这个就是查询注解方式的 advisor
- advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
- return advisors;
- }
在 buildAspectJAdvisors 方法查询 advisor 的时候, 它大致可以分 3 个步骤.
从 Bean 工厂获取所有的 beanName String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
循环 beanNames, 判断 bean 是否包含 Aspect 注解
- for (String beanName : beanNames) {
- Class<?> beanType = this.beanFactory.getType(beanName);
- if (this.advisorFactory.isAspect(beanType)) {
- aspectNames.add(beanName);
- ......
- }
- }
获取 Advisors. 先拿到 Class 对象上的所有 Method 对象, 根据 Method 对象的 Annotation 类型, 返回不同的 Advice 对象. 这个跟 xml 方式返回的 Advice 对象是一样的.
- switch (aspectJAnnotation.getAnnotationType()) {
- case AtBefore:
- springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
- break;
- case AtAfter:
- springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
- break;
- case AtAfterReturning:
- springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
- AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
- if (StringUtils.hasText(afterReturningAnnotation.returning())) {
- springAdvice.setReturningName(afterReturningAnnotation.returning());
- }
- break;
- case AtAfterThrowing:
- springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
- AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
- if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
- springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
- }
- break;
- case AtAround:
- springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
- break;
- case AtPointcut:
- if (logger.isDebugEnabled()) {
- logger.debug("Processing pointcut'" + candidateAdviceMethod.getName() + "'");
- }
- return null;
- default:
- throw new UnsupportedOperationException(
- "Unsupported advice type on method" + candidateAdviceMethod);
- }
最后将 advice 和 pointcut 封装成 InstantiationModelAwarePointcutAdvisorImpl 对象返回. 返回之后, 创建代理类进行替换.
由此看来, 注解方式的 AOP, 是在查询 Advisors 的时候才去解析并生成的, 其他的与 xml 的配置方式处理流程都是一样的.
4, 总结
本章节我们重点阐述了 3 个问题. 即怎样产生代理类, 代理类的执行过程, AnnotationAOP 的处理流程. 结合本章节和上一章节的内容, 可能使我们对 Spring AOP 的内部处理流程加深了印象.
怎样产生代理类? 通过循环 beanNames 实例化 Bean 对象, 判断此对象是否与 pointcut 表达式匹配. 如果匹配就根据 advice 生成不同的 advisor 对象, 然后调用 JDK 或者 CGLIB 的方法生成代理类返回.
代理类执行 调用 JDK 或者 CGLIB 的 invoke 方法, 查询 advisor 的调用链. 链式调用, 根据通知类型调用不同的 advice 实现增强.
来源: https://juejin.im/post/5c7ba93ff265da2d89634b1c