当前位置: 移动技术网 > IT编程>开发语言>Java > Spring复习--IoC,AOP以及循环依赖

Spring复习--IoC,AOP以及循环依赖

2020年07月22日  | 移动技术网IT编程  | 我要评论

Spring

概述

参考Spring复习

Spring 是什么

​ Spring 是一个轻量级的 IoC 和 AOP 容器框架,是为 Java 应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于 XML 的配置,基于注解的配置,基于 Java 的配置;


Spring 的优点

  • 方便解耦,简化开发 ;

  • Spring 的 DI(依赖注入) 机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;

  • Spring 提供了 AOP 技术,支持将一些通用任务,如安全,事务,日志,权限等进行集中式管理,从而提供更好的复用;

  • Spring 对于主流的应用框架提供了集成支持;


Spring 的 IoC

IoC 就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到 Spring 容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。

DI(Dependency Injection) 依赖注入,和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖 IoC 容器来动态注入对象需要的外部资源;也就是说,我们的程序在编写时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况,那这种依赖关系(如业务层和持久层直接的依赖关系),在使用 Spring 之后,就让 Spring 来维护了。


BeanFactory 和 ApplicationContext

BeanFactory 和 ApplicationContext 是 Spring 的两大核心接口,都可以当做 Spring 的容器;其中 ApplicationContext 是BeanFactory 的子接口;

  • BeanFactory
    • 提供了 IoC 容器应遵守的的最基本的接口;
    • 是 Spring 里面最底层的接口,包含了各种 bean 的定义,读取 bean 配置文档,管理 bean 的加载,实例化,控制 bean 的生命周期,维护 bean 之间的依赖关系 等等;
    • BeanFactroy 采用的是延迟加载形式来注入 Bean 的,即只有在使用到某个 Bean 时(调用getBean()),才对该 Bean 进行加载实例化;
    • 只是个接口,并不是 IoC 容器的具体实现,但是 Spring 容器给出了很多种实现,而 ApplicationContext 就是 BeanFactory 的一个子接口;
  • ApplicationContext
    • ApplicationContext 是 BeanFactory 的子接口,除了提供 BeanFactory 所具有的功能外,还提供了更完整的框架功能;
    • 它是在容器启动时,一次性创建了所有的 Bean;
    • ApplicationContext 下主要有三个实现类:ClassPathXmlApplicationContext(从类的根路径下加载配置文件),FileSystemXmlApplicationContext(从磁盘路径上加载配置文件),AnnotationConfigApplicationContext(使用注解配置容器对象时,需要使用此类来创建 Spring 容器)

Bean 的作用域

Spring 容器中的 bean 可以分为5个范围:

  • singleton(单例):默认,每个容器中只有一个 bean 的实例,单例的模式由 BeanFactory 自身来维护;
  • prototype(原型):为每一个 bean 请求提供一个实例;
  • request(请求):为每一个网络请求创建一个实例,在请求完成以后,bean 会失效并被垃圾回收器回收;
  • session(会话):与 request 范围类似,确保每个 session 中有一个 bean 的实例,在 session 过期后,bean会随之失效;
  • global-session:全局作用域,WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么 global Session 相当于 session;

xml的注入方式

setter 方法注入,构造器注入,静态工厂注入,实例工厂注入;

  • setter 方法注入

    public class Person{
        private String name;
        private int age;
        public Person(){}
        public Person(String name,String age){
            this.name = name;
            this.age = age;
        }
        public setName(String name){
            this.name = name;
        }
        public setAge(int age){
            this.age = age;
        }
        public getName(){
            return this.name;
        }
        public getAge(){
            return this.age;
        }
    }
    

    xml 文件

    <bean id="person" class="com.qixia.test.Person">
    	<property name="name" value="Qixia"></property> 
        <property name="age" value="18"></property> 
    </bean>
    
  • 构造器注入

    无参

    <bean id="Person" class="com.qixia.test.Person"></bean> 
    

    有参

    index 用来指定有参构造器中的参数位置,从 0 开始;

    <bean id="Person" class="com.qixia.test.Person">
    	<constructor-arg index="0" value="Qixia"></constructor-arg>
        <constructor-arg index="1" value="18"></constructor-arg>
    </bean> 
    
  • 静态工厂

    public class DaoFactory { 
    	//静态工厂 
    	public static final FactoryDao getStaticFactoryDaoImpl(){ 
        	return new StaticFacotryDaoImpl(); 
        } 
    }
    

    xml

    factory-method 指定生产对 id 象的静态方法 ;

    <bean id="daoFactory" class="com.qixia.test.DaoFactory" factory-method="createAccountService">
    </bean>
    
  • 实例工厂

    public class InstanceFactory {   
        public IAccountService createAccountService(){   
            return new AccountServiceImpl();  
        } 
    }
    

    xml

    factory-bean:用于指定实例工厂 bean 的 id;

    factory-method 属性:用于指定实例工厂中创建对象的方法;

    <bean id="instanceFactory" class="com.qixia.test.InstanceFactoryInstanceFactory"></bean>
    <!-- 先把工厂的创建交给Spring来管理
    然后在使用工厂的 bean 来调用里面的方法
    -->
    <bean id="factory" factory-bean="instancFactory" factory-method="createAccountService"></bean>
    

Bean 的生命周期

声明:使用注解方式注入一个 bean 实例;

  1. Bean实例化主要是在 refresh() 方法中的 finishBeanFactoryInitialization方法中进行 bean 的创建以及属性注入和初始化 ):
    1. 调用beanFactory.preInstantiateSingletons()初始化所有剩余的(非延迟)单例
    2. 在上述preInstantiateSingletons方法中,会判断当前的Bean是否是FactoryBean(工厂Bean)中的,如果不是就会继续调用getBean(beanName)方法和doGetBean方法继续创建;
    3. 接着会调用createBean方法—>doCreateBean—>createBeanInstance最终在createBeanInstance方法中会根据工厂方法或确定对象的构造器创建出Bean实例;
  2. Bean的属性注入
    1. 在doCreateBean方法中会调用populateBean(beanName, mbd, instanceWrapper)方法来进行Bean的属性赋值;
  3. Bean的初始化
    1. initializeBean(beanName, exposedObject, mbd)方法中进行bean的初始化;
    2. 会先执行BeanNameAware的setBeanName方法:Spring将Bean的Id传递给setBeanName()方法;
    3. BeanClassLoaderAware的setBeanClassLoader方法:在普通属性设置之后,InitializingBean.afterPropertiesSet()之前调用;
    4. BeanFactoryAware的setBeanFactory方法:Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
    5. 调用ApplicationContextAware的setApplicationContext方法,将上下文引用传入;
    6. 如果实现了BeanPostProcessor接口就会执行 postProcessBeforeInitialization方法;
    7. 检查Bean 是否实现了InitializingBean接口,如果是,Spring将调用他们的afterPropertiesSet()方法;
    8. 检查是否配有自定义的init-method方法(自定义初始化方法),如果有,则调用自定义初始化方法;
    9. 如果实现了BeanPostProcessor接口就会执行后置处理方法,beanProcessor.postProcessAfterInitialization(result, beanName)方法;
    10. 在初始化的最后会调用registerDisposableBeanIfNecessary方法来注册Bean的销毁方法;
  4. 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁;
  5. 调用DisposableBean的destory方法:在调用了close()方法后就会去执行销毁方法。

总结

  1. Bean的实例化;
  2. Bean的属性注入;
  3. 检查Aware相关接口并设置相关依赖(BeanNameWare,BeanFactoryWare,ApplicationContextWare);
  4. 如果实现BeanPostProcessor接口就会调用前置处理方法(postProcessBeforeInitialization方法);
  5. 检查是否实现了InitializingBean接口,以决定是否调用afterPropertiesSet()方法;
  6. 检查是否配有自定义的init-method(命名为init的方法)方法(自定义初始化方法),以决定是否调用自定义初始化方法;
  7. 如果实现BeanPostProcessor接口就会调用后置处理方法(postProcessAfterInitialization方法);
  8. Bean已经准备就绪或被我们使用;
  9. 判断是否实现DisposableBean接口,以决定是否调用destory()方法,如果 Bean 中也有自定义的destroy-method(命名为destroy的方法)方法,该访法也会被调用;

在这里插入图片描述


BeanFactory 和 FactoryBean

  • BeanFactory 是 Spring 中最底层的接口,它主要是用于 bean 实例的定义,读取 bean 的配置文件,管理 bean 实例和 bean 的生命周期,维护 bean 之间的依赖关系;
  • 而 FactoryBean 是一个能生产或修饰对象生成的工厂 bean ,用户可以通过实现该接口定制实例化 bean 的逻辑

单例bean是否是线程安全的

Spring 框架并没有对单例 bean 进行任何多线程的封装处理;对于单例 bean,所有线程都共享一个单例实例 bean,因此是存在资源的竞争

如果单例 bean 是一个无状态 bean(调用 bean 的方法不会使 bean 的字段属性发生改变),也就是线程中的操作不会对 bean 的成员执行查询以外的操作,那么这个单例 bean 是线程安全的,比如 SpringMVC 的 Controller,Service,Dao 等类,这些 bean 大多是无状态的,只关注于方法本身

最浅显的解决办法就是将多态 bean 的作用域由 singleton 变更为 prototype

对于原型(prototype) bean,每次创建一个新对象,也就是线程之间并不存在 bean 共享,自然是不会有线程安全的问题

注意:Spring 容器本身并没有提供线程安全的策略,因此是否线程安全完全取决于 Bean 本身的特性!


循环依赖及如何解决

循环依赖

循环依赖其实就是循环引用,也就是两个或则两个以上的 bean 互相持有对方,最终形成闭环。比如 A 依赖于 B,B 依赖于 C,C 又依赖于 A;如下图所示:

在这里插入图片描述

Spring 中循环依赖的场景有:

  • 全部的构造器循环依赖(无法解决)
  • 属性的循环依赖

什么情况下循环依赖可以被处理?

  • 出现循环依赖的 Bean 必须要是单例;
  • 依赖注入的方式不能全是构造器注入的方式;(一个使用构造器注入,一个使用 setter 注入,这种情况是可以解决的)

注意:“全部的”构造器的循环依赖是无法被解决的,只能拋出 BeanCurrentlyInCreationException 异常


假设,有类 A 和 B,A 中通过 setter 注入了 B,而 B 中通过 setter 注入了 A

首先,创建 bean 实例主要经过以下三步:

  1. 实例化 bean: AbstractAutowireCapableBeanFactory 中的 createBeanInstance 方法;
  2. 属性注入:AbstractAutowireCapableBeanFactory 中的 populateBean 方法;
  3. 初始化:AbstractAutowireCapableBeanFactory 中的 initializeBean 方法;

Spring 为了解决单例的循环依赖问题,使用了三级缓存,并且这三级缓存是在 方法 getBean 中的:

  • singletonObjects:一级缓存,存储的是所有创建好了的单例 Bean;
  • earlySingletonObjects:早期曝光对象;
  • singletonFactories:早期曝光对象工厂,二级缓存中存储的就是从这个工厂中获取到的对象

setter 依赖流程

因为 A 是第一次被创建,所以不管哪个缓存中必然都是没有的,因此会进入 doGetBean 方法的 getSingleton 方法的另外一个重载方法 getSingleton(beanName, singletonFactory) 中;

在 getSingleton 该方法中会通过 createBean 方法去创建 A 的实例 bean;并且在未完全初始化好该 bean 的情况下直接暴露在三级缓存 singletonFactories 之中;(注意:在存入三级缓存之前会先调用 getEarlyBeanReference 方法去暴露该 bean 实例,但如果不考虑 AOP 的情况下,那么就可以出是直接返回了一个之前创建的不完全的实例对象 bean)

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

当 A 完成了实例化并添加进了三级缓存后,就要开始为 A 进行属性注入了,在注入时发现 A 依赖了 B,那么这个时候 Spring 又会去 getBean(b),然后反射调用 setter 方法完成属性注入;

因为 B 需要注入 A,所以在创建 B 的时候,又会去调用 getBean(a),这个时候就又回到之前的流程了,但是不同的是,之前的 getBean 是为了创建 Bean,而此时再调用 getBean 不是为了创建了,而是要从缓存中获取,由于之前 A 在实例化后已经将其放入了三级缓存 singletonFactories 中,所以此时 getBean(a) 的流程就是如下的过程,第一步,先获取到三级缓存中的工厂,第二步,调用对象工厂的 getObject 方法来获取到对应的对象,得到这个对象后将其注入到B中

在这里插入图片描述

B 拿到 A 对象实例后顺利完成了创建对象实例后的属性注入以及初始化过程,并且完全初始化之后将自己放入到一级缓存 singletonObjects 中

此时再返回 A 的属性注入的方法中,A 就能够顺利的拿到 B 的实例并且完成 A 的属性注入以及初始化等方法,最终 A 也存入了一级缓存 singletonObjects 中


知道这些流程之后,我们就能够知道,A 的构造方法中依赖了 B 的实例对象,同时 B 的构造方法中依赖了 A 的实例对象的这种情况下是无法完成依赖注入的,这就是因为,加入 singletonFactories 三级缓存的前提是使用 createBeanInstance 方法调用构造器创建了实例对象!!


结合 AOP 循环依赖

在普通的循环依赖的情况下,三级缓存没有任何作用,三级缓存实际上跟 Spring 中的 AOP 相关,在 AOP 下继续观察方法 getEarlyBeanReference:

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

如果在开启 AOP 的情况下,那么就是调用到 AnnotationAwareAspectJAutoProxyCreatorgetEarlyBeanReference 方法,对应的源码如下:

	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

这样就可以看出对 A 进行了 AOP 代理的话,那么此时 getEarlyBeanReference返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着 B 中注入的 A 将是一个代理对象而不是 A 的实例化阶段创建后的对象


Spring AOP

AOP:面向切面编程(Aspect-Oriented Programming),可以说是对 OOP 的一种补充,专门用于处理一些具有横切性质的服务。指在程序运行期间动态的将某段代码切入到指定方法指定位置并进行运行的编程方式;

用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect);


主要名词

  • 切面(Aspect)被抽取的公共模块,可能会横切多个对象。 在 Spring AOP 中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect(告诉Spring该类是一个切面类) 注解来实现;
  • 连接点(Join point):指目标类中的方法,在 Spring AOP 中,一个连接点总是代表一个方法的执行;
  • 通知(Advice)在切面的某个特定的连接点(Join point)上执行的动作。通知,有各种类型,其中包括 “around”,“before” 和 “after” 等通知。在 Spring 中,是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链;
  • 切入点(Pointcut)切入点是指我们要对哪些 Join point 进行拦截的定义。**通过切入点表达式,指定拦截的方法,比如指定拦截 add * ,search *** ;
  • 目标对象(Target Object): 被一个或者多个切面(Aspect)所通知(Advice)的对象。也有人把它叫做被通知(Adviced) 对象。 既然 Spring AOP 是通过运行时代理实现的,这个对象永远是一个被代理(proxied) 对象;
  • 织入(Weaving):指把增强(可以理解为Advice)应用到目标对象来创建新的代理对象的过程。Spring 是在运行时完成织入;

通知有哪些?

  • 前置通知(@Before):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常);
  • 后置通知(@After):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出);
  • 返回通知(@AfterReturning):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回;
  • 异常通知(@AfterThrowing):在方法抛出异常退出时执行的通知;
  • 环绕通知(@Around):包围一个连接点(join point)的通知,如方法调用;环绕通知可以在方法调用前后完成自定义的行为,它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行;

Spring AOP 原理及源码(涉及Bean的创建)

如何有效的查看源码:给容器中注入了什么组件(对象实例),该组件什么时候工作,该组件的功能是什么?

声明:使用注解 @EnableAspectJAutoProxy 开启 AOP 功能,而不是 xml 形式。


AnnotationAwareAspectJAutoProxyCreator创建

  1. 首先在该注解中会导入 AspectJAutoProxyRegistrar(实现接口 ImportBeanDefinitionRegistrar ) 类给容器中注册一个 AnnotationAwareAspectJAutoProxyCreator 实例,该实例就是基于注解进行 AOP 代理对象的生成器

    AnnotationAwareAspectJAutoProxyCreator 类有如下继承关系;我们可以清楚地发现,它实现了 InstantiationAwareBeanPostProcessor接口,而该接口继承于 BeanPostProcessor 接口,说明 Spring AOP 是使用 BeanPostProcessor 来做对连接点的拦击和处理的

在这里插入图片描述

当然,目前的 AnnotationAwareAspectJAutoProxyCreator 类肯定不能完成 AOP 的所有逻辑,那么肯定会在其父类中有所实现,那么就需要去关注该类的父类以及该父类的其它子类;

  1. 在我们启动容器时进行容器的加载与创建时,就会来到 AnnotationConfigApplicationContext 类中的方法 refresh() 方法;而该方法就是用来创建整个 IoC 容器的方法;由于 Spring AOP 的实现是使用 BeanPostProcessor 接口,那么在 refresh() 方法中就会调用方法 registerBeanPostProcessors(beanFactory) 来对容器中的 BeanPostProceeanssor 的实现类进行一个注册加载(创建对应的实现类对象实例)

  2. 由于 AnnotationAwareAspectJAutoProxyCreator 实现了接口 Ordered,因此会在 registerBeanPostProcessors 中的第二个位置被注册(第一个注册是实现了PriorityOrdered接口的BeanPostProcessors,第二个是实现了Ordered,最后一个注册就是常规的BeanPostProcessors实现类),第一次注册时是没有 AnnotationAwareAspectJAutoProxyCreator 实例的,因此就直接去创建一个 AnnotationAwareAspectJAutoProxyCreator 的实例对象;

  3. 接着在创建实例后(也就是一直调用到 doCreateBean方法),就会进行初始化 bean 时,也就是方法 initializeBean;

  4. 该方法中也调用了关于 Ware 接口的设置方法(如果实现了BeanNameWare,BeanFactoryWare,ApplicationContextWare接口),调用 invokeAwareMethods(beanName, bean) 执行这些接口中的方法,其中在 AOP 中就会将 BeanFactory 注入;

  5. 然后继续执行 BeanPostProcessor 的 前置处理方法和后置处理方法;

  6. 该方法中有方法 applyBeanPostProcessorsBeforeInitialization ,该方法就是获取到容器中所有的 BeanPostProcessor 的实现类,然后使用 for 循环逐个的调用 BeanPostProcessor 接口中的 postProcessBeforeInitialization方法;对应的就会在applyBeanPostProcessorsAfterInitialization 方法中执行 postProcessAfterInitialization 方法;

  7. 最终就会创建出 AnnotationAwareAspectJAutoProxyCreator 对象的实列;(注意:目前创建的对象实例属于切面的对象实例,只有当我们执行连接点时,才会使用目前创建出的实例调用对应的通知方法)


创建代理对象

需要注意的是:AnnotationAwareAspectJAutoProxyCreator 实现的是 BeanPostProcessor 的子类 InstantiationAwareBeanPostProcessor 接口,而该接口中的前置处理方法和后置处理方法分别是:postProcessBeforeInstantiation 和 postProcessAfterInstantiation,这两个方法和 BeanPostProcessor 中的方法名称不一样;

  1. 主要在 refresh() 方法中的 finishBeanFactoryInitialization 方法中进行容器中剩余的所有的 bean 实例的创建(如果之前创建过那么直接返回,否则就逐个遍历并创建);

  2. 在方法 createBean 中会调用方法 resolveBeforeInstantiation ,而该方法发就是使用 BeanProstProcessor 返回一个代理对象(因为 AOP 使用的就是代理模式),如果没有就继续之前的创建 bean 实例的流程;

  3. resolveBeforeInstantiation 方法中就会有如下代码:

    if (targetType != null) {
    	bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
    	if (bean != null) {
    		bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
    	}
    }
    

    在 applyBeanPostProcessorsBeforeInstantiation 方法中就会有如下代码:

    for (BeanPostProcessor bp : getBeanPostProcessors()) {
    	if (bp instanceof InstantiationAwareBeanPostProcessor) {
    		InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
    		Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
    		if (result != null) {
    			return result;
    		}
    	}
    }
    

    for 循环逐个遍历哪个类实现了 InstantiationAwareBeanPostProcessor 接口,很显然,之前我们也看见 AnnotationAwareAspectJAutoProxyCreator 的父类就实现了该接口,因此 Spring AOP 就是利用这一点来拦截容器中的所有bean 并获取 AOP 中的实现的 postProcessBeforeInstantiation 方法(如果有就获取并执行);

  4. 接着就会在 AnnotationAwareAspectJAutoProxyCreator 的父类 AbstractAutoProxyCreator 类中使用 postProcessBeforeInstantiation 方法进行 bean 的拦截,并且判断 bean 类型,是否是切面或者是否是目标对象,最后返回对象;

    注意:第一次创建目标对象时,该 bean 什么都没有(初始化尚未结束),因此返回的就是一个null!!

    @Override
    	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {//将拦截的 bean 进行获取
    		Object cacheKey = getCacheKey(beanClass, beanName);                                  
    		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
    			//判断拦截的bean是否是“增强的bean”,即,需要AOP的业务类,也就是目标对象
                //第一次时,该bean不是被增强的
                if (this.advisedBeans.containsKey(cacheKey)) {
    				return null;
    			}
    /*isInfrastructureClass方法 判断是否实现了Advisor,Pointcut,AopInfrastructureBean接口
    或者判断该bean是否是切面
    */
                /*
                shouldSkip方法,判断是否直接跳过
                该方法中会去获取切面中的所有的通知方法【List<Advisor> candidateAdvisors】
                由于方法中会判断通知Advice是否是 AspectJPointcutAdvisor 类型的,如果是就返回true,
                否则就返回false;
                */
    			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
    				this.advisedBeans.put(cacheKey, Boolean.FALSE);
    				return null;
    			}
    		}
    		// Create proxy here if we have a custom TargetSource.
    		// Suppresses unnecessary default instantiation of the target bean:
    		// The TargetSource will handle target instances in a custom fashion.
    		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;
    	}
    
  5. 然后执行方法 postProcessAfterInitialization ,注意:此时执行 postProcessAfterInitialization 方法时,此时已经创建了 bean 的实列(因为该方法就是 bean 的后置处理方法);而在该方法中调用方法 wrapIfNecessary ,该方法就是判断是否需要给目前的 bean 实列进行一个包装(包装通知方法,即包装增强器 Advice)

    1. getAdvicesAndAdvisorsForBean 方法用来查找目标对象bean的所有候选的增强器,并且获取能够使用的增强器;
    2. 然后会对获取的增强器进行排序;
    3. 创建当前目标对象的代理对象 (createProxy 方法);
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
    			return bean;
    		}
        //判断是否是目标对象
    		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
    			return bean;
    		}
        //判断是否是目标对象,经过这几步,如果是目标对象,也就是需要代理的类,
        //那么就进行下面的包装;
    		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
    			this.advisedBeans.put(cacheKey, Boolean.FALSE);
    			return bean;
    		}
    
    		// Create proxy if we have advice.
    /*
    getAdvicesAndAdvisorsForBean 方法用来查找目标对象bean的所有的增强器,并且将能够
    使用的增强器加入该bean实例中;
    */
        //Object[] specificInterceptors 存储了所有可用的增强器
    		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    		if (specificInterceptors != DO_NOT_PROXY) {
                //保存当前目标对象bena
    			this.advisedBeans.put(cacheKey, Boolean.TRUE);
             //创建代理对象
    			Object proxy = createProxy(
    					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    			this.proxyTypes.put(cacheKey, proxy.getClass());
    			return proxy;
    		}
    
    		this.advisedBeans.put(cacheKey, Boolean.FALSE);
    		return bean;
    	}
    
  6. 方法 createProxy 创建目标对象的代理对象;

    1. 获取所有的增强器,即通知方法;
    2. 将这些方法保存到代理工厂中;
    3. 使用代理工厂创建目标方法的代理对象;
    	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
    			@Nullable Object[] specificInterceptors, TargetSource targetSource) {
    
    		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
    			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    		}
    		//创建代理工厂
    		ProxyFactory proxyFactory = new ProxyFactory();
    		proxyFactory.copyFrom(this);
    
    		if (!proxyFactory.isProxyTargetClass()) {
    			if (shouldProxyTargetClass(beanClass, beanName)) {
    				proxyFactory.setProxyTargetClass(true);
    			}
    			else {
    				evaluateProxyInterfaces(beanClass, proxyFactory);
    			}
    		}
    		//获取所有的增强器,即通知方法
    		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
            //将这些方法保存到代理工厂中
    		proxyFactory.addAdvisors(advisors);
    		proxyFactory.setTargetSource(targetSource);
    		customizeProxyFactory(proxyFactory);
    
    		proxyFactory.setFrozen(this.freezeProxy);
    		if (advisorsPreFiltered()) {
    			proxyFactory.setPreFiltered(true);
    		}
    		//使用代理工厂创建目标方法的代理对象
    		return proxyFactory.getProxy(getProxyClassLoader());
    	}
    
    1. 方法 proxyFactory.getProxy 创建目标对象的代理对象;

      判断是否是 JDK 动态代理对象,还是 Cglib 动态代理对象;

    	@Override
    	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
    			Class<?> targetClass = config.getTargetClass();
    			if (targetClass == null) {
    				throw new AopConfigException("TargetSource cannot determine target class: " +
    						"Either an interface or a target is required for proxy creation.");
    			}
    			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                  //创建 JDK 动态代理
    				return new JdkDynamicAopProxy(config);
    			}
                //创建 Cglib 动态代理
    			return new ObjenesisCglibAopProxy(config);
    		}
    		else {
    			return new JdkDynamicAopProxy(config);
    		}
    	}
    
  7. 最终在方法 proxyFactory.getProxy 中返回一个动态代理对象:

在这里插入图片描述

  1. 最终将创建号的代理对象返回出去;

执行连接点方法

  1. 在上一步我们可以获取到了创建好的代理对象,并且该对象中保存了所有可以使用的增强器等信息数据;

  2. 如果该对象是 Cglib 创建出来的代理对象,那么当我们执行目标对象的目标方法时,就会被 CglibAopProxy中的 intercept 方法所拦截

    1. 获取目标对象;
    2. 然后根据 ProxyFactory 对象获取将要执行的目标方法拦截器链;
    3. 如果该拦截器链是空的,那么直接去执行目标对象的目标方法
    4. 如果存在拦截器链,将需要执行的目标对象,目标方法,拦截器链等信息传入并创建一个 CglibMethodInvocation 对象,并调用该对象的 proceed 方法,最后返回一个 retVal,也就是返回一个执行后的结果;
    	@Override
    		@Nullable
    		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    			Object oldProxy = null;
    			boolean setProxyContext = false;
    			Object target = null;
    			TargetSource targetSource = this.advised.getTargetSource();
    			try {
    				if (this.advised.exposeProxy) {
    					// Make invocation available if necessary.
    					oldProxy = AopContext.setCurrentProxy(proxy);
    					setProxyContext = true;
    				}
    				// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
                //获取目标对象
    				target = targetSource.getTarget();
    				Class<?> targetClass = (target != null ? target.getClass() : null);
                //根据 ProxyFactory 对象获取将要执行的目标方法拦截器链;
    				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    				Object retVal;
    
                    //如果该拦截器链是空的,那么直接去执行目标对象的目标方法
    				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
    					// We can skip creating a MethodInvocation: just invoke the target directly.
    					// Note that the final invoker must be an InvokerInterceptor, so we know
    					// it does nothing but a reflective operation on the target, and no hot
    					// swapping or fancy proxying.
    					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                      //执行目标方法
    					retVal = methodProxy.invoke(target, argsToUse);
    				}
    				else {
    					// We need to create a method invocation...
    					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
    				}
    				retVal = processReturnType(proxy, target, method, retVal);
    				return retVal;
    			}
    			finally {
    				if (target != null && !targetSource.isStatic()) {
    					targetSource.releaseTarget(target);
    				}
    				if (setProxyContext) {
    					// Restore old proxy.
    					AopContext.setCurrentProxy(oldProxy);
    				}
    			}
    		}
    
  3. 如何获取当前目标方法的拦截器链?

    List< Object > chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 方法

    遍历并获取所有的增强器,并且将所有的增强器转为 MethodInterceptor[] interceptors 方法拦截器;(会判断类型是否匹配,如果匹配就直接进行一个转换,也就是适配器模式)

  4. 当上一步方法拦截器创建好之后,就会进行目标方法,通知方法的执行(proceed 方法):

    在执行前,会先使用索引(开始时索引为-1)去获取拦截器中的通知方法,并且去执行该通知方法,但是在此时并未执行通知方法,而是继续获取到下一个拦截器方法(通知方法);

    直到所有的拦截器方法(通知方法)获取完毕后(如果通知方法中存在前置通知,那么前置通知就是最后一个获取到的),就会正真的执行该通知方法,并且执行完毕后,就会返回执行之前获取到的拦截器方法(通知方法);

    注意:在前置通知方法执行完毕后,就会去调用目标方法执行;

    然后其它的通知方法就会依次执行;

    public Object proceed() throws Throwable {
    		//判断当前的拦截器方法链(所有通知方法)的长度-1是否等于currentInterceptorIndex索引
        //currentInterceptorIndex索引每次执行proceed方法会+1
        //如果当前通知方法是4个(还有一个默认的ExposeInvocationIntercepter,总共5个,则
        //interceptorsAndDynamicMethodMatchers长度也就是5),那么
        //当执行完前置通知方法时,interceptorsAndDynamicMethodMatchers的长度就会是5-1 =4
        //而此时currentInterceptorIndex索引也是4,那么就直接调用连接点方法,也就是目标方法
    		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {			//前置通知方法完成后就会直接执行目标方法(连接点)
    			return invokeJoinpoint();
    		}
    
        //每次执行proceed方法索引会+1(开始索引为-1)
    		Object interceptorOrInterceptionAdvice =
    				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    			// Evaluate dynamic method matcher here: static part will already have
    			// been evaluated and found to match.
    			InterceptorAndDynamicMethodMatcher dm =
    					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
    			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
                 //执行通知方法,但是开始时并未执行通知方法,而是继续获取到下一个拦截器方法(通知方法)
                //也就是当前置通知方法调用完invoke后,在前置通知执行方法中又会调用proceed方法(递归)
    				return dm.interceptor.invoke(this);
    			}
    			else {
    				// Dynamic matching failed.
    				// Skip this interceptor and invoke the next in the chain.
    				return proceed();
    			}
    		}
    		else {
    			// It's an interceptor, so we just invoke it: The pointcut will have
    			// been evaluated statically before this object was constructed.
    			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    		}
    	}
    

Spring的事物

Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,Spring 是无法提供事务功能的;

示例

配置类:

数据源,JDBCTemplate 以及 配置事务管理器 TransactionManager;

@Configuration
@EnableTransactionManagement
@ComponentScan(value = "com.qixia.test")
public class TxConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("song1202S");
        druidDataSource.setUrl("jdbc:mysql:///test");
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return druidDataSource;
    }
    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }
    @Bean
    public PlatformTransactionManager platformTransactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }
}

Dao 类

@Repository
public class UserDao {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Transactional
    public void insert() {
        String sql = "insert into user(name,password,address,phone) values(?,?,?,?)";
        jdbcTemplate.update(sql, "七夏", "123456", "岐山", "110120119");
        //会进行回滚
        int i = 10 / 0;
    }
}

测试类

public class TxTest {
    @Test
    public void test(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TxConfig.class);
        UserDao userDao = ac.getBean(UserDao.class);
        userDao.insert();
    }
}

Spring 的事物种类

Spring 支持编程式事务管理和声明式事务管理两种方式:

  1. 编程式事务管理使用 TransactionTemplate ;

  2. 声明式事务管理建立在 AOP 之上,其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务;

    声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件或配置类中做相关的事务规则声明或通过 @Transactional 注解的方式,便可以将事务规则应用到业务逻辑中;

    声明式事务管理要优于编程式事务管理,这正是 Spring 倡导的非侵入式的开发方式,使业务代码不受污染,只要加上注解就可以获得完全的事务支持。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。


Spring 的事物传播行为

在注解 @Transactional 注解中使用 propagation 属性进行设置;

事务传播行为 解释
PROPAGATION_REQUIRED 如果有事务在运行就加入该事务,否则,就启动一个新的事务,并在自己的事务内运行
PROPAGATION_REQUIRED_NEW 不管当前有没有事物都新建一个事物
PROPAGATION_SUPPORTS 如果当前存在事物就加入该事务,否则就以非事物方式运行
PROPAGATION_NOT_SUPPORTED 以非事物方式运行,如果当前存在事物则将当前事物挂起
PROPAGATION_MANDATORY 当前存在事物时就加入该事务,否则就抛出异常
PROPAGATION_NEVER 以非事物方式运行,如果存在当前存在事物则抛出异常
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行,否则新建一个事物

Spring 的隔离级别

MySQL 中事物的默认隔离级别是 REPEATABLE_READ,可重复读;

  • 脏读:对于两个事务 T1 和 T2,当 T1 读取了已经被 T2 更新但还没有被提交的字段,之后,若 T2 回滚,T1 读取的内容就是临时且无效的;
  • 不可重复读:对于两个事务 T1 和 T2,当 T1 读取了一个字段,然后 T2 更新并提交了该字段,之后,T1 再次读取同一个字段,值就不同了
  • 幻读:对于两个事务 T1 和 T2,当 T1 从一个表中读取了一个字段,然后 T2 在该表中插入或除了一些行数据,之后,如果 T1 再次读取同一个表,就会多出或少了几行;
隔离级别 解释
ISOLATION_DEFAULT 是 PlatformTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别
ISOLATION_READ_UNCOMMITTED 读未提交,允许另外一个事务可以看到这个事务未提交的数据
ISOLATION_READ_COMMITTED 读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新
ISOLATION_REPEATABLE_READ 可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新
ISOLATION_SERIALIZABLE 序列化,一个事务在执行的过程中完全看不到其他事务对数据库所做的更新

本文地址:https://blog.csdn.net/qq_43816409/article/details/107484339

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网