前面讲到了使用XML 配置ProxyFactoryBean,结合注解配置@Bean也可以实现代码配置。还讲到了代码配置ProxyFactory。前者与Spring IOC 容器结合的更紧密,不需要自己调用getProxy()方法,从IOC 容器中获取的Bean 已经是最终的代理对象。

这两种方式需要我们自己设置目标对象,通知以及代理接口,使用起来还是比较繁琐的。每当我们需要一个代理对象时,就需要配置ProxyFactoryBeanProxyFactory

有没有更好的方式,只需要做简单的配置就可以为多个对象生成代理对象呢?

我们已经有了创建代理的方式,就是前面介绍的工厂类,剩下的工作只需要解决如何配置和如何解析配置的问题就可以了。

下面介绍最核心的类。

AbstractAutoProxyCreator

看看Spring 官方介绍:

该抽象类实现了BeanPostProcessor接口,用AOP代理来包装每个合适的bean,并在调用bean本身之前委托给指定的拦截器。这个类区分了“公共”拦截器和“特定”拦截器,前者用于它创建的所有代理,后者用于每个bean实例。可以不需要任何通用的拦截器。如果有,则可以使用interceptorNames属性设置它们。与org.springframework.aop.framework.ProxyFactoryBean 一样,使用拦截器名称而不是bean引用来正确处理原型顾问和拦截器:例如,支持有状态的混合。interceptorNames属性支持任何通知类型。

如果有大量的bean需要用类似的代理(即委托给相同的拦截器)来包装,那么这种自动代理特别有用。可以在bean工厂中注册一个这样的后处理程序,而不是为x个目标bean进行x个重复的代理定义,来达到相同的效果。

子类可以应用任何策略来决定一个bean是否被代理,例如通过类型、名称、bean定义细节等。它们还可以返回额外的拦截器,这些拦截器应该只应用于特定的bean实例。BeanNameAutoProxyCreator是一个简单的实现类,它通过指定名称识别要代理的bean。

可以使用任意数量的TargetSourceCreator实现来创建自定义目标源:例如,来共享原型对象。只要TargetSourceCreator指定了自定义TargetSource,即使没有通知,也会发生自动代理。如果没有设置TargetSourceCreator,或者没有匹配上,那么默认情况下将使用 SingletonTargetSource来包装目标bean实例.

从上面的描述中,可以看出此类实现了BeanPostProcessor接口,拦截bean的创建过程。并提供了自定义获取目标对象的方式,以及识别通知,创建代理对象的核心逻辑。子类提供了多种更具体的创建代理的策略。

类图

AbstractAutoProxyCreator

从上图中可以看到此类实现了SmartInstantiationAwareBeanPostProcessor接口,该接口在前面Bean的初始化中讲到过,如果postProcessBeforeInstantiation方法返回了非null对象,则将会打断原bean的初始化过程,从而使用该方法返回的对象。如果上面的方法返回null,那么将走常规初始化对象方式,初始化对象之后将调用postProcessAfterInitialization方法,在此方法中可以根据需要返回代理对象。

我们猜想一下如何创建?

  1. 判断是否需要创建
  2. 判断之前是否创建过
  3. 找到目标对象所有匹配的通知
  4. 有了通知,剩下的就是通过之前的创建代理的方式来创建。

postProcessBeforeInstantiation 方法

此方法将会在进行常规化实例bean时执行,如果不需要阻断初始化流程,则需要返回null。在这个方法实现中提供了一个自定义目标对象获取方式的机会,即TargetSourceCreator,如果注册了该接口的实现类并且返回非null目标对象,那么将在postProcessBeforeInstantiation方法中创建代理对象。如果没有注册或者注册的TargetSourceCreator实现都返回null,那么不会在postProcessBeforeInstantiation方法中创建代理对象,走常规初始化流程。初始化bean之后还有机会再次修改bean实例。

默认直接从Spring 工厂中拿对应的bean实例,并不会在此处实现自定义TargetSourceCreator接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !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;
}
}
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
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;
}

postProcessAfterInitialization 方法

在前面的方法中如果返回了null,那么将会继续初始化bean。初始化之后将调用BeanPostProcessor.postProcessAfterInitialization方法。AbstractAutoProxyCreator将在此方法中创建代理对象。这里有个小地方要注意,因为在实例化的过程中,如果是要提前暴露出来的bean,将会提前加入到singletonFactories map中,在获取这个bean的时候将会调用getEarlyBeanReference方法,也就是说可能需要在这个方法中创建代理对象,所以如果已经在getEarlyBeanReference方法中创建了,那么在postProcessAfterInitialization方法中就不需要再创建一次了。

1
2
3
4
5
6
7
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;

getEarlyBeanReference 方法

在提前暴露bean时会调用此方法。

1
2
3
4
5
6
7
8
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
this.earlyProxyReferences.add(cacheKey);
}
return wrapIfNecessary(bean, beanName, cacheKey);
}

判断是否需要创建

看过了上面3个入口的代码,主要用isInfrastructureClass(beanClass) shouldSkip(beanClass, beanName)两个方法来判断。

isInfrastructureClass用来判断该类是不是基础类,包括AdvicePointcutAdvisorAopInfrastructureBean 这些aop基础接口实现类,这些类的对象不应该被代理。

shouldSkip(beanClass, beanName) 用来实现自定义的跳过逻辑,子类可以重写该方法。默认判断实例类是不是以.ORIGINAL结尾,是则跳过,反之则不跳过。

判断之前是否创建过

通过earlyProxyReferences Set 将 提前暴露出来的bean缓存起来,advisedBeans Map用来存放经过历增强过程的Bean,已经增强过的对应的valueTrue,不需要增强的则为FalsetargetSourcedBeans Set用来将自定义TargetSource的bean缓存起来。

  1. 在调用postProcessBeforeInstantiation方法时,如果没有传bean name,或者targetSourcedBeans不包含bean name,那么如果advisedBeans包含此缓存key(缓存key是以bean的Class和name来组成的,如果没有name则只使用class),则代表之前已经处理过这种class或者bean name,直接返回null

    如果在此方法中创建了代理,则会将bean name 加入到targetSourcedBeans

  2. 在调用getEarlyBeanReference方法时,如果earlyProxyReferences Set中没有包含此缓存key,则将此缓存key加入到earlyProxyReferences。如果bean name不为空并且targetSourcedBeans包含bean name,则意味着在第一中情况中已经创建了代理,直接返回该bean。

    如果advisedBeans中缓存key对应的值为False,则意味着这个bean不需要创建代理,直接返回此bean。

  3. 在调用postProcessAfterInitialization方法时,如果earlyProxyReferences Set中包含此缓存key,则意味着在第二种情况中已经创建了代理,直接返回此bean即可。

    如果bean name不为空并且targetSourcedBeans包含bean name,则意味着在第一种情况中已经创建了代理,直接返回该bean。

    如果advisedBeans中缓存key对应的值为False,则意味着这个bean不需要创建代理,直接返回此bean。

由于有三个入口可以用来创建代理对象,因此要保证只创建一次,所以用了上面的方式。

下面看一下完整的流程图:

AbstractAutoProxyCreator创建代理对象流程图

getAdvicesAndAdvisorsForBean 方法

此方法用来查找当前bean匹配的通知,是一个抽象方法,需要子类去实现具体的查找逻辑。

查到到之后将通知的拦截器转换为Spring 的Advisor,这个转换过程中,可以预先配置通用的拦截器,可以在每个bean中生效,

具体的转换方法是org.springframework.aop.framework.adapter.AdvisorAdapterRegistry#wrap()

子类 BeanNameAutoProxyCreator

通过配置bean 名称过滤需要代理的bean并为其创建代理。

该类只能通过interceptorNames属性指定应用于所有匹配bean的拦截器名称,设置beanNames来配置要代理的bean,可能指定通配符*,如果需要为FactoryBean对象创建代理,需要加上&标记。

如果bean能匹配上,则getAdvicesAndAdvisorsForBean将返回PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS,这是一个空数组,也就是说这个方法将不会返回额外的拦截器,但是会创建代理对象。

子类 AbstractAdvisorAutoProxyCreator

通用的自动代理创建器,基于每个bean检测到的advisor为特定bean构建AOP代理。
子类必须实现抽象findCandidateAdvisors()方法,以返回应用于任何对象的顾问列表。子类还可以重写继承的shouldSkip方法,以从自动代理中排除某些对象。
需要排序的通知应该实现org.springframework.core.Ordered接口。该类按Orderedorder值对通知进行排序,未实现Ordered接口的通知将被认为是无序的,它们将以未定义的顺序出现在advisor链的末尾。

这个类实现了getAdvicesAndAdvisorsForBean方法,并提供了新的模板方法供子类去实现。主要是findCandidateAdvisors方法用来查找所有候选的AdvisorfindAdvisorsThatCanApply方法用来筛选能匹配的AdvisorextendAdvisors方法用来注册额外的AdvisorsortAdvisors方法用来对Advisor进行排序。

findCandidateAdvisors方法

该方法用来查找Advisor实现类,默认使用工具类BeanFactoryAdvisorRetrievalHelper,最重要的一行代码是

1
2
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);

在beanFactory中查找所有Advisor实现类的Bean名称,然后再循环调用beanFactory的getBean()方法初始化advisor。

并定义了volatile修饰的cachedAdvisorBeanNames变量将找到的Advisor bean缓存起来,以便下次直接使用。

findAdvisorsThatCanApply方法

此方法用于匹配当前目标对象和上一步中找到的Advisor。

先使用线程变量将当前bean暴露出来。

1
ProxyCreationContext.setCurrentProxiedBeanName(beanName)

再使用AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass)方法返回匹配的Advisor。

一些通用的方法都抽出放到了AopUtils这个工具类中。

在Spring AOP 中可以分为两种Advisor:IntroductionAdvisorPointcutAdvisor。一种用来增强类(为对象引入新的接口),另一种用来增强具体的方法。

匹配流程:advisor匹配过程

从上面的流程图中可以看出,主要的步骤是遍历Advisor,再调用IntroductionAdvisor的ClassFilter的matches方法或者PointcutAdvisor的MethodMatcher的match方法,前者针对class级别的增强,后者为方法级别的增强。

extendAdvisors方法

在上一步中已经找到了匹配的advisor,此方法用于子类去扩展,添加其他的advisor。

sortAdvisors方法

根据@Order注解或者Ordered接口将advisor排序。

子类 AspectJAwareAdvisorAutoProxyCreator

AbstractAdvisorAutoProxyCreator的子类,暴露出AspectJ的调用上下文,并解析当多个通知来自同一切面时AspectJ的通知优先级规则。

sortAdvisors 方法

AspectJ优先级对通知进行排序。如果两个通知来自同一个Aspect,它们的顺序也会相同。来自同一Aspect的通知将根据以下规则进一步排序:
如果这一对中的任何一个是 after advice,那么最后声明的通知优先级最高(最后运行)。否则,先声明的通知优先级最高(先运行)。
重要提示: advisor按优先级排序,从优先级最高到最低。在连接点之前,优先级最高的advisor先运行。在连接点之后,优先级最高的顾问最后运行。

extendAdvisors 方法

添加ExposeInvocationInterceptor 到通知链开头。它将当前MethodInvocation对象公开为线程变量。我们偶尔需要这样的功能,例如,当切入点(例如AspectJ表达式切入点)需要知道完整的调用上下文时。除非真的有这个必要,一般不需要使用这个拦截器。

子类 AnnotationAwareAspectJAutoProxyCreator

继承自AspectJAwareAdvisorAutoProxyCreator,用来处理当前应用程序上下文中所有AspectJ注解声明的Aspect,以及Spring Advisor

如果使用了<aop:include>元素,只有名称与正则匹配的@AspectJ bean 才会用于AOP。

findCandidateAdvisors 方法

此方法先调用父类的方法处理常规的Advisor,以此支持上面提到过的AOP声明方式。

再由BeanFactoryAspectJAdvisorsBuilder获取通过AspecJ注解声明的Advisor。