Spring AOP 引入——AOP 源码解析(六)
一、概述
到此为止,AOP的具体实现已经分析的差不多了,下面关注最后一个问题,动态代理是如何启用的,其实无非两种配置方式。
- 注解方式配置,使用@EnableAspectJAutoProxy启动AOP;
- xml方式配置,使用aop:aspectj-autoproxy/标签引入。
本文主要以注解配置的方式为例,分析一下,Spring如何启用AOP。其实也就是分析,@EnableAspectJAutoProxy是如何把AnnotationAwareAspectJAutoProxyCreator这个PostProcesser注册到BeanFactory中的。
二、@EnableAspectJAutoProxy
打开EnableAspectJAutoProxy可以看到一个Import注解,引入了AspectJAutoProxyRegistrar,这就是注册AOP所需要组件的注册类,实现了ImportBeanDefinitionRegistrar接口,用于灵活的注册组件。实现也跟简单,就是注册了AnnotationAwareAspectJAutoProxyCreator这个BeanPostProcessor。
分析路径 @EnableAspectJAutoProxy -> @Import(AspectJAutoProxyRegistrar.class) -> implements ImportBeanDefinitionRegistrar
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 在这里注册AnnotationAwareAspectJAutoProxyCreator,是一个BeanPostProcessor,用来增强Bean
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
// 这里处理了proxyTargetClass和exposeProxy属性
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
// 是否强制使用CGLIB代理
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// 是否要把代理对象暴露出来
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
源码出自:org.springframework.context.annotation.AspectJAutoProxyRegistrar.registerBeanDefinitions
2.1 注册AnnotationAwareAspectJAutoProxyCreator
注册AnnotationAwareAspectJAutoProxyCreator的过程还是比较简单的,调用了AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,最终调用了
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//已经存在,判断下优先级,如果参数cls类的优先级更高,则替换class
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
//没有冲突,则注册到BeanDefinitionRegistry中
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
从名字就能看出来,是注册或者增强(Escalate)AutoProxyCreator
- 如果不存在BeanName是‘org.springframework.aop.config.internalAutoProxyCreator’的beanDefinition,就注册这个bean
- 如果存在,获取这个beanDefinition下的class,与参数中的cls比较优先级,参数中的优先级更高,就替换这个beanDefinition的class;
优先级是写死的,AnnotationAwareAspectJAutoProxyCreator的优先级最高:
private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3);
static {
// Set up the escalation list...
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}
此处cls的参数就是AnnotationAwareAspectJAutoProxyCreator.class,优先级是最高的。
还有个比较有趣的细节是,这个BeanPostProcessor的优先级是最高的,注册的时候,设置了order是最高优先级,Integer.MIN_VALUE,也就是说,这个BeanPostProcessor中最先执行。
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
2.2 proxyTargetClass & exposeProxy
回顾下proxyTargetClass & exposeProxy这两个属性,这些之前都提到过,这里简单介绍一下。
- proxyTargetClass - AOP有两种实现,默认Spring自动选择,如果实现了接口,就使用JDK,否则是CGLIB,也可以强制指定使用CGLIB,可以在EnableAspectJAutoProxy注解中,设置proxyTargetClass=true。
- exposeProxy - 是否把当前代理对象,作为线程变量,暴露出来。就是解决被代理对象,内部调用的问题。暴露出来后,代理后的对象,会放置在AopContext的线程变量中,可以通过
((XXXService) AopContext.currentProxy()).another();
方式调用,解决内部调用无法代理的问题。
三、xml的方式引入
xml引入的方式与注解代码差不多,只是入口不同,xml的方式,需要引入aop:aspectj-autoproxy标签,通过查找AOP对应包下的spring.handlers,就可以逐步找到对应代码,最后也是调用AopNamespaceUtils的注册代码。
分析路径: spring.handlers -> AopNamespaceHandler -> AspectJAutoProxyBeanDefinitionParser -> AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary
这里不再详细对代码进行解释。
四、TargetSource
之前已经详细讨论过TargetSource这个东西了,在上一篇文章中,还留了个小尾巴,如何把TargetSourceCreator注册到AnnotationAwareAspectJAutoProxyCreator中(确切说是AbstractAutoProxyCreator),实现使用TargetSource扩展功能的目的。
本文我们分析了AnnotationAwareAspectJAutoProxyCreator是如何引入的。那么我们现在思考一下,该如何引入自定义的TargetSourceCreator。如果你对Spring IOC的扩展方式比较了解的话,那么可能会想到多种思路,我在这里抛砖引玉,提出一种使用BeanFactoryPostProcessor的方式,注入自定义的TargetSourceCreator。
BeanFactoryPostProcessor这个接口,有个postProcessBeanFactory方法,可以在beanFactory初始化完成后执行,我们可以利用这个扩展,修改bean的定义。我们就在这里,修改代表AnnotationAwareAspectJAutoProxyCreator的bean的定义。
4.1 LazyInitTargetSourceCreator
首先我们要有一个TargetSourceCreator,我们也不自定义了,直接使用spring已经给我们提供的一个LazyInitTargetSourceCreator,它解决什么问题呢?
假设你有两个bean,分别是AopService和AopDao,前者依赖后者。现在我们希望AopDao懒加载,那么可以使用@Lazy注解实现这个能力。但是这样操作后,你就会发现,行不通。因为AopService并不是懒加载的,它依赖AopDao,在AopService初始化时,也会把他的依赖,也就是AopDao实例化。
@Service
public class AopService {
@Resource
private AopDao aopDao;
public void doSomething(){
aopDao.selectAll();
aopDao.update("xxx");
}
}
@Repository
@Lazy
public class AopDaoImpl implements AopDao {
@Override
public List<String> selectAll() {
return Arrays.asList("hello", "world");
}
@Override
public int update(String s) {
System.out.println("update " + s);
return 1;
}
}
怎么办呢,就可以使用LazyInitTargetSourceCreator。他会把懒加载的对象,包装为TargetSource,只有在真正调用的时候,才会实例化。
protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(
Class<?> beanClass, String beanName) {
//如果是懒加载,则包装为TargetSource,那么这个bean,直到调用TargetSource的getTarget方法时,才会实例化
if (getBeanFactory() instanceof ConfigurableListableBeanFactory) {
BeanDefinition definition =
((ConfigurableListableBeanFactory) getBeanFactory()).getBeanDefinition(beanName);
if (definition.isLazyInit()) {
return new LazyInitTargetSource();
}
}
return null;
}
//代码出自:org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator
LazyInitTargetSource的懒加载实现,也很简单,就是初次调用才初始化。
@Override
@Nullable
public synchronized Object getTarget() throws BeansException {
if (this.target == null) {
this.target = getBeanFactory().getBean(getTargetBeanName());
postProcessTargetObject(this.target);
}
return this.target;
}
4.2 自定义BeanFactoryPostProcessor
有了TargetSourceCreator,下面我们按照思路,定义一个BeanFactoryPostProcessor,修改AnnotationAwareAspectJAutoProxyCreator的bean的定义。
@Component
public class AddCustomerTargetSourceBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
LazyInitTargetSourceCreator lazyInitTargetSourceCreator = new LazyInitTargetSourceCreator();
lazyInitTargetSourceCreator.setBeanFactory(beanFactory);
beanDefinition.getPropertyValues().add("customTargetSourceCreators", new TargetSourceCreator[]{lazyInitTargetSourceCreator});
}
}
好嘞,大功告成,有兴趣,你可以撸下代码,看下是否按照预期那样,AopDaoImpl在第一次执行方法时,才会被调用。
系列所有文章
转载自:https://juejin.cn/post/7368302351213445159