Spring AOP 自动代理组件:增强切面代理与容器搭建的关键
1. 前言
在前边几节内容中,我们介绍了 AOP 的三大核心,增强、切面和代理,此时已经可以实现一个完整的 AOP 的功能。但仔细想一下,代理对象需要一个个去创建,显得异常繁琐,因此我们需要把创建代理对象的流程自动化。具体思路是,依托 容器强大的管理能力,在 Bean 创建流程中检查对象是否需要被代理,从而实现自动创建代理的功能。更为重要的是,通过自动代理组件将 AOP 机制与 容器联系起来,这意味着 框架的两大基石就此得以确立。
2. 自动代理组件2.1 继承结构
类有两个分支, 及其子类实现了创建代理的功能,t 及其子类则将创建代理的功能整合到 体系之中。尤其是 ator 依赖 ,这说明自动代理的底层仍是通过手动地方式创建代理,手动代理是自动代理的基础。
2.2 ator
ator 作为自动代理的核心类,实现了 接口,拥有了对 Bean 进行处理的能力,同时将 AOP 功能与 IOC 容器联系在一起。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
private BeanFactory beanFactory;
private AdvisorAdapterRegistry advisorAdapterRegistry = new DefaultAdvisorAdapterRegistry();
private final Map
ator 实现了 接口及其父接口 的三个方法,它们都能用来创建代理对象,区别在于调用时机和用途。
三个方法的调用时机如下图所示,本节我们只讨论 方法的实现,也就是初始化后创建代理的流程,另外两种代理方式在下一节介绍。
3. 初始化后创建代理3.1 主流程
ator 实现了 接口的 方法,该方法在初始化之后执行。此时实例已创建,依赖关系已解析,完成度相当高,正是创建代理对象的最佳时机。 方法起到了辅助作用,首先检查 字段中是否缓存了当前对象,如果没有提前暴露代理对象,则调用 方法创建代理对象。
//所属类[cn.stimd.spring.aop.framework.AbstractAutoProxyCreator]
//初始化后回调
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean != null) {
Object cacheKey = StringUtils.hasLength(beanName) ? beanName : bean.getClass();
//检查是否提前创建代理,确保创建代理的操作只会执行一次
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return null;
}
方法是创建代理的核心流程,如果不需要被代理,则返回当前对象本身。 方法可以分为三步,最重要的是第二步,如下所示:
前置检查,如果当前对象已经处理过,或者本身是 AOP 相关的组件类,则不处理获取适用于当前对象的拦截器集合,如果不为空,说明需要被代理通过 完成代理对象的创建工作
//所属类[cn.stimd.spring.aop.framework.AbstractAutoProxyCreator]
//如有必要,创建代理对象
private Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//1. 前置检查
//1.1 如果目标Bean已经在缓存中,且不需要代理,直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//1.2 如果是AOP相关的组件类,则加入缓存并标记为不处理
Class> beanClass = bean.getClass();
if (isInfrastructureClass(beanClass)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
//2. 获取增强器(模板方法,由子类实现)
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != null) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 3. 创建代理对象
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
//不需要代理,加入缓存,标记为false
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
3.2 获取增强器集合
方法由子类 实现,类名说明了增强器的类型是 。具体的逻辑是由 方法完成的,可以分为三步:
从 容器中找出所有的 组件过滤适用于当前类的 集合对符合条件的 进行排序并返回
//所属类[cn.stimd.spring.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator]
//获取适用于指定类的所有Advisor集合
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class> beanClass, String beanName, TargetSource customTargetSource) {
List eligibleAdvisors = findEligibleAdvisors(beanClass);
if (eligibleAdvisors.isEmpty()) {
return null;
}
return eligibleAdvisors.toArray();
}
protected List findEligibleAdvisors(Class> beanClass) {
//1. 从BeanFactory寻找候选的Advisor集合
List candidateAdvisors = findCandidateAdvisors();
//2. 过滤出符合当前类的所有Advisor
List eligibleAdvisors = AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
//3. 排序
if (!eligibleAdvisors.isEmpty()) {
AnnotationAwareOrderComparator.sort(eligibleAdvisors);
}
return eligibleAdvisors;
}
第一步,寻找候选的 组件。首先找出 容器中所有 类型的单例,然后遍历这些组件,调用 n 方法判断 是否符合条件,如果符合条件,则将 组件添加到候选列表中。
//cn.stimd.spring.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator
//查找候选的Advisor
protected List findCandidateAdvisors(){
List advisorNames = getBeanFactory().getBeanNamesForType(Advisor.class);
List advisors = new LinkedList<>();
for (String name : advisorNames) {
if (isEligibleAdvisorBean(name)) {
//从容器中获取Advisor组件,并添加到候选列表中
advisors.add(getBeanFactory().getBean(name, Advisor.class));
}
}
return advisors;
}
子类 重写了 n 方法,判定逻辑有两个,一是 容器中存在 实例,二是单例的角色是 ,也就是框架内部使用。
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
@Override
protected boolean isEligibleAdvisorBean(String beanName) {
ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) getBeanFactory();
return beanFactory.containsBeanDefinition(beanName)
&& beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE;
}
}
第二步,过滤出符合指定类的 集合。 工具类的 pply 方法对所有候选的 进行检查,判断逻辑由 方法实现。
//所属类[cn.stimd.spring.aop.support.AopUtils]
//检索适用于指定类的Advisor列表
public static List findAdvisorsThatCanApply(List candidateAdvisors, Class> clazz) {
List eligibleAdvisors = new LinkedList<>();
for (Advisor candidate : candidateAdvisors) {
if (canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
类有两个重载的 方法,先来看第一个。 方法将 分为切点和引入两种类型分别处理。需要注意的是,如果引入或切点都匹配失败,则默认该 是符合条件的。为什么匹配失败还会认为是符合条件的?这是说,当 失去了切面的特性,就退化成了 组件,可以应用于所有的对象。
//所属类[cn.stimd.spring.aop.support.AopUtils]
//检查Advisor是否可以应用于指定类
private static boolean canApply(Advisor advisor, Class> targetClass) {
//IntroductionAdvisor略
//PointcutAdvisor
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pa = (PointcutAdvisor) advisor;
return canApply(pa.getPointcut(), targetClass);
}
//Advisor失去切面特性,默认适用于所有类
return true;
}
我们不关心引入,来看第二个 方法的实现,可以分为三步:
检查 是否匹配当前类,以及 是否默认适用所有方法。获取当前类实现的所有接口类型,再加上当前类自身,得到一个 Class 集合。在创建代理对象的时候,需要实现目标对象的接口。遍历这个集合中的所有方法,如果 匹配了至少一个方法,则认为 是符合条件的。(JDK 动态代理必须要定义接口,因此可以匹配接口或者实现类上的方法。对于 CGLIB 代理来说,只检查当前类自身的所有方法即可)
//所属类[cn.stimd.spring.aop.support.AopUtils]
//检查切点是否匹配指定的类
private static boolean canApply(Pointcut pointcut, Class> targetClass){
//1. 检查ClassFilter和MethodMatcher是否匹配
if (!pointcut.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pointcut.getMethodMatcher();
if(methodMatcher == MethodMatcher.TRUE){
return true;
}
//2. 获取当前类的所有接口类型
Set> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
//3. 遍历当前类及接口的所有方法,只要有一个符合条件,则认为PointcutAdvisor适用于当前类
for (Class> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if(methodMatcher.matches(method, targetClass)){
return true;
}
}
}
return false;
}

第三步,由于多个切面可以对同一个方法进行增强,因此有必要确定多个切面的执行顺序。sor 类实现了 接口,调用 的静态方法 sort 进行排序。
3.3 创建代理对象
在执行完 方法后,返回一个拦截器的集合,如果拦截器集合不为空,说明当前实例应当被代理。这一点非常重要,手动代理只解决了创建代理的问题,哪些对象有资格被代理才是自动的含义所在。
接下来是创建代理对象的工作,由 方法完成,实际上重复了手动代理的流程,前边已经详细介绍过了。需要注意的是,对于每个需要代理的对象,都创建了一个代理工厂的的实例。这一点也很好理解, 的父类 保存了目标对象和 集合等重要信息,这些内容对于每个代理来说都是唯一的。
//所属类[cn.stimd.spring.aop.framework.AbstractAutoProxyCreator]
//创建代理对象
protected Object createProxy(Class> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this); //将AopConfig复制一份给代理对象
//判断目标对象使用JDK代理还是Cglib代理
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
//将各种Advice转换为统一的Advisor
Advisor[] advisors = buildAdvisors(specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
//使用代理工厂创建代理
return proxyFactory.getProxy();
}
4. 配置自动代理组件
是 内置的自动代理组件,对于 AOP 功能来说该组件是标配,因此可以将注册和配置的逻辑固定下来方便使用。 工具类提供了两个静态方法,如下所示:
public class AopConfigUtils {
public static final String AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator";
//注册AutoProxyCreator组件类
public static void registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
RootBeanDefinition rbd = new RootBeanDefinition(InfrastructureAdvisorAutoProxyCreator.class);
rbd.getPropertyValues().addPropertyValue("order", Ordered.HIGHEST_PRECEDENCE);
rbd.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, rbd);
}
}
//强行将proxyTargetClass属性设置为true
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.TRUE);
}
}
}
注:在实际使用中,@xy 注解的作用是开启 AOP 功能,@ 注解的作用是开启事务,而事务建立在 AOP 功能的基础之上。这两个注解都是通过 来注册和配置 AOP 组件的。
5. 测试5.1 准备工作
本次测试需要的类比较多,首先是 、 和 三个类提供 AOP 功能的支持,其次是 注解类起到了标识的作用,最后是 作为创建代理的目标类。
是简单的注解类,声明在方法上,表示当前类需要被代理。
//测试类:日志注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Logger {}
实现了 接口,增强的逻辑是在执行目标方法前打印日志。
//测试类:日志拦截器
public class LoggerInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("日志拦截: " + invocation.getThis().getClass().getSimpleName() + " 方法:" + invocation.getMethod().getName());
return invocation.proceed();
}
}
继承了 ,说明对方法进行静态匹配。 方法实现了匹配逻辑,检查目标方法是否声明了 @ 注解。
//测试类:日志切点
public class LoggerPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, Class> targetClass) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(method,
Logger.class, false, false);
return attributes != null;
}
}
主要起到了整合的作用,使用 作为切点,使用 作为增强器。
//测试类:日志Advisor
public class LoggerAdvisor extends AbstractGenericPointcutAdvisor {
private Pointcut pointcut = new LoggerPointcut();
public LoggerAdvisor() {
//设置增强器为LoggerInterceptor
setAdvice(new LoggerInterceptor());
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
}
是普通对象,foo 方法声明了 @ 注解,bar 方法没有声明注解,作为对照组。
//测试类
public class LoggerTarget {
@Logger
public void foo(){
System.out.println("执行foo方法...");
}
public void bar() {
System.out.println("执行bar方法...");
}
}
5.2 自动代理
测试方法可以分为三步,第一步注册 ,需要指定角色为 ,否则无法识别。第二步注册自动代理的组件 。第三步注册目标对象,当调用 方法时,会回调 ator 的 方法,完成创建代理对象的工作。
//测试方法
@Test
public void testAutoproxy(){
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//注册Advisor
RootBeanDefinition definition = new RootBeanDefinition(LoggerAdvisor.class);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
factory.registerBeanDefinition("loggerAdvisor", definition);
//注册自动代理组件
InfrastructureAdvisorAutoProxyCreator processor = new InfrastructureAdvisorAutoProxyCreator();
processor.setBeanFactory(factory);
factory.addBeanPostProcessor(processor);
//注册目标对象,完成代理流程
factory.registerBeanDefinition("target", new RootBeanDefinition(LoggerTarget.class));
LoggerTarget target = factory.getBean("target", LoggerTarget.class);
target.foo();
target.bar();
}
从测试结果可以看到,foo 方法作为增强方法执行了日志拦截的操作,而 bar 方法仅作为普通方法调用。这说明代理对象创建成功,并且切面是起作用的。
日志拦截: LoggerTarget 方法:foo
执行foo方法...
执行bar方法...
6. 总结
AOP 代理是实现面向切面编程的基本单元,对于每个有增强需求的对象,都需要创建一个代理对象。问题在于创建代理对象是一个复杂的过程,即使通过代理工厂屏蔽了一部分细节,仍有相当多的工作需要手动完成。鉴于此, 提供了一种标准化的自动创建代理对象的机制,我们可以从三个方面来进行考量。
首先,为了参与对象的创建流程,必须与 建立关联,这一点是通过 接口提供的扩展性实现的。
其次, 组件封装了增强和切面,并对各种增强实现进行统一地适配,其设计的目的就是作为独立实体被 容器管理。此时, 容器既有增强组件,又有普通对象,且两者之间是多对多的关系。一个对象可以对应多个 ,一个 也可以对应多个对象。也就是说,自动代理的主要工作就是为指定对象寻找符合条件的 集合。
第三, 提供了若干自动代理组件,最重要的是 ator,该类定义了三个创建代理的方法,这些方法的调用时机和用途均不相同。本节只讨论了 方法,调用时机是在初始化之后,这是创建代理最主要的途径。
7. 项目信息
新增修改一览,新增(12),修改(1)。
aop
└─ src
├─ main
│ └─ java
│ └─ cn.stimd.springwheel.aop
│ ├─ config
│ │ └─ AopConfigUtils.java (+)
│ ├─ framework
│ │ ├─ autoproxy
│ │ │ ├─ AbstractAdvisorAutoProxyCreator.java(+)
│ │ │ └─ InfrastructureAdvisorAutoProxyCreator.java(+)
│ │ ├─ AbstractAutoProxyCreator.java (+)
│ │ ├─ AopInfrastructureBean.java (+)
│ │ └─ ProxyProcessorSupport.java (+)
│ └─ support
│ └─ AopUtils.java (+)
└─ aop.test
└─ proxy
├─ autoproxy
│ ├─Logger.java(+)
│ ├─LoggerAdvisor.java(+)
│ ├─LoggerInterceptor.java(+)
│ ├─LoggerPointcut.java(+)
│ └─LoggerTarget.java(+)
└─ ProxyTest.java (*)
注:+号表示新增、*表示修改
注:项目的 分支会跟随教程的进度不断更新,如果想查看某一节的代码,请选择对应小节的分支代码。
























