AspectJ的开启
在Spring项目中通过AspectJ来使用aop时,需要在配置文件中做如下配置
<aop:aspectj-autoproxy/>
Spring对AspectJ标签的解析
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
* and '{@code scoped-proxy}' tags.
*/
@Override
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
可以看到Spring对aspectj标签的解析是通过AspectJAutoProxyBeanDefinitionParser这个BeanDefinitionParser来完成的.
进入该类的parse方法,可以看到如下调用链
一直跟进到最内层的方法查看代码逻辑
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
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;
}
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;
}
这里便是AspectJAutoProxyBeanDefinitionParser完成的最主要的功能,即想容器注册一个内部BeanDefinition,该BeanDefinition的Class类型为AnnotationAwareAspectJAutoProxyCreator.class,这点从该函数调用层级的上一层传进来的参数可以得到.
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
至此,完成了aspectj标签的解析工作,注意,这里上述分析只是提到了解析这部分的核心部分,其他一些细节点就不再具体分析了.完成了解析工作只是为aspectj实现的aop做好了准备工作,下面在分析一下aop是如何运转起来的.
AspectJ下aop的工作机制
通过前面的分析,发现AspectJAutoProxyBeanDefinitionParser注册了一个AnnotationAwareAspectJAutoProxyCreator类型的内部BeanDefinition,先来看一下这个类的继承体系图
可以看到AnnotationAwareAspectJAutoProxyCreator是BeanPostProcessor接口的一个实现类,那么它必然会实现了该接口中的postProcessAfterInitialization这个方法,而这里就是aop运行起来的核心起点.
AnnotationAwareAspectJAutoProxyCreator类中的postProcessAfterInitialization方法继承自父类AbstractAutoProxyCreator,跟进方法可以看到其核心逻辑为wrapIfNecessary方法.来看下代码实现.
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
这里可以看到其核心思想就如下:
- 获取当前bean所适用的拦截器列表
- 如果拦截器列表为空,说明当前bean不需要进行aop增强,那么直接返回原始bean;
- 如果拦截器列表不为空,说明当前bean需要进行aop增强,那么为它生成代理对象返回,生成代理对象的方式无非就是jdk proxy或者是cglib proxy两种方式,根据bean的情况具体判断.
所以,这里的核心实现就是拦截器列表的获取,即getAdvicesAndAdvisorsForBean方法.继续跟进代码:
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 关键点1
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 关键点2
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
上面代码中关键点1处方法findCandidateAdvisors的作用是获取所有的通知器.关键点2处方法findAdvisorsThatCanApply的作用是从通知器全量中找出适用于当前bean的.
先看一下findCandidateAdvisors方法内部实现
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
// 这里是加载Spring中所有符合父类规则的通知器
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
// 这里添加通过AspectJ注解配置的通知器,内部实现包括了对@After @Before @Aroud等的解析工作
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
这个方法中的两个部分所完成的功能如注释所述,就不再贴代码.
findAdvisorsThatCanApply方法的大体逻辑就是判断bean对应的class中的各方法是否有和通知器中的切点有匹配的,若匹配则记录到一个可用列表中,若不匹配则过滤掉,然后返回该可用列表.这里需要注意的是该class中的方法只要有一个与切点匹配,即认为匹配成功,并不是要求所有方法都匹配.
至此,变获得了最开始处wrapIfNecessary方法中需要的拦截器列表.前面的分析只描述的核心流程,其他一些细节点,比如对拦截器链的排序,一些缓存的使用以提高性能避免重复劳动还有一些扩展点就不再具体分析了.
后面便是根据拦截器列表生成所需的代理实例了,后面对原始bean中方法的调用会转为对proxy对象中方法的调用,就是我们熟知的jdk proxy或者cglib proxy那一套原理了,当然具体的代理调用的实现细节会有不同,Spring做了自己的一些处理,但是核心思想和根本原理是一致的,此处就就不再做过多分析了.