Aop的执行原理,我们应该基本都了解:通过编写切面类,我们可以在指定的切入点处插入我们额外的代码块,就好比代理模式中,我们可以在执行目标方法的前后干一些自己想干的事情。那么这是怎么样实现的呢?
我们自己写的类中的代码是硬编码写死的,要想改变一个已经写好的类,我们常见的操作就是动态代理了,没错,AOP的底层就是将切入点所在的类创建成了代理对象。
我们知道Spring中的一个主要功能就是管理所有的bean对象,在创建对象的时候,为我们提供了很多的扩展点,可以方便我们来干预对象的创建,那么Aop究竟是在哪一个扩展点的地方帮我们创建了代理对象呢?本文就来讲解下从Aop的前置准备到创建代理对象的整个流程。
一、@EnableAspectJAutoProxy
熟悉Spring开发的模式的话,我们都知道,开启一个新的功能的话,我们基本需要在配置类上加上一个@EnableXxx的注解,而这类@EnableXxx的注解多半是向Spring容器中注入了影响bean创建生命周期的bean信息,开启基于注解的Aop的@EnableAspectJAutoProxy注解同样于此,他为我们导入了AnnotationAwareAspectJAutoProxyCreator类的定义信息。我们来看下AnnotationAwareAspectJAutoProxyCreator的继承实现结构图:
在整个结构图中,能干预bean的生命周期的是左上角的BeanPostProcessor接口,那么整个AOP功能的具体实现就是在对于InstantiationAwareBeanPostProcessor的方法的具体实现中,就是AbstractAutoProxyCreator类中postProcessBeforeInstantiation方法和postProcessAfterInitialization方法,我们下面就关注它的具体实现:
二、Aop的前置准备
1、AOP基础准备工作
首先,我们来看AbstractAutoProxyCreator类中postProcessBeforeInstantiation方法的具体实现,他实现的接口是InstantiationAwareBeanPostProcessor,而InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation的触发时机是在Spring容器开始准备创建bean之前,Spring给我们提供了一个机会可以自己在这创建对象,而不走Spring的创建对象流程,但是AOP会在此就创建对象吗?我们继续往下看。
我们先看下前半部分代码:
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {//advisedBeans已经分析过的组件,不是增强过的组件
return null;
}//所有增强了的组件会被缓存在advisedBeans中,如果是我们需要增强的bean,就放在缓存中
//isInfrastructureClass(beanClass):判断当前类是否有@Aspect注解,即当前类是否是切面;shouldSkip中解析了所有的切面类并封装了切面类中的切面方法
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {// shouldSkip:如果是切面类,则跳过处
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
这段代码中主要在管理advisedBeans这个集合,这个集合中key为bean的名称,value为boolean值,个人理解为这个集合的意义是,key表示处理过的beanName,而value值表示这个bean应不应该被增强【不代理】。那什么样的类是不需要增强的呢?这就需要关注if中的两个条件了,一个是isInfrastructureClass(beanClass),另一个是shouldSkip(beanClass, beanName),满足这两个条件之一的都会加入advisedBeans集合,并且标记为不增强;一起来看看这两个方法:
- isInfrastructureClass(beanClass):判断是不是AOP的基础设施类,如果是的话,就加入到advised集合中,并标记为不应该增强,那么究竟什么类是AOP的基础设施类呢?深入到代码里我们发现:分为两大块:一个是实现了Advice,Pointcut,Advisor,AopInfrastructureBean这四个接口的类属于AOP的基础设施类;另一个是这个类上面标注了@Aspect,即切面类
- shouldSkip(beanClass, beanName):这个方法是AOP准备过程中的一个重要点,因为这个方法中干了很多事情,为后面的AOP代理做好了铺垫;代码如下:
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// TODO: Consider optimization by caching the list of the aspect names
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}
整个应不应该跳过的逻辑是:获取所有的Advisor集合,然后遍历Advisor集合,如果当前bean的名称和其中的一个AspectJPointcutAdvisor的aspectName相同,其实就是说明这个类是切面类,那么就会跳过。到这里,我相信有的小伙伴就会发出一个疑问了:前面不是处理过了吗?为什么此处还是需要处理呢?其实这里我也有点疑问,但是它干了更多的事情,那就是创建并缓存了所有的Advisor对象。
在findCandidateAdvisors方法的逻辑中首先是super执行了AbstractAdvisorAutoProxyCreator中的逻辑:找到所有的Advisor的实现类的bean名称,并进行getBean创建对象;其次时寻找@Aspect注解的类,然后为通知方法构建Advisor,整个的构建流程可以归纳如下:
- 拿到容器中所有的bean名称
- 循环遍历beanName,拿到beanType,判断是不是切面类(@Aspect)
- 是切面类的话,就拿到类中除去标有@Pointcut的方法。然后遍历方法:
- 把每个切面方法构建为Advisor【InstantiationModelAwarePointcutAdvisorImpl】
- 处理@DeclareParents属性注解,最终会构建DeclareParentsAdvisor【属于IntroductionAdvisor,类级别的切入】,其中对应的Advice为DelegatePerTargetObjectIntroductionInterceptor【属于IntroductionInterceptor,同样也是MethodInterceptor】
- 缓存进advisorsCache 【beanName--->List<Advisor>】
2、Advice的构建
其中,在构建InstantiationModelAwarePointcutAdvisorImpl中,会构建当前增强方法的Advice,也就是构造方法中的instantiateAdvice方法,最终会调用ReflectiveAspectJAdvisorFactory的getAdvice方法来构建相应的Advice,关键代码如下:
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
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;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
最终会把切面类beanName和其中的增强方法Advisor存放到advisorsCache缓存中,方便后面使用。
可以说准备阶段,Spring主要就干了两件事吧:第一件事是标记那些不需要代理的AOP基础设施类;第二件事就是寻找并创建容器中所有的Advisor,这一步也分为两小步:其一,去寻找并创建所有直接实现了Advisor接口的;其二,处理标注了@Aspect注解的类,并将其以aspectName及List<Advisor>的形式缓存到advisorsCache中。
3、Advisor & Advice
有必要介绍下AOP中的Advisor和Advice,这两个东西到底有什么区别和关系呢?Advice就是我们要干预正常代码而额外加的一部分代码逻辑,其实可以理解为拦截器,而Advisor可以说成是对Advice的一种封装吧,因为每个Advisor包含一个Advice,然后Advisor还应该包括Advice增强的增强表达式,即它应该什么时候进行增强,即增强条件吧;在Spring中,提供了两个Advisor的子接口,分别是IntroductionAdvisor和PointcutAdvisor,IntroductionAdvisor提供的getClassFilter()方法和PointcutAdvisor中提供的getPointcut()方法就是各自的增强条件(如下图),从增强条件我们也可以看出:IntroductionAdvisor是基于类级别的增强,而PointcutAdvisor是基于方法级别或者类级别的增强,显然后者方法级别的增强是更加细粒度的,也是我们常用的@Aspect注解的Advisor。如果这么说还是过于抽象的话,那我们拿我们常用的切面类@Aspect来进行来进行类比:
@Component //切面也是容器中的组件
@Aspect //说明这是切面
public class LogAspect {
public LogAspect() {
System.out.println("LogAspect....");
}
@DeclareParents(value = "com.spring.aop.HelloService", defaultImpl = DeclareParentsTestImpl.class)
private DeclareParentsTest declareParentsTest;
//前置通知
@Before("execution(* com.spring.aop.HelloService.sayHello(..))")
public void logStart(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
System.out.println("logStart()===>" + name + "...【args:" + Arrays.asList(joinPoint.getArgs()) + "】");
}
//返回通知
@AfterReturning(value = "execution(* com.spring.aop.HelloService.sayHello(..))",returning = "result")
public void logReturn(JoinPoint joinPoint,Object result) {
String name = joinPoint.getSignature().getName();
System.out.println("logReturn()==>" + name + "...【args:"+ Arrays.asList(joinPoint.getArgs())+"】【result:"+result+"】");
}
//后置通知
@After("execution(* com.spring.aop.HelloService.sayHello(..))")
public void logEnd(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
System.out.println("logEnd()===>" + name + "...【args:" + Arrays.asList(joinPoint.getArgs()) + "】");
}
//异常通知
@AfterThrowing(value = "execution(* com.spring.aop.HelloService.sayHello(..))",throwing = "e")
public void logError(JoinPoint joinPoint,Exception e) {
String name = joinPoint.getSignature().getName();
System.out.println("logError()==>" + name + "...【args:"+ Arrays.asList(joinPoint.getArgs())+"】【result:"+e+"】");
}
}
像上面logStart,logReturn,logEnd,logError等就可以理解为一个Advice,而@Before,@AfterReturning,@After,@AfterThrowing中的表达式就会说一个Pointcut,然后方法加上注解中的表达式就构成了一个Advisor(PointcutAdvisor),相信这么说,大家应该都能明白了吧。
三、Aop生成代理
Spring提供了两个地方来生成Aop代理对象,下面我们来看看在哪两个地方可以生成Aop的代理对象:
1、Aop生成代理对象的第一个地方
第一个地方是在AbstractAutoProxyCreator的postProcessBeforeInstantiation方法中,Aop的准备工作做完后,就会查看有没有自定义的TargetSource,如果有符合的TargetSource的话就会在此处直接创建bean的代理对象,不会继续走Spring的创建bean的流程,但是,这个地方有个麻烦的点,就是我们需要干预到AnnotationAwareAspectJAutoProxyCreator的创建,需要修改其Bean的定义信息,将我们自定义的TargetSourceCreator赋值给AnnotationAwareAspectJAutoProxyCreator的customTargetSourceCreators属性,我们可以如下操作:
自定义的TargetSource:
public class MyTargetSource implements TargetSource {
private final Object target;
public MyTargetSource(Object target) {
this.target = target;
}
@Override
public Class<?> getTargetClass() {
return HelloService.class;
}
@Override
public boolean isStatic() {
return true;
}
@Override
public Object getTarget() throws Exception {
return target;
}
@Override
public void releaseTarget(Object target) throws Exception {
}
}
封装成TargetSourceCreator:
public class MyTargetSourceCreator implements TargetSourceCreator {
@Override
public TargetSource getTargetSource(Class<?> beanClass, String beanName) {
if (beanName.equals("helloService")) {
try {
return new MyTargetSource(ReflectionUtils.accessibleConstructor(HelloService.class).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
实现BeanFactoryPostProcessor修改AnnotationAwareAspectJAutoProxyCreator的bean定义信息:
@Component
public class TargetSourceCreatorBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bd = beanFactory.getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME); // AnnotationAwareAspectJAutoProxyCreator的beanName
bd.getPropertyValues().add("customTargetSourceCreators",new TargetSourceCreator[]{new MyTargetSourceCreator()});
}
}
如上述配置后,HelloService的对象创建就会在AOP准备工作做好后,通过如下代码,获取到自定义TargetSource后直接创建AOP代理对象,不走这个bean后面的生命周期了:
//创建个代理,如果为这个类指定了targetSource会在此就生成代理直接返回了,不走这个bean后面的生命周期了
TargetSource targetSource = getCustomTargetSource(beanClass, beanName); //
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
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;
}
2、Aop生成代理对象的第二个地方
另一种情况是,当没有为对象指定自定义的TargetSource时,Spring会在bean的对象创建完成后的AbstractAutoProxyCreator的postProcessAfterInitialization方法中的wrapIfNecessary方法中创建Aop代理对象,代码如下:
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(//创建代理对象,specificInterceptors所有的增强器
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
两处创建Aop代理对象的逻辑都是一样的,仅仅是时机不一样罢了,Spring默认使用的是CGLIB来创建代理对象的,具体的创建过程在此就不说了,后续有时间再补上,我们需要知道的是:当我们执行被AOP增强的类时,需要回调DynamicAdvisedInterceptor这个类中的intercept方法,这也是后面我们讲述AOP执行流程的入口。