Aop是面向接口的,也便是面向方法的,实现是在IOC的基础上,Aop能够拦截指定的方法而且对方法加强,并且无需侵入到业务代码中,使业务与非业务处理逻辑分离,好比Spring的事务,经过事务的注解配置,Spring会自动在业务方法中开启、提交业务,而且在业务处理失败时,执行相应的回滚策略,aop的实现主要包括了两个部分:java
匹配符合条件的方法(Pointcut)spring
对匹配的方法加强(JDK代理cglib代理)shell
spring针对xml配置和配置自动代理的Advisor有很大的处理差异,在IOC中主要是基于XML配置分析的,在AOP的源码解读中,则主要从自动代理的方式解析,分析完注解的方式,再分析基于xml的方式。express
下面是spring aop的用法 也是用于源码分析的案例缓存
切面类:TracesRecordAdvisor
bash
@Aspect
@Component
public class TracesRecordAdvisor {
@Pointcut("execution(* spring.action.expend.aop.services.*.*(..))")
public void expression() {
}
@Before("expression()")
public void beforePrint() {
System.out.println("进入服务,记录日志开始....");
}
@AfterReturning("expression()")
public void afterPrint() {
System.out.println("退出服务,记录日志退出.....");
}
}
复制代码
xml配置: aop的注解启用只须要在xml中配置这段代码便可,这个是做为入口app
<aop:aspectj-autoproxy/>
复制代码
服务类:PayServiceImpl
使用jdk代理 因此要有一个接口ide
@Service
public class PayServiceImpl implements PayService {
public void payMoneyService() {
System.out.println("付款服务正在进行...");
}
}
复制代码
测试方法:函数
@Test
public void springAopTestService() {
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-aop.xml");
PayService payService= (PayService) applicationContext.getBean("payServiceImpl");
payService.payMoneyService();
}
复制代码
执行结果:源码分析
进入服务,记录日志开始....
付款服务正在进行...
退出服务,记录日志退出.....
复制代码
从上面的执行结果看,payMoneyService方法的确是被加强了。
在读spring源码时,我想首先来看下BeanFactoryPostProcessor
和BeanPostProcess
,这两个接口都是在spring经过配置文件或者xml获取bean声明生成完BeanDefinition
后容许咱们对生成BeanDefinition
进行再次包装的入口。
首先看下BeanFactoryPostProcessor
的定义
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory); } 复制代码
方法postProcessBeanFactory
的参数为ConfigurableListableBeanFactory
,前文说过beanFactory
用来获取bean的,而ConfigurableListableBeanFactory
继承接口SingletonBeanRegistry
和BeanFactroy
,因此能够访问到已经生成过的BeanDefinitions
集合,若是某个类实现该接口,spring会注册这个类,而后执行这个类的postProcessBeanFactory
方法,以便咱们对BeanDefinition
进行扩展。
对于BeanFactoryPostProcessor
只作简单的介绍,只是说明在Spring中,咱们能够修改生成后的BeanDefinition,这里住下看下Spring是如何注册BeanFactoryPostProcessor
并执行postProcessBeanFactory
的。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
//核心方法1
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
//核心方法2 执行BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
//核心方法 3 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
............
throw ex;
}
finally {
............
resetCommonCaches();
}
}
}
复制代码
核心方法1obtainFreshBeanFactory
就是前两篇所说的生成BeanDefinition
的入口,invokeBeanFactoryPostProcessors
核心方法2就是执行BeanFactoryPostProcessor
接口的方法。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}
复制代码
经过方法getBeanFactoryPostProcessors获取注册BeanFactoryPostProcessor,而后来看看如何添加一个处理器
@Override
public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {
this.beanFactoryPostProcessors.add(beanFactoryPostProcessor);
}
复制代码
对于方法invokeBeanFactoryPostProcessors
再也不往下看了,里面的方法大体先对BeanFactoryPostProcessor
进行排序,排序的标准是是否实现了PriorityOrdered
,而后根据设置的order大小指定执行顺序,生成一个排序集合和一个普通的集合,最后执行invokeBeanFactoryPostProcessors
private static void invokeBeanFactoryPostProcessors( Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
//执行到自定义的BeanFactoryPostProcessor
postProcessor.postProcessBeanFactory(beanFactory);
}
}
复制代码
这个方法就会循环先前注册的BeanFactoryPostProcessor
集合,而后执行postProcessBeanFactory
。
与BeanFactoryPostProcessor
相比,BeanPostProcess
就重要得多了,由于Spring的注解、AOP等都是经过这个接口的方法拦截执行的,它贯穿了Bean建立过程的整个生命周期,在IOC阶段,Spring只注册BeanPostProcess,执行则放到了Bean的实例化建立阶段。
首先看下BeanPostProcessor的接口定义
public interface BeanPostProcessor {
//在bean建立 属性赋值以后 Aware接口执行以后执行
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//在init-method afterPropertiesSet 执行以后执行
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
复制代码
在bean的声明周期中,下面的序列是bean建立后要执行的接口和方法顺序:
实例化(
autowireConstructor
或者instantiateBean
)---------------属性初始化(populateBean
)-------------Aware接口(若是bean实现了的话)--------------------------
BeanPostProcess.postProcessBeforeInitialization
--------------------
PostConstructInitializingBean.afterPropertiesSet
-----BeanPostProcess.postProcessAfterInitialization
其中经过注解引入依赖的方式就是在AutowiredAnnotationBeanPostProcessor
这个类中实现的,而接下来要分析的Spring Aop也是从这里开始的,这个类叫AnnotationAwareAspectJAutoProxyCreator
,
在Spring中,任何的技术都是在IOC的基础上进行的,Aop也不例外,程序会首先读取xml配置文件,而后对读取到的标签先查找命名空间,而后找对应的NameSpaceHandler,最终调用parse方法解析标签。
aop标签的解析,使用纯注解的方式aop:aspectj-autoproxy
和使用aop:config
的配置解析不太同样,具体表如今生成PointCut
和生成Before
、After
、Around
等切面类时,使用aop:config
的方式会为这些注解生成一个BeanDefinition
,而这个BeanDefinition
的构造函数是由3个BeanDefinition
组成,代表这个类是合成类,即synthetic
这个属性为true。而后跟解析普通的bean同样,生成这些实例对象,后面的过程就跟是用纯注解的方式相同了,接下来的分析是基于纯注解分析的,也就是解析从解析aop:aspectj-autoproxy
这个标签开始。
前面的xml文件的标签解析是经过parseDefaultElement
方法解析默认的<bean>
标签的,而咱们在配置文件里面配置了启动自动代理的方式<aop:aspectj-autoproxy/>
,当Spring读取到这个标签,则会走parseCustomElement(root)
这个方法了,这个方法的源码再也不解析,主要完成的功能以下:
获取element的nameSpaceUri,根据nameSpaceUri找到NameSpaceHanlder
调用NameSpaceHanlder的parse方法解析element
下面是NameSpaceHanlder接口的定义
public interface NamespaceHandler {
void init();
BeanDefinition parse(Element element, ParserContext parserContext);
BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
}
复制代码
这里面的init方法是咱们初始化操做的,这里能够完成对指定的标签设置解析器,而后再parse方法里面找到指定标签的解析器,而后调用该解析器的parse方法解析标签,后面会重点看这两个方法。
再来看下Spring如何加载NameSpaceHanlder的,Spring首先会取查找项目空间下目录META-INF/的全部spring.handlers文件,这个文件是在Spring依赖的jar下面,在核心jar包都会由这个文件,aop的jar包路径下文件内容为:spring.handlers
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
复制代码
发现这里面存储的是一个key,value,key是aop的nameSpaceUri,value是AopNamespaceHandler,从这个类名上就能发现该类实现了NamespaceHandler,确定也就实现了init和parse方法,因此解析<aop:aspectj-autoproxy/>
的任务就由AopNamespaceHandler的parse完成。
查看AopNamespaceHandler的init方法
@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());
}
复制代码
上面的代码就很清晰了,<aop:config>
标签由ConfigBeanDefinitionParse
r处理,<aop:aspectj-autoproxy/>
则由AspectJAutoProxyBeanDefinitionParser
这个类处理,这两种处理其实对应了自动代理和经过xml配置的处理方式,而后会调用AspectJAutoProxyBeanDefinitionParser
的parse
方法
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
复制代码
这个方法其实就是为了注册一个AnnotationAwareAspectJAutoProxyCreator
类,而后AOP的全部处理逻辑都会交给这个类处理,因为这个类的实现了BeanPostProcessor
,因此这个类的入口就是BeanPostProcessor接口的两个方法:
上面分析了,当spring读取xml文件遇到<aop:aspectj-autoproxy/>
会找到AopNamespaceHandler
这个处理类,而后这个类又将这个标签委托给了AspectJAutoProxyBeanDefinitionParser
类,最终调用这个类得parse方法,parse
方法未作分析,其实这个方法的目的很简单,就是注册AnnotationAwareAspectJAutoProxyCreator
这个类,这个类实现了BeanPostProcessor
和InstantiationAwareBeanPostProcessor
接口,最终在实例化bean对象也就是执行BeanFactory.getBean(beanName)
的过程当中,会调用这两个接口的方法(执行顺序以下):
InstantiationAwareBeanPostProcessor先执行:
postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
postProcessAfterInstantiation(Object bean, String beanName)
BeanPostProcessor再执行:
postProcessBeforeInitialization(Object bean, String beanName)
Object postProcessAfterInitialization(Object bean, String beanName)
AOP的实现基本上是在这两个方法中进行的,因此就从这里来看Spring是如何实现AOP的,Spring的AOP代理目前支持方法的加强,看源码目前好像也支持了属性的加强了。
读取源码前首先来分析一下方法加强的原理,有助于咱们读取源码时牢牢抓住主线。首先第一个问题,若是咱们想对一个类的方法进行加强,咱们应该怎么作呢?
这种业务需求能够经过代理实现,在方法执行前,拦截这个方法,而且加入要执行加强的逻辑,最后再执行目标方法。下面是Spring用的两种代理方式:
JDK代理:咱们能够通Proxy类获取一个目标类的代理对象,但JDK代理要求被代理的类必须实现接口,因此是基于接口的代理。
cglib代理:若是目标类没有接口,使用cglib代理,是由asm封装的,直接操做类得字节码,效率也很高。
因为在生产业务中,咱们不可能对全部的类都执行加强,因此还须要一个选择器,将符合条件的bean进行加强,Spring使用了PointCut
接口,经过该接口的getMethodMatcher
方法获取一个方法匹配器,而后经过matches
方法匹配到目标类对象的目标方法执行加强操做。mathcer
匹配规则就是经过Spring 配置的expression表达式了。
因此在分析源码的时,要围绕这两方面进行:
匹配切点方法(构建切入点表达式类和切面类)
建立代理对象
这两方面在Spring的实现里很是复杂,尤为是第一步匹配切点方法过程,这个过程当中,Spring会将@Aspect
注解类的@Before
,@After
,@Around
、@Pointcut
等注解都封装成待执行的切面方法类,而后经过方法匹配器匹配到的要加强的方法先后执行切面方法类,达到方法加强的目的。
第二阶段,建立代理对象默认是经过JDK代理实现配置,<aop:aspectj-autoproxy proxy-target-class="true">
这样配置能够指定使用cglib代理。
上面分析了真正实现AOP功能的是AnnotationAwareAspectJAutoProxyCreator
,因为这个类实现了BeanPostProcessor
和InstantiationAwareBeanPostProcessor
,因此在建立一个bean的时候,会进入到这两个接口的方法,这两个接口包含了四个方法,方法执行顺序上面已经分析过了,来看看这个类的类图:
类图上比较重要的接口就是右上角实现的两个接口,在bean建立的生命周期过程当中,会校验当前容器中是否注册了实现了这两个接口的类,若是有则调用接口的方法,前面的分析中在解析<aop:aspectj-autoproxy/>
时,将这个类注册到了容器中,并且上面也罗列了这两个接口中四个方法的调用顺序,在这个类中完成主要功能的2个方法及其执行顺序:
InstantiationAwareBeanPostProcessor先执行:
postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
BeanPostProcessor再执行:
Object postProcessAfterInitialization(Object bean, String beanName)
postProcessBeforeInstantiation
方法主要是找出注解了Advice的类,并将Advice的类使用了@Before
,@After
,@Around
、@Pointcut
,@AfterThrowing
等注解的方法封装成一个一个类放入到缓存中供匹配到的类生成代理用。postProcessAfterInitialization
主要是匹配符合条件的目标类对象,而后生成代理的过程,接下来就按顺序分析这两个方法完成的功能。
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
//构建一个缓存key
Object cacheKey = getCacheKey(beanClass, beanName);
if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
//若是当前beanClass的缓存key 存在于Class为Advise的缓存中,表示当前的beanClass是Adivse类
//并且不须要生成代理。
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
//核心校验:1 当前类是不是AOP的基础类 二、当前类是否应该跳过不生成代理
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
//这部分主要是用于实现了TargetSource接口的bean,而后从getTarget中获取对象 建立代理
if (beanName != null) {
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
this.targetSourcedBeans.add(beanName);
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
}
return null;
}
复制代码
这个方法主要是先为beanClass
生成一个缓存的key,这个beanClass
若是是FactoryBean
,则按照工厂类的命名规则命名,不然用beanName
命名,而后用刚才生成的key判断beanClass
是否已经存在于Advice
的缓存集合中,若是已经存在则表明该类是切面类并且已经被处理过了,后续处理不会为该类生成代理,若是没有没处理过,则会调用下面的方法校验该类是不是AOP的基础类 ,总之这个方法做用就是将AOP相关操做的切面类和基础类放入到缓存中,当为bean生成代理的时候,忽略advice
缓存中的AOP切面类和基础类,下面是具体校验过程:
AnnotationAwareAspectJAutoProxyCreator
重写了该方法
@Override
protected boolean isInfrastructureClass(Class<?> beanClass) {
//调用父类的isInfrastructureClass判断是不是aop基础类
//校验当前类是否使用@Aspect注解
return (super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass));
}
复制代码
父类的isInfrastructureClass方法
protected boolean isInfrastructureClass(Class<?> beanClass) {
boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
Advisor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass);
if (retVal && logger.isTraceEnabled()) {
logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
}
return retVal;
}
复制代码
里面isAssignableFrom
表示当前类是否容许被设置为beanClass
类对象,能够以此判断beanClass
是不是Advice类,因此这个方法的校验目的就是判断当前正在建立目标类是不是AOP的基础类,即该类是不是Advice
,Advisor
或者实现了AopInfrastructureBean
接口。该方法调用父类的isInfrastructureClass
判断是不是aop基础类,而后再校验当前类是否使用@Aspect
注解,目的只有一个,若是是Advice
切面相关的类不作任何处理,直接放入advice
缓存便可。
而后再来看shouldSkip(beanClass, beanName)
:
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
//查找当前已经生成的全部Advisor切面类 不展开分析
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor) {
if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
return true;
}
}
}
return super.shouldSkip(beanClass, beanName);
}
复制代码
这个方法主要是校验当前正在建立bean的beanName是否属于已经建立好的切面类缓存中,若是是则加入到advices
缓存中,再也不处理。其中findCandidateAdvisors()
会查找当前容器中生成的全部实现了Advisor
的类,Spring会将@Before
,@After
,@Around
等生成一个继承了Advisor类对象存储到缓存中供后续使用,这一部分时Spring AOP前半段的核心内容,后续都会围绕着如何将切面类的注解生成Adisor类探索。
AnnotationAwareAspectJAutoProxyCreator
重写了findCandidateAdvisors
方法,因此会执行到该方法:
@Override
protected List<Advisor> findCandidateAdvisors() {
//经过父类的方法查找全部容器中的Advisor类,也就是基于xml配置的<aop:before/>生成的
List<Advisor> advisors = super.findCandidateAdvisors();
//查找经过注解的方式生成Advisor类
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
复制代码
这个方法会首先调用父类的findCandidateAdvisors
方法用于获取经过xml文件配置生成的Advisor
,也就是经过<aop:before>
,<aop:after>
等生成的,而后调用经过注解方式即@Before
,@After
,@Around
、@Pointcut
,@AfterThrowing
生成的advisor,能够说,这两个方法分别处理了基于xml配置文件的方式和基于注解的配置方式,由于全部的分析都是基于AnnotationAwareAspectJAutoProxyCreator这个类进行的,因此在这个地方会先获取配置文件的,再生成基于注解类的Advisor,这样就将基于xml配置的和基于注解的配置都会解析到。
看下this.aspectJAdvisorsBuilder.buildAspectJAdvisors()
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = null;
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<Advisor>();
aspectNames = new LinkedList<String>();
//从beanDefinitions中获取全部的beanName
String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
//若是beanName不符合配置的 <aop:include name="***"/>
//忽略这个bean上全部的切面方法
if (!isEligibleBean(beanName)) {
continue;
}
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
//若是当前beanType是一个切面类 则将该切面类相关信息封装起来
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
//将切面信息放入到分装到MetadataAwareAspectInstanceFactory 生成一个AspectMetadata
MetadataAwareAspectInstanceFactory factory =new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//获取容器中全部Advisor类 须要进入这个方法详细分析
List<Advisor> classAdvisors=this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
//单例加入缓存
this.advisorsCache.put(beanName, classAdvisors);
}
else {
//非单例 将工厂加入缓存
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// 非单例 将生成Advisor的工厂类加入到缓存
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;
}
}
..............
}
复制代码
这个方法主要的任务其实就是获取类得类型为Aspect
的切面类,而后获取切面类方法的全部注解并将注解转换成Advisor
类返回,主要步骤为:
获取容器中全部的BeanDefinition的beanName
根据beanName
,或者beanClass
,匹配符合规则的Aspect切面类,经过<aop:include>
配置的规则
获取Aspect
切面类的全部切面方法封装成Advisor
对象返回。
将获取到的全部Advisor
放入到缓存中。
这个方法代码虽然不少,可是核心的是this.advisorFactory.getAdvisors(factory)
,即第三个步骤,这个方法将会获取到切面类的全部切面方法,并封装成Advisor
,getAdvisors
是一个接口,ReflectiveAspectJAdvisorFactory
实现了这个接口,下面代码是其实现逻辑:
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
//获取切面类Class
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
//获取切面类的beanName
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
//进一步对AspectMetadata封装 里面包含了切面类的信息
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new LinkedList<Advisor>();
//获取切面类中没有使用Pointcut注解的方法
for (Method method : getAdvisorMethods(aspectClass)) {
//检查该方法是不是切面方法, 若是是成Advisor类返回
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
//若是没有切面方法 设置一个空的
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
//处理属性字段 Spring支持到了属性的加强
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
复制代码
这个方法首先已经将切面类信息封装到AspectMetadata
的类再次封装到MetadataAwareAspectInstanceFactory
,而后获取切面类的全部没有使用Pointcut
注解的方法,调用getAdvisor
获取这个方法使用的切面注解,生成对应的Advisor
类。 至于PointCut
的处理则是再后面的getAdvisor
中处理的。
获取Advisor
类的方法为getAdvisor
,首先来看下这个方法的参数:
//切面类的切面方法 这里可能就是 beforePrint()
Method candidateAdviceMethod
//获取AspectMetadata的实例工厂(能够获取切面的类全部信息)
MetadataAwareAspectInstanceFactory aspectInstanceFactory
//切面的排序
int declarationOrderInAspect
//切面类的beanName 这里是tracesRecordAdvisor
String aspectName
复制代码
上面的参数中能够获取到切面类和切面方法,这样就能够得到一个Advisor对象,而后还须要一个切入点表达式PointCut
用来匹配符合条件的方法,拦截到目标方法后,就能够执行Adivsor
加强方法了。 来看看建立Advisor
的过程,这里假设Method
是TracesRecordAdvisor
类的beforePrint
方法,也就是咱们测试案例中建立使用了@Before
注解的切面方法:
@Override
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
//获取pointCut,这里实际上得到的是 expression()这个方法对应了pointCut的内容
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
//建立advisor
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
复制代码
看看getPointCut
方法如何获取到exression
过程须要嵌套不少步骤,这里不展开了,简单看下如何将查找到的值设置到表达式中的:
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
//
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
//将上面生成的AspectJAnnotation 解析出的expression方法放入到表达式中
//
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
return ajexp;
}
复制代码
这里须要关注下上面的findAspectJAnnotationOnMethod
方法:
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
//看到了咱们熟悉的切面方法注解,这里的beforePrint使用@Before注解
Class<?>[] classesToLookFor = new Class<?>[] {
Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
for (Class<?> c : classesToLookFor) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
复制代码
这个方法就是查找切面方法是否使用了Before
, Around
, After
,AfterReturning
, AfterThrowing
,Pointcut
注解,若是使用了,则返回一个AspectJAnnotation
对象,里面有一个annotation
的泛型对象,这个泛型对象就是被设置为这些注解的值,并且还会得到这些注解里面配置的pointcut
表达式内容,若是是引用的表达式方法,则将方法参数设置到pointcutExpression
这个属性中。
解析完切面方法的注解后如今再回过头来看看如何建立一个advisor
实例:
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
this.declaredPointcut = declaredPointcut;
this.aspectJAdviceMethod = aspectJAdviceMethod;
this.aspectJAdvisorFactory = aspectJAdvisorFactory;
this.aspectInstanceFactory = aspectInstanceFactory;
this.declarationOrder = declarationOrder;
this.aspectName = aspectName;
//切面类是不是懒加载
if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut = Pointcuts.union(
aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
// If it's not a dynamic pointcut, it may be optimized out
// by the Spring AOP infrastructure after the first evaluation.
this.pointcut = new PerTargetInstantiationModelPointcut(
this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
this.lazy = true;
}
else {
this.pointcut = this.declaredPointcut;
this.lazy = false;
//最终会执行到这里获取一个advice
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
}
复制代码
上面方法的最后一句instantiateAdvice(this.declaredPointcut)
会建立一个advice
,具体是调用getAdvice
方法获取:
@Override
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
//获取切面类对象,这里是TracesRecordAdvisor
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
//核心点1:获取切面注解,这里得方法是 beforePrint 使用了@Before注解
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
....................
AbstractAspectJAdvice springAdvice;
//核心点2:根据注解转换后的 将注解生成不一样的Advice类。
switch (aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtPointcut:
//这里对PointCut不作处理
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// 将切面类信息配置到SpringAdvice中
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
复制代码
首先来看看核心点1,上面其实已经看过了, 可是上面的方法做用仅仅是为了获取注解上的exression
表达式的,这里再调用一遍就是为注解生成Advice
类的,目的就是获取切面注解与AspectJAnnotation
的映射类。
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
//看到了咱们熟悉的切面方法注解,这里的beforePrint使用@Before注解
Class<?>[] classesToLookFor = new Class<?>[] {
Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
for (Class<?> c : classesToLookFor) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
复制代码
这个方法就是查找切面方法是否实现了Before
, Around
, After
,AfterReturning
, AfterThrowing
,Pointcut
注解,若是实现了,则返回一个AspectJAnnotation
对象,里面有一个annotation
的泛型对象,这个泛型对象就是被设置为这些注解的值。最终这些对象会被转换成下面的对象存入AspectJAnnotation
中:
static {
//会将注解转换成后面的AspectJAnnotationType枚举的类。
annotationTypes.put(Pointcut.class,AspectJAnnotationType.AtPointcut);
annotationTypes.put(After.class,AspectJAnnotationType.AtAfter);
annotationTypes.put(AfterReturning.class,AspectJAnnotationType.AtAfterReturning);
annotationTypes.put(AfterThrowing.class,AspectJAnnotationType.AtAfterThrowing);
annotationTypes.put(Around.class,AspectJAnnotationType.AtAround);
annotationTypes.put(Before.class,AspectJAnnotationType.AtBefore);
}
复制代码
经过核心点1,Spring已经将注解@Before
对应转换为AtBefore
,@After
转换成AtAfter
,以此类推,都会一一映射到了核心点2的switch的条件类了,在核心点2中,会为对应的切面注解类生成Advice
类。 全部的注解切面类具体实现都是由AbstractAspectJAdvice
这个抽象类实现的,这个类的构造函数有三个参数:
//切面方法 这里多是beforePrint
Method aspectJAroundAdviceMethod
//切入点表达式匹配器 这里指封装了exression的匹配器
AspectJExpressionPointcut pointcut
//切面类 这里指TracesRecordAdvisor
AspectInstanceFactory aif
复制代码
下面是Spring为对应注解生成对应的Advice类
注解类 | Advice 顾问方法 |
---|---|
AtBefore | AspectJMethodBeforeAdvice |
AtAfter | AspectJAfterAdvice |
AtAfterReturning | AspectJAfterReturningAdvice |
AtAfterThrowing | AspectJAfterThrowingAdvice |
AtAround | AspectJAroundAdvice |
各个注解会在不一样的实际执行自身加强方法,这个部分只是生成Advice
类,然会放入到缓存中,等真正生成代理时就会调用这些方法。这个在建立代理的时候须要具体拆开说,至此,Spring将使用了@Aspect
注解的切面类的切面方法,都转换成了对应的Adivsor
类,这个类包含了切面方法,封装后的切点匹配器PointCut
以及生成切面类的实例对象,经过这个类就能够匹配到符合条件的目标类的目标方法,而后执行加强操做了。
由切面注解生成的Advice
类,最终会放入到一个缓存中,当生成目标bean的时候,会将全部因此可以匹配到目标bean的advice放入到集合中,由一个实现了MethodInvocation
的类统一管理调用过程,这个类后面会详细说到,这里简单分析下AspectJAfterAdvice
的invoke方法,看看它的调用过程
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
//调用是实现了MethodInvocation方法的类 这个实际上是个链式调用
return mi.proceed();
}
finally {
//最终执行后置加强方法
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
复制代码
上面的invoke
方法须要一个MethodInvocation
的参数,上面的Advice
类除了AspectJMethodBeforeAdvice
以外,都实现了这个接口,因此能够实现链式调用,这个逻辑会在建立代理的具体讲解,这里只是简单分析下,这些advice
的invoke
方法规定了切面方法于要加强方法的执行时机。
上面一部分操做主要是处理使用了@Aspect
注解的切面类,而后将切面类的全部切面方法根据使用的注解生成对应的Advisor
的过程,这个Advisor
包含了切面方法,切入点匹配器和切面类,也就是准好了要加强的逻辑,接下来就是要将这些逻辑注入到合适的位置进行加强,这部分的操做就是由老生常谈的代理实现的了。
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//若是要建立的类不是提早暴露的代理 则进入下面的方法
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
复制代码
建立代理前,须要先校验bean是否须要建立代理
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//若是bean是经过TargetSource接口获取 则直接返回
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//若是bean是切面类 直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//若是bean是Aspect 并且容许跳过建立代理, 加入advise缓存 返回
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
//若是前面生成的advisor缓存中存在可以匹配到目标类方法的Advisor 则建立代理
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;
}
复制代码
方法很简单,主要的关注点在getAdvicesAndAdvisorsForBean
和createProxy
上,第一个是获取可以匹配目标类方法的Advisor
集合,若是这个集合不为空,则表明该类须要被加强,须要生成代理,若是匹配不到,则表示该类并不须要被加强,无需建立代理。至于createProxy
就很明显了,就是建立代理,这个方法里面决定了使用jdk代理仍是cglib代理,而且用到了前面生成的Advisor实现加强功能。 这部份内容会放到下一篇文章中专门分析。
简单总结一下,Spring AOP在第一阶段完成的主要任务:
读取配置文件阶段:
读取xml文件遇到 <aop:aspectj-autoproxy/>
标签时,找到命名空间处理器AopNamespaceHandler
,而后找处处理该标签的类AspectJAutoProxyBeanDefinitionParser
经过AspectJAutoProxyBeanDefinitionParser
的parse
方法,将AspectJAwareAdvisorAutoProxyCreator
注册到容器的声明周期中。
建立bean阶段:
执行AspectJAwareAdvisorAutoProxyCreator
的postProcessBeforeInstantiation
校验目标类是不是Aspect
类和AOP基础类以及是否须要跳过不须要执行代理的类
获取beanDefinitions
中全部使用了Aspect注解的类,而后将切面方法根据使用的注解生成Advisor类放入到缓存(关键)
调用AspectJAwareAdvisorAutoProxyCreator
的postProcessAfterInitialization
的方法,对须要加强的类建立代理。
这个就是Spring AOP在这个阶段所完成的工做,下一部分将专门针对Spring如何实现jdk和cglib代理分析。