首页 技术 正文
技术 2022年11月14日
0 收藏 591 点赞 2,769 浏览 7839 个字

一.前言

  上一章节主要介绍了JDK动态代理和CGLIB动态代理:https://www.cnblogs.com/GrimMjx/p/11194283.html

  这一章主要结合我们之前学习的动态代理的基础来学习Sring AOP,本章学习需要Spring IOC的基础。首先会有一个Spring AOP的例子,后面逐渐深入会把一些关键的源码贴出来供大家学习。

二.一个栗子

2.1 创建Spring配置文件

  本例子使用xml的方式来配置Spring,如果你用Springboot可以用@EnableAspectJAutoProxy来开启AOP功能。xml配置Spring是否使用注解AOP的方式是<aop:aspectj-autoproxy/>,配置文件中有这句话Spring就会支持注解的AOP。

从动态代理到Spring AOP(中)

2.2 创建要被代理的bean

  这个bean的某个方法可能封装着核心逻辑,如果我们想对这个方法的前后加入日志或者其他逻辑进行增强,直接修改这个bean不符合面向对象的设计,还好Spring AOP帮我们做到这一点。那我们先创建一个简单的bean:

从动态代理到Spring AOP(中)

2.3 创建Advisor

  Spring2.0可以采用@AspectJ注解对POJO进行标记,从而定义一个包含切点信息和增强横切逻辑的切面然后织入匹配的目标Bean中

  在AspectJConfig中,我们简单的对test方法前后记录日志。记住光用@AspectJ注解是不够的。要么再添加@Component注解要么在xml添加这个bean,官方解释如下:

  You may register aspect classes as regular beans in your Spring XML configuration, or autodetect them through classpath scanning – just like any other Spring-managed bean. However, note that the @Aspect annotation is not sufficient for autodetection in the classpath: For that purpose, you need to add a separate @Component annotation (or alternatively a custom stereotype annotation that qualifies, as per the rules of Spring’s component scanner).

  再来看一下代码:

从动态代理到Spring AOP(中)

2.4 测试

  我们可以写一个简单的测试类,通过容器拿到TestBean这个bean,然后调用test方法,看一下结果:

从动态代理到Spring AOP(中)

三.源码赏析

  最好带着问题去看源码,要不然自己也是跟着走一遍,源码是无尽,开发思想是有尽的。比如今天看了ConcurrentHashMap的size方法对cas的优化,再看看LongAdder是咋玩的,学到了分散思想。

  -谁来创建AOP的?

  -谁来解析@Aspect注解的类?

  -JDK动态代理怎么创建的?

  -CGlib动态代理怎么创建的?

  -有什么好的设计方式吗?

  -等等

3.1 谁来创建?

  是AnnotationAwareAspectJAutoProxyCreator。(名字就很通俗易懂)

  Spring扫到<aop:aspectj-autoproxy/>后,AspectJAutoProxyBeanDefinitionParser会注册这位创建者。对于Spring AOP的实现,AnnotationAwareAspectJAutoProxyCreator是负责代理的创建者,也是我们赏析的开始。先来看下这个创建者的类图:

从动态代理到Spring AOP(中)

  当Spring加载每个Bean的时候会在实例化前调用postProcessorAfterInitialization方法,对于AOP的逻辑也由此开始:

从动态代理到Spring AOP(中)

3.2 获取所有增强器

  还记得之前的栗子吗?有个类是有@AspectJ注解的AspectJConfig类,这个类里面有@PointCut注解,这个注解的意思是对哪些方法进行增强,这里@Pointcut(“execution(* *.test(..))”)表示要对所有test方法进行增强。通过不同的切点表达函数可以实现对某些你想要类或者方法进行增强。

  那么,什么叫增强器?Spring AOP在JoinPoint“周围”维护一系列的拦截器。有哪些Advice呢?

  • @Before – 在JoinPoint方法之前执行
  • @AfterReturning – 在JoinPoint方法正常执行后执行
  • @AfterThrowing – 在JoinPoint方法抛出异常退出并执行
  • @After – 无论JoinPoint方法正常返回还是异常返回都会执行
  • @Around – 在JoinPoint方法前后执行

  例子中,有2个增强器。那我们看下源码是如何拿到所有的增强器的,看这个方法:org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors

 /**
* Look for AspectJ-annotated aspect beans in the current bean factory,
* and return to a list of Spring AOP Advisors representing them.
* <p>Creates a Spring Advisor for each AspectJ advice method.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
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>();
// 获取容器内所有的beanName
String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
// for循环,找出增强方法
for (String beanName : beanNames) {
// 过滤不合法的bean,子类实现
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this
// case they would be cached by the Spring container but would not
// have been weaved
// 获取到bean类型
Class beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// 如果存在@AspectJ注解
if (this.advisorFactory.isAspect(beanType)) {
// 加入list
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 解析获取增强方法
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 {
// 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.EMPTY_LIST;
}
List<Advisor> advisors = new LinkedList<Advisor>();
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;
}

  到此我们找到了所有声明@AspectJ注解的类,接下来是不是该找用@Before,@After等等那些注解的方法了?

从动态代理到Spring AOP(中)

  等等,我们要的是Advisor,所有增强由Advisor的实现类InstantiationModelAwarePointcutAdvisorImpl封装,不同的注解会封装成不同的增强器。

 public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
/** 检查开始 */
Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
// 之前讲过,获取到方法上的注解信息
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}
/** 检查结束 */ // 根据不同的注解生成不同的增强器
AbstractAspectJAdvice springAdvice;
switch (aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method " + candidateAdviceMethod);
}
// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrderInAspect);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}

3.3 获取匹配的增强器

  前面讲了获取所有的增强器,不一定都适用于现在的bean,我们要选出合适的增强器,也就是满足配置的增强器,具体方法在:org.springframework.aop.support.AopUtils#findAdvisorsThatCanApply:

从动态代理到Spring AOP(中)

3.4 创建代理

  首先看这个方法:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy

从动态代理到Spring AOP(中)

  那么接下来就是代理的创建与获取了:

从动态代理到Spring AOP(中)

  我们先看下如何创建代理的,注释如图:

从动态代理到Spring AOP(中)

  所以我们可以得出以下结论:

  • 如果目标类实现了接口,默认使用JDK动态代理实现AOP,但是也可以强制使用CGLIB实现AOP
  • 如果目标类没有实现接口,那么必须采用CGLIB库

3.5 调用过程  

  最后就是方法调用的关键方法了,可以说最核心的方法就是这个了,org.springframework.aop.framework.ReflectiveMethodInvocation#proceed:

  Spring aop的精华都在于此,核心就是递归思想,调用完了拦截链(所有增强器)中的所有拦截器方法后,再调用目标对象的方法

从动态代理到Spring AOP(中)

  到底invoke方法是啥呢?见下图,我们可以看到是一个接口的方法,不同增强器有不同的实现,我们这里就看Before和After的:

从动态代理到Spring AOP(中)

从动态代理到Spring AOP(中)

从动态代理到Spring AOP(中)

  最后画一张图吧,这样比较好理解一点:

从动态代理到Spring AOP(中)

四.总结

  过程就是先拿出所有适用的Adivsors,然后构造拦截链(chain),最后进行串行调用(递归)。

  最后还是希望多写几个demo来实践一下,打打断点。还是那句话,源码是看不完的,最重要的是思想。这是我第一家公司技术总监老羊说的。我觉得还是挺收益的。本文还有很多不足之处,如果有写的不对的地方还请指点一下,感谢。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,077
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,552
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,401
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,176
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,813
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,896