文章目录
一、SpringAOP的总体流程
- 注册解析AOP的服务
- 解析和加载横切逻辑
- 将横切逻辑织入目标Bean中
二、为什么在启动类加上@EnableAspectJAutoProxy就能支持AOP
注解有两个属性
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
proxyTargetClass
- 如果为true,则将全部使用CGLIb进行代理;如果为false,则会尽可能使用JDK的动态代理,如果对象没有实现接口,将会使用CGLIb
exposeProxy
-
决定是否可以将动态代理对象暴露出来
-
如果设置为true将会放入AopContext类的ThreadLocal属性中的中,这样就可以通过容器获取到该动态代理的对象
SpringAOP的流程
- 注册解析AOP的服务
- 解析和加载横切逻辑
- 将横切逻辑织入目标Bean中
三、如何注册解析AOP的服务?
- 主要依靠@Import(AspectJAutoProxyRegistrar.class)注解
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//往容器注册一个AOP基于注解的自动代理创建器,负责动态代理的创建
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
//注册注解的两个信息
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
registerBeanDefinitions()方法有两个参数:
- AnnotationMetadata:保存注解相关的信息
- BeanDefinitionRegistry:往容器注册BeanDefinition
该方法做的事情:
- 往容器注册一个AOP基于注解的自动代理创建器,负责动态代理的创建
- 根据注解属性,注册的相关配置
什么时候将配置注册到容器中?
- 在容器初始化执行Refresh方法的时候,通过容器级别的后置处理器注册的
四、横切逻辑加载分析
1. TargetSource接口
- 实现该接口,可以实现池化的操作,将存取代理类或者替换代理类
SingletonTargetSource:确保被代理的实例是单例的,定义了一个final的Object类,确保每次调用的都是同一个实例
private final Object target;
@Override
public Class<?> getTargetClass() {
return this.target.getClass();
}
ProtoTypeTargetSource:确保被代理的实例是多例的,每次调用时都会返回一个新的实例
@Override
public Object getTarget() throws BeansException {
return newPrototypeInstance();
}
其他的还有
- 提供池化的CommonsPool2TargetSource,类似于线程池
- 提供热交换的HotSwappableTargetSource,可以增删改查代理实例
2. buildAspectJAdvisors方法
- 从容器中获取所有的beanName
- 遍历所有的beanName,解析出被@Aspect注解标记的类
- 提取Aspect类中的Advisors
- 将结果放入缓存
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
//用于保存切面的名称的集合
aspectNames = new ArrayList<>();
//获取所有的beanName
// AOP功能中在这里传入的是Object对象,代表去容器中获取到所有的组件的名称,然后再
// 进行遍历,这个过程是十分的消耗性能的,所以说Spring会再这里加入了保存切面信息的缓存。
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
//遍历我们从IOC容器中获取处的所有Bean的名称
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
//获取对应的bean的类型
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
//提取@Aspect注解标记的Class
if (this.advisorFactory.isAspect(beanType)) {
//是切面类
//加入到缓存中
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//Aspect里面的advice和pointcut被拆分成一个个的advisor,
// advisor里的advice和pointcut是1对1的关系
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
//单例则直接将Advisor类存到缓存
this.advisorsCache.put(beanName, classAdvisors);
}
else {
// 否则将其对应的工厂缓存
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
五、横切逻辑织入
在Bean完成初始化后,利用责任链模式进行横切逻辑的织入。
1. 横切逻辑的织入方法提供的有两个
- 第一个是正常流程的织入入口,通过后置处理器实现
- 第二个是循环依赖的织入入口,通过获取EarlyBeanReference实现
2. 流程
-
横切逻辑的织入前会进行一系列的判断
- 被织入的类名称不能是空
- 判断该类是否需要跳过织入或者是基础的类,比如实现了Advice、Pointcut等接口
-
获取合适的切面,利用AspectJ框架,进行类级别的初筛和方法级别的精确筛选,这些切面在容器创建的时候已经完成解析
- 初筛只能校验within的表达式,对于execution,只能检验精确到某个类的,表达式使用的包名加类名
-
创建Bean对应的代理,默认使用SingletonTargetSource用于封装实现类的信息,根据Bean是否实现了接口来决定使用JDK动态代理或者CGLib
-
将生成的动态代理,放入缓存中
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//已经被处理过
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//不需要被织入逻辑的
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//是不是基础的bean 是不是需要跳过的
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 返回匹配当前Bean的所有Advice\Advisor\Interceptor
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建Bean对应的代理,SingletonTargetSource用于封装实现类的信息
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
//该Bean是不需要进行代理的,下次就不需要重复生成了
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}