简介
- 面向切面编程,是面向对象编程的重要组成部分,在不改变业务逻辑功能的基础上,对横切逻辑进行扩展
添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
通知
-
@Before
(前置通知):在业务方法执行之前调用 -
@After
(后置通知):在方法之后执行 -
@AfterReturning
(正常返回通知):在方法之后执行,只有在业务方法没有出现异常的时候才会执行 -
@AfterThrowing
(异常通知) : 在方法之后执行,只有在业务方法出现异常的时候才会执行 -
@Around
(环绕通知):在业务方法执行之前和之后执行
连接点
- 业务层的所有方法,叫做连接点
- 业务类中可以被增强的方法都叫做连接点
切点
- 能切入切面逻辑的方法,叫做切点
- 实际被增强的方法叫做切入点 ,其他的那些没有被增强的方法(连接点)不是切点
切面
- 定义了增强方法的类就叫做切面
实现
- 在配置类上开启切面,使用
@EnableAspectJAutoProxy
@Configuration
@ComponentScan(value = {"cn.tedu.demo"})
@EnableAsync
@EnableAspectJAutoProxy
public class FirstConfig {}
- 使用五个不同的通知完成切入:
/**
* 切面,使用@Aspect标注
*/
@Component
@Aspect
public class CustomAspect {
/**
* 使用@PointCut定义切入点
*/
@Pointcut(value = "execution(* cn.tedu.demo.aspect.AspectInvok.invok(..))")
public void pointCut(){}
/**
* 前置通知,在指定方法之前执行
* @param point JoinPoint对象,可以获取切点的各种属性,比如入参的参数等
*/
@Before(value = "pointCut()")
public void before(JoinPoint point){
//获取入参的参数的值
Object[] args = point.getArgs();
System.out.println(Arrays.asList(args));
//获取MethodSignature,其中可以获取切点的各种属性,比如方法返回类型,参数等等
MethodSignature signature = (MethodSignature) point.getSignature();
String[] parameterNames = signature.getParameterNames();
System.out.println(Arrays.asList(parameterNames));
System.out.println("在方法之前执行");
}
/**
* 在切点之后执行
* @param point JoinPoint对象
*/
@After(value = "pointCut()",argNames = "point")
public void after(JoinPoint point){
System.out.println("在方法之后执行");
}
/**
* 在方法前后都会执行
* @param point
*/
@Around(value = "pointCut()")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("前置执行");
//执行方法,可以获取返回值,否则方法将不会执行
Object result = point.proceed(point.getArgs());
System.out.println("后置执行,执行的结果=="+result);
}
/**
* 正常返回通知,
* @param point Joinpoint对象
* @param result 方法执行返回的结果,需要和@AfterReturning注解中returning中的属性值相同,否则不能自动装配
*/
@AfterReturning(value = "pointCut()",returning = "result")
public void afterReturning(JoinPoint point,Object result){
System.out.println("正常返回执行,执行的结果为:"+result);
}
/**
* 异常返回执行,程序出现异常了才会执行
* @param point
* @param ex 切入点执行抛出的异常,需要和@AfterThrowing注解的throwing值相同,否则不能完成自动装配
*/
@AfterThrowing(value = "pointCut()",throwing = "ex")
public void afterThrowing(JoinPoint point,Exception ex){
System.out.println("异常返回执行,执行的异常为:"+ex);
}
}
注解的实现
- 详细请看我的上一篇博客
- 定义一个注解,如下:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
String name() default "陈加兵";
int age() default 22;
}
- 定义一个切面,使用注解如下:
@Component
@Aspect
public class CustomAspect {
/**
* 使用@PointCut定义切入点
*/
@Pointcut(value = "@annotation(cn.tedu.demo.aspect.MyAnnotation)")
public void pointCut(){}
/**
* 前置通知,在指定方法之前执行
* @param point JoinPoint对象,可以获取切点的各种属性,比如入参的参数等
*/
@Before(value = "pointCut()")
public void before(JoinPoint point){
//获取MethodSignature,其中可以获取切点的各种属性,比如方法返回类型,参数等等
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
//获取方法上指定的注解
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
System.out.println("name="+myAnnotation.name());
System.out.println("在方法之前执行");
}
}
源码解析
@EnableAspectJAutoProxy
- 从
@EnableAspectJAutoProxy
该注解中可以看出使用了@Import(AspectJAutoProxyRegistrar.class)
,因此实际作用的类就是AspectJAutoProxyRegistrar
,因此我们必须跟进源码去看。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
AspectJAutoProxyRegistrar
- 该类实现
ImportBeanDefinitionRegistrar
【向容器中注入Bean】。 - 该类的主要作用就是向ioc容器中注入
AnnotationAutoProxyCreator
。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* 向容器中注入AnnotationAutoProxyCreator
*/
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//调用方法注册AnnotationAutoProxyCreator
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
//获取@EnableAspectJAutoProxy注解中两个属性的值
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
//判断注解属性的值
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
AnnotationAutoProxyCreator【创建代理】
概念
- advisor(增强器):实现Advisor接口,简单的说一个advisor存储的就是被五个通知的标注的方法的信息。
- advisedBeans:
Map<Object, Boolean>
类型,key是Bean,value存储的是这个Bean是否需要被增强器(通知方法)增强
分析
-
@EnableAspectJAutoProxy
注解的主要作用就是向容器中注入AnnotationAutoProxyCreator
,ID为org.springframework.aop.config.internalAutoProxyCreator
,因此切面的关键类就是这个,我们看看继承关系图,如下:
[图片上传失败...(image-e8798a-1562336107568)]
- 从继承关系中可以看到该类及其父类实现了两个重要的接口,如下:
-
InstantiationAwareBeanPostProcessor
:spring的后置处理器,在实例化前后,初始化前后执行 -
BeanFactoryAware
:自动注入BeanFactory
-
- 通过上面的分析可以知道,AOP的主要功能必定是在
AnnotationAutoProxyCreator
这个Bean的初始化和实例化前后执行逻辑,无非就是上面两个接口定义的方法,我们可以在对应的方法中打断点跟踪代码。
代码跟踪
-
刷新容器种执行
registerBeanPostProcessors(beanFactory)
方法将全部的后置处理器注入到容器中,在其中执行执行后续的setBeanFactory的方法-
根据Bean的生命周期我们可以判断最新执行的一定是实现Aware方法的setBeanFactory方法【在invokeAwareMethods()方法中执行】,实际执行也是符合我们的猜想,第一个执行的方法就是
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#setBeanFactory
- 创建
AspectJAdvisorFactory
:顾名思义,当然是从标注@Aspect的类中
创建增强器【切面增强的方法】 - 创建
BeanFactoryAspectJAdvisorsBuilder
:简单的说就是创建增强器的辅助类,用来构造增强器
- 创建
@Override public void setBeanFactory(BeanFactory beanFactory) { //调用父类的方法 super.setBeanFactory(beanFactory); if (!(beanFactory instanceof ConfigurableListableBeanFactory)) { throw new IllegalArgumentException( "AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory); } //实际执行的是initBeanFactory initBeanFactory((ConfigurableListableBeanFactory) beanFactory); } /*******************************initBeanFactory**************************/ @Override protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.initBeanFactory(beanFactory); //创建切面增强器工厂 if (this.aspectJAdvisorFactory == null) { this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory); } //创建切面增强生成器 this.aspectJAdvisorsBuilder = new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory); }
-
根据Bean的生命周期我们可以判断最新执行的一定是实现Aware方法的setBeanFactory方法【在invokeAwareMethods()方法中执行】,实际执行也是符合我们的猜想,第一个执行的方法就是
-
刷新容器方法中执行
finishBeanFactoryInitialization(beanFactory)
【实例化剩余的单例Bean】-
getBean()--->doGetBean--->createBean,在createBean方法中执行
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
【在实例化之前给一个机会返回一个代理】-
内部执行
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
-
内部循环遍历后置处理器,判断类型为
InstantiationAwareBeanPostProcessor
,执行postProcessBeforeInstantiation()
方法【在实例化之前调用,接口定义】。- 此时执行到
AbstractAutoProxyCreator#postProcessBeforeInstantiation
,源码如下:- 这个方法的主要作用就是查找切面类和已经有了TargetSource的类,这些类都是不需要增强的,设置adviseBean的value为false,后续就不需要创建代理对象了
/** * 在实例化之前执行逻辑,如果返回不为null,后续的属性赋值等操作将不会执行 * 主要的作用 */ public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) { //获取cacheKey,主要是和FactoryBean对象区别 Object cacheKey = getCacheKey(beanClass, beanName); //beanName为空,或者targetSourcedBeans不包含当前Bean if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { //如果需要增强的Bean已经包含了当前Bean,后续流程不需要执行,返回null即可 if (this.advisedBeans.containsKey(cacheKey)) { return null; } /**isInfrastructureClass(beanClass):【如果当前Bean是基础的类【实现了Advice、Pointcut、Advisor、AopInfrastructureBean】并且满足this.aspectJAdvisorFactory不为null&&当前类被@Aspect注解标注了】 *shouldSkip(beanClass, beanName): * ①获取所有的候选的增强器【List<Advisor> candidateAdvisors = findCandidateAdvisors();】 * ②循环候选增强器,判断类型是否是AspectJPointcutAdvisor */ if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { //如果满足条件,那么表示该类是一个切面,不是需要增强的Bean,设置value为false this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } //获取自定义的TargetSource 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; } return null; }
- 代码继续执行,发现
postProcessProperties
、postProcessBeforeInitialization
、postProcessBeforeInitialization
都是默认的实现,这里跳过。 - 真正创建代理对象的是在
postProcessAfterInitialization
中,逻辑如下:- ①判断bean是否为null和earlyProxyReferences中是否存在当前的Bean
- ②如果满足条件,调用
wrapIfNecessary(bean, beanName, cacheKey)
方法,封装Bean,其实就是创建代理对象 - ③进入
wrapIfNecessary
方法- ①做一些判断,包括是否是targetSourcedBeans是否包含,是否是基础类,是否应该跳过,是否adviseBean中的value为false【不需要增强的Bean】,满足以上条件的都直接返回bean,不需要创建代理
- ②调用方法获取能够作用于当前Bean的增强器(通知方法集合)封装在
specificInterceptors
,代码跟进,最重要的实现是在org.springframework.aop.support.AopUtils.canApply(org.springframework.aop.Advisor, java.lang.Class<?>, boolean)
这个方法中,大致的思想就是 - ③判断specificInterceptors是否为null,如果为空,表示当前Bean不需要增强,设置adviseBean的值为false,反之需要增强,此时就需要创建代理了,此时主要调用的是
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean))
- ④ 进入
createProxy
方法- ① 创建ProxyFactory,主要作用就是创建代理的工厂,其中封装了Advisor,targetSource,getProxy()【创建代理】方法等
- ② 判断是否应该直接代理目标类【if (!proxyFactory.isProxyTargetClass())】
- ③ 调用
evaluateProxyInterfaces(beanClass, proxyFactory);
方法,主要的作用就是检查当前Bean实现的接口是否符合要求能够创建代理对象【不能是InitializingBean
,DisposableBean
,Closeable
,AutoCloseable
等接口】,如果满足直接设置到ProxyFactory中,否则设置proxyTargetClass=true
【直接代理目标类targetSource】 - ④ 调用
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors)
方法,将公共的拦截器和适配的拦截器specificInterceptors
一起返回- ① 根据指定的
this.interceptorNames
拦截器名称从容器中获取Bean,将其解析成commonInterceptors
- ② 遍历commonInterceptors和specificInterceptors将其封装成Advisor数组返回
- ① 根据指定的
- ⑤ 将advisor和targetSource添加到ProxyFactory中
- ⑥ 调用
proxyFactory.getProxy(getProxyClassLoader())
方法,获取代理对象- ① 最终调用的是
org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy
的方法创建代理对象,是AopProxyFactory默认实现类中的方法,具体逻辑如下:- 如果targetClass是接口或者继承Proxy使用JDK的动态代理,否则使用Cglib代理
- ① 最终调用的是
- ⑤ 将创建的代理对象proxy存放在
proxyTypes
中,返回proxy - ⑥ 至此代理对象创建完成
- 此时执行到
-
-
-
代理执行
- 上面解释了代理对象的创建流程,下面就开始真正的执行和增强的流程了,需要了解到五种通知类型在什么时候执行,执行的顺序是什么?
- 未完待续。。。。。
网友评论