1. 前言
在前边几节内容中,我们介绍了 Spring AOP 的三大核心,增强、切面和代理,此时已经可以实现一个完整的 AOP 的功能。但仔细想一下,代理对象需要一个个去创建,显得异常繁琐,因此我们需要把创建代理对象的流程自动化。具体思路是,依托 Spring 容器强大的管理能力,在 Bean 创建流程中检查对象是否需要被代理,从而实现自动创建代理的功能。更为重要的是,通过自动代理组件将 AOP 机制与 Spring 容器联系起来,这意味着 Spring 框架的两大基石就此得以确立。
2. 自动代理组件
2.1 继承结构
ProxyConfig
类有两个分支,AdvisedSupport
及其子类实现了创建代理的功能,ProxyProcessorSupport
及其子类则将创建代理的功能整合到 BeanFactory
体系之中。尤其是 AbstractAutoProxyCreator
依赖 ProxyFactory
,这说明自动代理的底层仍是通过手动地方式创建代理,手动代理是自动代理的基础。
-
AopInfrastructureBean
:标记接口,表示一个 AOP 组件类,自身不应该被代理。 -
ProxyProcessorSupport
:提供辅助功能,供子类使用。 -
AbstractAutoProxyCreator
:自动创建代理的核心类,最大的特点是通过ProxyFactory
来创建代理对象。 -
AbstractAdvisorAutoProxyCreator
:使用 Spring 容器中已有的Advisor
实例构建拦截器链。 -
InfrastructureAdvisorAutoProxyCreator
:排除自定义的Advisor
,使用 Spring 内置的组件。
2.2 AbstractAutoProxyCreator
整理了这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处】即可免费获取
AbstractAutoProxyCreator
作为自动代理的核心类,实现了 InstantiationAwareBeanPostProcessor
接口,拥有了对 Bean 进行处理的能力,同时将 AOP 功能与 IOC 容器联系在一起。
advisedBeans
:缓存所有已处理的 Bean,确保每个对象只被处理一次earlyProxyReferences
:缓存提前创建的代理对象,解决了代理对象的循环依赖问题
java
代码解读
复制代码
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements InstantiationAwareBeanPostProcessor, BeanFactoryAware { private BeanFactory beanFactory; private AdvisorAdapterRegistry advisorAdapterRegistry = new DefaultAdvisorAdapterRegistry(); private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256); private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16); }
AbstractAutoProxyCreator
实现了 InstantiationAwareBeanPostProcessor
接口及其父接口 BeanPostProcessor
的三个方法,它们都能用来创建代理对象,区别在于调用时机和用途。
-
postProcessAfterInitialization
方法在初始化之后执行,是创建代理对象的主要方式。 -
getEarlyBeanReference
方法在填充对象之前执行,专门负责处理代理对象的循环依赖。 -
postProcessBeforeInstantiation
方法在实例化之前执行,是一种很特殊的代理方式。
三个方法的调用时机如下图所示,本节我们只讨论 postProcessBeforeInstantiation
方法的实现,也就是初始化后创建代理的流程,另外两种代理方式在下一节介绍。
3. 初始化后创建代理
3.1 主流程
AbstractAutoProxyCreator
实现了 BeanPostProcessor
接口的 postProcessAfterInitialization
方法,该方法在初始化之后执行。此时实例已创建,依赖关系已解析,完成度相当高,正是创建代理对象的最佳时机。postProcessAfterInitialization
方法起到了辅助作用,首先检查 earlyProxyReferences
字段中是否缓存了当前对象,如果没有提前暴露代理对象,则调用 wrapIfNecessary
方法创建代理对象。
java
代码解读
复制代码
//所属类[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; }
wrapIfNecessary
方法是创建代理的核心流程,如果不需要被代理,则返回当前对象本身。wrapIfNecessary
方法可以分为三步,最重要的是第二步,如下所示:
- 前置检查,如果当前对象已经处理过,或者本身是 AOP 相关的组件类,则不处理
- 获取适用于当前对象的拦截器集合,如果不为空,说明需要被代理
- 通过
ProxyFactory
完成代理对象的创建工作
java
代码解读
复制代码
//所属类[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 获取增强器集合
getAdvicesAndAdvisorsForBean
方法由子类 AbstractAdvisorAutoProxyCreator
实现,类名说明了增强器的类型是 Advisor
。具体的逻辑是由 findEligibleAdvisors
方法完成的,可以分为三步:
- 从 Spring 容器中找出所有的
Advisor
组件 - 过滤适用于当前类的
Advisor
集合 - 对符合条件的
Advisor
进行排序并返回
java
代码解读
复制代码
//所属类[cn.stimd.spring.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator] //获取适用于指定类的所有Advisor集合 @Override protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) { List<Advisor> eligibleAdvisors = findEligibleAdvisors(beanClass); if (eligibleAdvisors.isEmpty()) { return null; } return eligibleAdvisors.toArray(); } protected List<Advisor> findEligibleAdvisors(Class<?> beanClass) { //1. 从BeanFactory寻找候选的Advisor集合 List<Advisor> candidateAdvisors = findCandidateAdvisors(); //2. 过滤出符合当前类的所有Advisor List<Advisor> eligibleAdvisors = AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); //3. 排序 if (!eligibleAdvisors.isEmpty()) { AnnotationAwareOrderComparator.sort(eligibleAdvisors); } return eligibleAdvisors; }
第一步,寻找候选的 Advisor
组件。首先找出 Spring 容器中所有 Advisor
类型的单例,然后遍历这些组件,调用 isEligibleAdvisorBean
方法判断 Advisor
是否符合条件,如果符合条件,则将 Advisor
组件添加到候选列表中。
java
代码解读
复制代码
//cn.stimd.spring.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator //查找候选的Advisor protected List<Advisor> findCandidateAdvisors(){ List<String> advisorNames = getBeanFactory().getBeanNamesForType(Advisor.class); List<Advisor> advisors = new LinkedList<>(); for (String name : advisorNames) { if (isEligibleAdvisorBean(name)) { //从容器中获取Advisor组件,并添加到候选列表中 advisors.add(getBeanFactory().getBean(name, Advisor.class)); } } return advisors; }
子类 InfrastructureAdvisorAutoProxyCreator
重写了 isEligibleAdvisorBean
方法,判定逻辑有两个,一是 Spring 容器中存在 Advisor
实例,二是单例的角色是 ROLE_INFRASTRUCTURE
,也就是框架内部使用。
java
代码解读
复制代码
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; } }
第二步,过滤出符合指定类的 Advisor
集合。AopUtils
工具类的 findAdvisorsThatCanApply
方法对所有候选的 Advisor
进行检查,判断逻辑由 canApply
方法实现。
java
代码解读
复制代码
//所属类[cn.stimd.spring.aop.support.AopUtils] //检索适用于指定类的Advisor列表 public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { List<Advisor> eligibleAdvisors = new LinkedList<>(); for (Advisor candidate : candidateAdvisors) { if (canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }
AopUtils
类有两个重载的 canApply
方法,先来看第一个。canApply
方法将 Advisor
分为切点和引入两种类型分别处理。需要注意的是,如果引入或切点都匹配失败,则默认该 Advisor
是符合条件的。为什么匹配失败还会认为是符合条件的?这是说,当 Advisor
失去了切面的特性,就退化成了 Advice
组件,可以应用于所有的对象。
java
代码解读
复制代码
//所属类[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; }
我们不关心引入,来看第二个 canApply
方法的实现,可以分为三步:
- 检查
ClassFliter
是否匹配当前类,以及MethodMatcher
是否默认适用所有方法。 - 获取当前类实现的所有接口类型,再加上当前类自身,得到一个
Class
集合。在创建代理对象的时候,需要实现目标对象的接口。 - 遍历这个集合中的所有方法,如果
MethodMatcher
匹配了至少一个方法,则认为Advisor
是符合条件的。(JDK 动态代理必须要定义接口,因此可以匹配接口或者实现类上的方法。对于 CGLIB 代理来说,只检查当前类自身的所有方法即可)
java
代码解读
复制代码
//所属类[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<Class<?>> 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; }
第三步,由于多个切面可以对同一个方法进行增强,因此有必要确定多个切面的执行顺序。AbstractPointcutAdvisor
类实现了 Ordered
接口,调用 AnnotationAwareOrderComparator
的静态方法 sort
进行排序。
3.3 创建代理对象
在执行完 getAdvicesAndAdvisorsForBean
方法后,返回一个拦截器的集合,如果拦截器集合不为空,说明当前实例应当被代理。这一点非常重要,手动代理只解决了创建代理的问题,哪些对象有资格被代理才是自动的含义所在。
接下来是创建代理对象的工作,由 createProxy
方法完成,实际上重复了手动代理的流程,前边已经详细介绍过了。需要注意的是,对于每个需要代理的对象,都创建了一个代理工厂的的实例。这一点也很好理解,ProxyFactory
的父类 AdvisedSupport
保存了目标对象和 Advisor
集合等重要信息,这些内容对于每个代理来说都是唯一的。
java
代码解读
复制代码
//所属类[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. 配置自动代理组件
InfrastructureAdvisorAutoProxyCreator
是 Spring 内置的自动代理组件,对于 AOP 功能来说该组件是标配,因此可以将注册和配置的逻辑固定下来方便使用。AopConfigUtils
工具类提供了两个静态方法,如下所示:
registerAutoProxyCreatorIfNecessary
方法:注册InfrastructureAdvisorAutoProxyCreator
组件forceAutoProxyCreatorToUseClassProxying
方法:将proxyTargetClass
属性设置为 true,也就是通过 CGLIB 框架来创建代理
java
代码解读
复制代码
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); } } }
注:在实际使用中,
@EnableAspectJAutoProxy
注解的作用是开启 AOP 功能,@EnableTransactionManagement
注解的作用是开启事务,而事务建立在 AOP 功能的基础之上。这两个注解都是通过AopConfigUtils
来注册和配置 AOP 组件的。
5. 测试
5.1 准备工作
本次测试需要的类比较多,首先是 LoggerInterceptor
、LoggerPointcut
和 LoggerAdvisor
三个类提供 AOP 功能的支持,其次是 Logger
注解类起到了标识的作用,最后是 LoggerTarget
作为创建代理的目标类。
Logger
是简单的注解类,声明在方法上,表示当前类需要被代理。
java
代码解读
复制代码
//测试类:日志注解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Logger {}
LoggerInterceptor
实现了 MethodInterceptor
接口,增强的逻辑是在执行目标方法前打印日志。
java
代码解读
复制代码
//测试类:日志拦截器 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(); } }
LoggerPointcut
继承了 StaticMethodMatcherPointcut
,说明对方法进行静态匹配。matches
方法实现了匹配逻辑,检查目标方法是否声明了 @Logger
注解。
java
代码解读
复制代码
//测试类:日志切点 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; } }
LoggerAdvisor
主要起到了整合的作用,使用 LoggerPointcut
作为切点,使用 LoggerInterceptor
作为增强器。
java
代码解读
复制代码
//测试类:日志Advisor public class LoggerAdvisor extends AbstractGenericPointcutAdvisor { private Pointcut pointcut = new LoggerPointcut(); public LoggerAdvisor() { //设置增强器为LoggerInterceptor setAdvice(new LoggerInterceptor()); } @Override public Pointcut getPointcut() { return this.pointcut; } }
LoggerTarget
是普通对象,foo
方法声明了 @Logger
注解,bar
方法没有声明注解,作为对照组。
java
代码解读
复制代码
//测试类 public class LoggerTarget { @Logger public void foo(){ System.out.println("执行foo方法..."); } public void bar() { System.out.println("执行bar方法..."); } }
5.2 自动代理
测试方法可以分为三步,第一步注册 Advisor
,需要指定角色为 ROLE_INFRASTRUCTURE
,否则无法识别。第二步注册自动代理的组件 InfrastructureAdvisorAutoProxyCreator
。第三步注册目标对象,当调用 getBean
方法时,会回调 AbstractAutoProxyCreator
的 postProcessAfterInitialization
方法,完成创建代理对象的工作。
java
代码解读
复制代码
//测试方法 @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
方法仅作为普通方法调用。这说明代理对象创建成功,并且切面是起作用的。
erlang
代码解读
复制代码
日志拦截: LoggerTarget 方法:foo 执行foo方法... 执行bar方法...
6. 总结
Spring AOP 代理是实现面向切面编程的基本单元,对于每个有增强需求的对象,都需要创建一个代理对象。问题在于创建代理对象是一个复杂的过程,即使通过代理工厂屏蔽了一部分细节,仍有相当多的工作需要手动完成。鉴于此,Spring 提供了一种标准化的自动创建代理对象的机制,我们可以从三个方面来进行考量。
首先,为了参与对象的创建流程,必须与 BeanFactory
建立关联,这一点是通过 BeanPostProcessor
接口提供的扩展性实现的。
其次,Advisor
组件封装了增强和切面,并对各种增强实现进行统一地适配,其设计的目的就是作为独立实体被 Spring 容器管理。此时,Spring 容器既有增强组件,又有普通对象,且两者之间是多对多的关系。一个对象可以对应多个 Advisor
,一个 Advisor
也可以对应多个对象。也就是说,自动代理的主要工作就是为指定对象寻找符合条件的 Advisor
集合。
第三,Spring 提供了若干自动代理组件,最重要的是 AbstractAutoProxyCreator
,该类定义了三个创建代理的方法,这些方法的调用时机和用途均不相同。本节只讨论了 postProcessAfterInitialization
方法,调用时机是在初始化之后,这是创建代理最主要的途径。
7. 项目信息
新增修改一览,新增(12),修改(1)。
scss
代码解读
复制代码
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 (*) 注:+号表示新增、*表示修改