在上一篇,我们对IOC核心部分流程已经分析完毕,相信小伙伴们有所收获,从这一篇开始,我们将会踏上新的旅程,即Spring的另一核心:AOP!
首先,为了让大家能更有效的理解AOP,先带大家过一下AOP中的术语:
- 切面(Aspect):指关注点模块化,这个关注点可能会横切多个对象。事务管理是企业级Java应用中有关横切关注点的例子。在Spring AOP中,切面可以使用在普通类中以@Aspect注解来实现。
- 连接点(Join point):在Spring AOP中,一个连接点总是代表一个方法的执行,其实就代表增强的方法。
- 通知(Advice):在切面的某个特定的连接点上执行的动作。通知有多种类型,包括
around
,before
和after
等等。许多AOP框架,包括Spring在内,都是以拦截器做通知模型的,并维护着一个以连接点为中心的拦截器链。 - 目标对象(Target):目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。
- 切点(Pointcut):匹配连接点的断言。通知和切点表达式相关联,并在满足这个切点的连接点上运行(例如,当执行某个特定名称的方法时)。切点表达式如何和连接点匹配是AOP的核心:Spring默认使用AspectJ切点语义。
- 顾问(Advisor): 顾问是Advice的一种包装体现,Advisor是Pointcut以及Advice的一个结合,用来管理Advice和Pointcut。
- 织入(Weaving):将通知切入连接点的过程叫织入
- 引入(Introductions):可以将其他接口和实现动态引入到targetClass中
一个栗子
术语看完了,我们先上个Demo回顾一下吧~
- 首先,使用
EnableAspectJAutoProxy
注解开启我们的AOP
@ComponentScan(basePackages = {"com.my.spring.test.aop"}) @Configuration @EnableAspectJAutoProxy public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); IService service = context.getBean("service", IService.class); service.doService(); } }
- 写一个接口
public interface IService { void doService(); }
- 写一个实现类
@Service("service") public class ServiceImpl implements IService{ @Override public void doService() { System.out.println("do service ..."); } }
- 写一个切面
@Aspect @Component public class ServiceAspect { @Pointcut(value = "execution(* com.my.spring.test.aop.*.*(..))") public void pointCut() { } @Before(value = "pointCut()") public void methodBefore(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("执行目标方法 【" + methodName + "】 的【前置通知】,入参:" + Arrays.toString(joinPoint.getArgs())); } @After(value = "pointCut()") public void methodAfter(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("执行目标方法 【" + methodName + "】 的【后置通知】,入参:" + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(value = "pointCut()") public void methodReturn(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("执行目标方法 【" + methodName + "】 的【返回通知】,入参:" + Arrays.toString(joinPoint.getArgs())); } @AfterThrowing(value = "pointCut()") public void methodThrow(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("执行目标方法 【" + methodName + "】 的【异常通知】,入参:" + Arrays.toString(joinPoint.getArgs())); } }
- 测试运行
执行目标方法 【doService】 的【前置通知】,入参:[] do service ... 执行目标方法 【doService】 的【返回通知】,入参:[] 执行目标方法 【doService】 的【后置通知】,入参:[]
以上
Demo看完了,运行效果也出来了,AOP已生效,但如何生效的呢?相比于我们普通使用Bean的Demo,在这里,我们只不过加上了一个@EnableAspectJAutoProxy
注解以及一个标识了@Aspectj
的类,那么我们先看看@EnableAspectJAutoProxy
这个注解做了什么吧~
开启AOP
以下是笔者所画的大致流程图
EnabelAspectJ.png
其中AspectJAutoProxyRegistrar
实现了ImportBeanDefinitionRegistrar
,所以在处理BeanFactoryPostProcessor
逻辑时将会调用registerBeanDefinitions
方法,此时就会把AnnotationAwareAspectJAutoProxyCreator
注册到容器中,其中BeanFactoryPostProcessor
的逻辑就不再说了,往期文章有过详细分析。而AnnotationAwareAspectJAutoProxyCreator
的类图如下:
AnnotationAwareAspectJ.png
我们发现AnnotationAwareAspectJAutoProxyCreator
是实现了BeanPostProcessor
接口的类,所以它其实是一个后置处理器,那么,还记得在创建Bean过程中的BeanPostProcessor
九次调用时机吗?不记得也没关系,AnnotationAwareAspectJAutoProxyCreator
起作用的地方是在bean的实例化前以及初始化后,分别对应着解析切面和创建动态代理的过程,现在,就让我们先来看看解析切面的过程吧~
解析切面
解析切面的流程如下图所示:
切面解析过程.png
我们已经了解到切面解析的过程是由AnnotationAwareAspectJAutoProxyCreator
完成的,而AnnotationAwareAspectJAutoProxyCreator
又继承了AbstractAutoProxyCreator
,所以首先,我们先会来到AbstractAutoProxyCreator#postProcessBeforeInstantiation
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) { // class类型是否为(Advice, Pointcut, Advisor, AopInfrastructureBean) // shouldSkip中将会解析切面 if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } }
调用到子类的AspectJAwareAdvisorAutoProxyCreator#shouldSkip
@Override protected boolean shouldSkip(Class<?> beanClass, String beanName) { // 寻找advisor List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) { return true; } } return super.shouldSkip(beanClass, beanName); }
findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() { // 寻找实现了Advisor接口的类, 由于我们一般不会以接口的方式实现切面,这里返回null List<Advisor> advisors = super.findCandidateAdvisors(); if (this.aspectJAdvisorsBuilder != null) { // 这里将解析出所有的切面 advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; }
buildAspectJAdvisors
public List<Advisor> buildAspectJAdvisors() { // aspectBeanNames有值则说明切面已解析完毕 List<String> aspectNames = this.aspectBeanNames; // Double Check if (aspectNames == null) { synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { List<Advisor> advisors = new ArrayList<>(); aspectNames = new ArrayList<>(); // 取出是Object子类的bean,其实就是所有的bean String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); for (String beanName : beanNames) { // 获得该bean的class Class<?> beanType = this.beanFactory.getType(beanName); // 判断是否有标识@AspectJ注解 if (this.advisorFactory.isAspect(beanType)) { // 将beanName放入集合中 aspectNames.add(beanName); // 将beanType和beanName封装到AspectMetadata中 AspectMetadata amd = new AspectMetadata(beanType, beanName); // Kind默认为SINGLETON if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 这里会通过@Before @After等标识的方法获取到所有的advisor List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); if (this.beanFactory.isSingleton(beanName)) { // 将获取到的所有advisor放入缓存 this.advisorsCache.put(beanName, classAdvisors); } advisors.addAll(classAdvisors); } } } // 将所有解析过的beanName赋值 this.aspectBeanNames = aspectNames; return advisors; } } } // aspectNames不为空,意味有advisor,取出之前解析好的所有advisor List<Advisor> advisors = new ArrayList<>(); // 获取到所有解析好的advisor for (String aspectName : aspectNames) { List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } return advisors; }
advisorFactory.getAdvisors
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { // 获取到标识了@AspectJ的class,其实就是刚刚封装的class Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); // 获取className String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); List<Advisor> advisors = new ArrayList<>(); // 拿出该类除了标识@PointCut的所有方法进行遍历 getAdvisorMethods时会对method进行一次排序 // 排序顺序 Around, Before, After, AfterReturning, AfterThrowing for (Method method : getAdvisorMethods(aspectClass)) { // 获取到advisor Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName); if (advisor != null) { // 加入到集合中 advisors.add(advisor); } } }
我们先看下getAdvisorMethods
方法
private List<Method> getAdvisorMethods(Class<?> aspectClass) { final List<Method> methods = new ArrayList<>(); // 循环遍历该类和父类的所有方法 ReflectionUtils.doWithMethods(aspectClass, method -> { // 排除@PointCut标识的方法 if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { methods.add(method); } }, ReflectionUtils.USER_DECLARED_METHODS); if (methods.size() > 1) { // 以Around, Before, After, AfterReturning, AfterThrowing的顺序自定义排序 methods.sort(METHOD_COMPARATOR); } return methods; }
不知道小伙伴们对ReflectionUtils.doWithMethods这个工具类熟不熟悉呢,这个工具类在之前分析Bean创建过程时可是出现了好多次呢,并且我们也是可以使用的
现在,已经获取到切面中的所有方法了,那么接下来就该对这些方法解析并进行封装成advisor了~
getAdvisor
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { // 获取方法上的切点表达式 AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 封装成对象返回,创建对象时将会解析方法创建advice return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
获取切点表达式的过程其实非常简单,即是解析方法上的注解,取出注解上的value即可
getPointcut
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) { // 查找方法上和AspectJ相关注解 AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); // 设置切点表达式 AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]); // PointcutExpression 为注解上value属性的值 ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); if (this.beanFactory != null) { ajexp.setBeanFactory(this.beanFactory); } return ajexp; }
new InstantiationModelAwarePointcutAdvisorImpl,在这里,才会真正创建出advice
public InstantiationModelAwarePointcutAdvisorImpl(){ //...省略赋值过程... // 实例化出advice this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); }
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) { // 获取advice,aspectJAdviceMethod为方法,aspectName为切面类 Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName); return (advice != null ? advice : EMPTY_ADVICE); }
public Advice getAdvice(){ // 根据方法获取到注解信息 AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); AbstractAspectJAdvice springAdvice; // 根据注解类型返回对象,创建对象的过程都是一样的,都是调用父类的构造方法 // candidateAdviceMethod为切面的方法,expressionPointcut是切点 switch (aspectJAnnotation.getAnnotationType()) { case AtPointcut 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; //...省略其他的advice default: throw new UnsupportedOperationException( "Unsupported advice type on method: " + candidateAdviceMethod); } return springAdvice; }
springAdvice已创建完毕,意味着切面中的某个方法已经解析完毕了,其他的方法解析过程大致也是相似的