Spring Aop深度解析(未完...)

Spring Aop深度解析

关于spring Aop也接触了好多知识了,在此,做一个总结,以便学习

Spring Aop是什么?

AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。

基本术语:

(1)切面(Aspect)

切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。 Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。

@Component
@Aspect
public class LogAspect {
}

可以简单地认为, 使用 @Aspect 注解的类就是切面

(2) 目标对象(Target)

目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。

(3) 连接点(JoinPoint)
程序执行过程中明确的点,如方法的调用或特定的异常被抛出。连接点由两个信息确定:

简单来说,连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。

@Before("pointcut()")
public void log(JoinPoint joinPoint) { //这个JoinPoint参数就是连接点
}

(4) 切入点(PointCut)
切入点是对连接点进行拦截的条件定义。切入点表达式如何和连接点匹配是AOP的核心,Spring缺省使用AspectJ切入点语法。
一般认为,所有的方法都可以认为是连接点,但是我们并不希望在所有的方法上都添加通知,而切入点的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配连接点,给满足规则的连接点添加通知。

@Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))")
public void pointcut() {
}

上边切入点的匹配规则是com.remcarpediem.test.aop.service包下的所有类的所有函数。

(5) 通知(Advice)
 通知是指拦截到连接点之后要执行的代码,包括了“around”、“before”和“after”等不同类型的通知。Spring AOP框架以拦截器来实现通知模型,并维护一个以连接点为中心的拦截器链。

// @Before说明这是一个前置通知,log函数中是要前置执行的代码,JoinPoint是连接点,
@Before("pointcut()")
public void log(JoinPoint joinPoint) { 
}

通知方法:

前置通知:在我们执行目标方法之前运行(@Before)
后置通知:在我们目标方法运行结束之后 ,不管有没有异常(@After)
返回通知:在我们的目标方法正常返回值后运行(@AfterReturning)
异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)
环绕通知:动态代理, 需要手动执行joinPoint.procced()(其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知(@Around)

代码展示:

切面类:

package Aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/**
 * @author 孙一鸣 on 2020/2/22
 */
@Aspect
public class LogAspects {
    @Pointcut("execution(public int Aop.Calculator.*(..))")
    public void pointCut(){};

    //@before代表在目标方法执行前切入, 并指定在哪个方法前切入
    @Before("pointCut()")
    public void logStart(){
        System.out.println("除法运行....参数列表是:{}");
    }
    @After("pointCut()")
    public void logEnd(){
        System.out.println("除法结束......");
    }
    @AfterReturning("pointCut()")
    public void logReturn(){
        System.out.println("除法正常返回......运行结果是:{}");
    }
    @AfterThrowing("pointCut()")
    public void logException(){
        System.out.println("运行异常......异常信息是:{}");
    }
    @Around("pointCut()")
    public Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("@Arount:执行目标方法之前...");
        Object obj = proceedingJoinPoint.proceed();//相当于开始调div地
        System.out.println("@Arount:执行目标方法之后...");
        return obj;
    }
    }

业务逻辑方法:

package Aop;

/**
 * @author 孙一鸣 on 2020/2/22
 */
public class Calculator {
    //业务逻辑方法
    public int div(int i, int j) {
        System.out.println("----业务方法:----");
        return i / j;
    }
}

配置bean:

package Aop;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * @author 孙一鸣 on 2020/2/22
 */
@Configuration
@EnableAspectJAutoProxy //开启注解版Aop功能
public class Config1 {
    @Bean
    public Calculator calculator(){
        return new Calculator();
    }

    @Bean
    public LogAspects aspects(){
        return new LogAspects();
    }
}

测试类:

package Aop;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author 孙一鸣 on 2020/2/22
 */
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config1.class);
        Calculator calculator = (Calculator) context.getBean("calculator");
        calculator.div(12,2);
    }
}

结果截图:

在这里插入图片描述

Aop原理研究:

对于Aop的原理,我们应该从Aop的注解 @EnableAspectJAutoProxy来研究。

1.@EnableAspectJAutoProxy是什么?
我们进入这个注解来看:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;
}

我们可以看到注解中要导入一个名为:AspectJAutoProxyRegistrar的组件。
进入这个组件:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    AspectJAutoProxyRegistrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }

        if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }

    }
}

ImportBeanDefinitionRegistrar 这个接口,在以前的学习中使用过,ImportBeanDefinitionRegistrar手动注册bean到容器中,通过@import(HelloBeanRegister.class)使用
在这里插入图片描述
到这里,我们大致梳理一下步骤

  1. 使用注解@EnableAspectJAutoProxy
  2. 注解中import导入组件 AspectJAutoProxyRegistrar
  3. 组件中要注册Bean
  4. 注册了一个什么样的对象呢? AnnotationAwareAspectJAutoProxyCreator.class
  5. 研究这个注解给容器中注册的这个组件的功能?

我们仔细看一下组件AspectJAutoProxyRegistrar是怎样注册Bean?
组件中实现了接口ImportBeanDefinitionRegistrar,定义了注册方法:registerBeanDefinitions(){
}

进入registerBeanDefinitions方法:

第一句:
 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

首先调用了Aop配置工具类里的方法
方法名解释:如果需要的话注册切面注解自动代理创建器

进入AopConfigUtils类

    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
        return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, (Object)null);
    }

    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
       //给容器中注册了一个注解 装配模式下切面自动代理创建器
    }

进入registerOrEscalateApcAsRequired方法:

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator"))
   //首先判断容器中是否含有internalAutoProxyCreator 这个Bean的定义信息 
        {
            BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
            //如果有得到internalAutoProxyCreator这个Bean的信息
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }

            return null;
        } else {//如果容器中没有internalAutoProxyCreator 这个Bean的定义信息 
            RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
            beanDefinition.setSource(source);
            beanDefinition.getPropertyValues().add("order", -2147483648);
            beanDefinition.setRole(2);
		//将这个internalAutoProxyCreator Bean注册进容器       
            registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
            return beanDefinition;
        }
    }

研究这个组件的功能?AnnotationAwareAspectJAutoProxyCreator.class

进入AnnotationAwareAspectJAutoProxyCreator类
继承了AspectJAwareAdvisorAutoProxyCreator

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
类图详细分析:

在这里插入图片描述
到这里,我们大致梳理一下步骤
这是前面的步骤:

  1. 使用注解@EnableAspectJAutoProxy
  2. 注解中import导入组件 AspectJAutoProxyRegistrar
  3. 组件中要注册Bean
  4. 注册了一个什么样的对象呢? AnnotationAwareAspectJAutoProxyCreator.class
  5. 研究这个注解给容器中注册的这个组件的功能?
    ===========================================
    新发现:
  6. 通过类图知道,这个组件具有BeanPostProcessor的特点,也有BeanFactoryAware的特点,说明这个Bean组件AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器,也是一个Aware接口的实现类
分析AnnotationAwareAspectJAutoProxyCreator的关于后置处理器的方法

首先来看AnnotationAwareAspectJAutoProxyCreator 继承的最顶层父类:1.AbstractAutoProxyCreator

在这里插入图片描述
2.AbstractAdvisorAutoProxyCreator
在这里插入图片描述
3.父类:AspectJAwareAdvisorAutoProxyCreator
在这里插入图片描述

4.自己:AnnotationAwareAspectJAutoProxyCreator

在这里插入图片描述

Debug模式学习源码:

创建和注册AnnotationAwareAspectJAutoProxyCreator的过程流程:
1)、传入配置类,创建ioc容器
2)、注册配置类,调用refresh()刷新容器;
3)、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;

  • 1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
    2)、给容器中加别的BeanPostProcessor
    3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
    4)、再给容器中注册实现了Ordered接口的BeanPostProcessor;
    5)、注册没实现优先级接口的BeanPostProcessor;
    6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
    创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
    •     1)、创建Bean的实例
         	2)、populateBean;给bean的各种属性赋值
         	3)、initializeBean:初始化bean;
         			1)、invokeAwareMethods():处理Aware接口的方法回调
         			2)、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
         			3)、invokeInitMethods();执行自定义的初始化方法
         			4)、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();
           4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;--》aspectJAdvisorsBuilder
      
    7)、把BeanPostProcessor注册到BeanFactory中;
    beanFactory.addBeanPostProcessor(postProcessor);

AnnotationAwareAspectJAutoProxyCreator => 后置处理器类型InstantiationAwareBeanPostProcessor
4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean

  • 1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
    getBean->doGetBean()->getSingleton()->
    2)、创建bean
    【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】
    2.1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;
    只要创建好的Bean都会被缓存起来
    2.2)、createBean();创建bean;
    AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例
    【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
    【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】

1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续

1)、后置处理器先尝试返回对象;
	bean = applyBeanPostProcessorsBeforeInstantiation():
拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;
		就执行postProcessBeforeInstantiation
	if (bean != null) {
		bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
						}

2)、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;

AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
1)、每一个bean创建之前,调用postProcessBeforeInstantiation();
关心MathCalculator和LogAspect的创建

  •  1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
    
  •  2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)
    
  •  3)、是否需要跳过
         1)、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
         	每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
       		判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
       	2)、永远返回false
    

2)、创建对象

  • postProcessAfterInitialization;
    在这里插入图片描述
    3)、目标方法执行 ;
    容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
    1)、CglibAopProxy.intercept();拦截目标方法的执行
    2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;

    • List< Object> chain =this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      1)、List< Object> interceptorList保存所有拦截器 5
      一个默认的ExposeInvocationInterceptor 和 4个增强器;
      2)、遍历所有的增强器,将其转为Interceptor;
      registry.getInterceptors(advisor);
      3)、将增强器转为List;
      如果是MethodInterceptor,直接加入到集合中
      如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
      转换完成返回MethodInterceptor数组;

    3)、如果没有拦截器链,直接执行目标方法;
    拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
    4)、如果有拦截器链,把需要执行的目标对象,目标方法,

     	拦截器链等信息传入创建一个 CglibMethodInvocation 对象,
     	并调用 Object retVal =  mi.proceed();
    

    5)、拦截器链的触发过程;

    • 1)、如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
    • 2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
      拦截器链的机制,保证通知方法与目标方法的执行顺序;

总结:

1)@EnableAspectJAutoProxy 开启AOP功能
2) @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
3)AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
4)容器的创建流程:

  • 1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
    2)、finishBeanFactoryInitialization()初始化剩下的单实例bean

    				1)、创建业务逻辑组件和切面组件
    				2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
    				3)、组件创建完之后,判断组件是否需要增强
    			是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);
    

5)执行目标方法:</font>

  • 1)、代理对象执行目标方法
    2)、CglibAopProxy.intercept();

      			1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
      			2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
      			3)、效果:
      		正常执行:前置通知-》目标方法-》后置通知-》返回通知
      		出现异常:前置通知-》目标方法-》后置通知-》异常通知
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值