Spring

Spring ⾼级框架

第⼀部分 Spring 概述

Spring的优势

  • ⽅便解耦,简化开发

    通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进⾏控制,避免硬编码所造成的过度程序耦合。⽤户也不必再为单例模式类、属性⽂件解析等这些很底层的需求编写代码,可以更专注于上层的应⽤。

  • AOP编程的⽀持

    通过Spring的AOP功能,⽅便进⾏⾯向切⾯的编程,许多不容易⽤传统OOP实现的功能可以通过AOP轻松应付。

  • 声明式事务的⽀持

    @Transactional

    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式⽅式灵活的进⾏事务的管理,提⾼开发效率和质量。

  • ⽅便程序的测试

    可以⽤⾮容器依赖的编程⽅式进⾏⼏乎所有的测试⼯作,测试不再是昂贵的操作,⽽是随⼿可做的事情。

  • ⽅便集成各种优秀框架

    Spring可以降低各种框架的使⽤难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接⽀持。

  • 降低JavaEE API的使⽤难度

    Spring对JavaEE API(如JDBC、JavaMail、远程调⽤等)进⾏了薄薄的封装层,使这些API的使⽤

    难度⼤为降低。

  • 源码是经典的 Java 学习范例

    Spring的源代码设计精妙、结构清晰、匠⼼独⽤,处处体现着⼤师对Java设计模式灵活运⽤以及对

    Java技术的⾼深造诣。它的源代码⽆意是Java技术的最佳实践的范例。

Spring的核⼼结构

Spring是⼀个分层⾮常清晰并且依赖关系、职责定位⾮常明确的轻量级框架,主要包括⼏个⼤模块:数据处理模块、Web模块、AOP(Aspect Oriented Programming)/Aspects模块、Core Container模块和 Test 模块,如下图所示,Spring依靠这些基本模块,实现了⼀个令⼈愉悦的融合了现有解决⽅案的零侵⼊的轻量级框架。

  • Spring核⼼容器(Core Container) 容器是Spring框架最核⼼的部分,它管理着Spring应⽤中bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。基于bean⼯⼚,我们还会发现有多种Spring应⽤上下⽂的实现。所有的Spring模块都构建于核⼼容器之上。

  • ⾯向切⾯编程(AOP)/Aspects Spring对⾯向切⾯编程提供了丰富的⽀持。这个模块是Spring应⽤系统中开发切⾯的基础,与DI⼀样,AOP可以帮助应⽤对象解耦。

  • 数据访问与集成(Data Access/Integration)Spring的JDBC和DAO模块封装了⼤量样板代码,这样可以使得数据库代码变得简洁,也可以更专注于我们的业务,还可以避免数据库资源释放失败⽽引起的问题。 另外,Spring AOP为数据访问提供了事务管理服务,同时Spring还对ORM进⾏了集成,如Hibernate、MyBatis等。该模块由JDBC、Transactions、ORM、OXM 和 JMS 等模块组成。

  • Web 该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。

  • Test 为了使得开发者能够很⽅便的进⾏测试,Spring提供了测试模块以致⼒于Spring应⽤的测试。 通过该模块,Spring为使⽤Servlet、JNDI等编写单元测试提供了⼀系列的mock对象实现。

第⼆部分 核⼼思想

IOC和DI

什么是IoC
  • IoC Inversion of Control (控制反转/反转控制),注意它是⼀个技术思想,不是⼀个技术实现。IOC是站在对象的角度,把其依赖的对象的实例化及管理交给了或者说反转给了容器

  • 描述的事情:Java开发领域对象的创建,管理的问题

  • 传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象IoC思想下开发⽅式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可

  • 我们丧失了⼀个权利(创建、管理对象的权利),得到了⼀个福利(不⽤考虑对象的创建、管理等⼀系列事情)

为什么叫做控制反转?
  • 控制:指的是对象创建(实例化、管理)的权利

  • 反转:控制权交给外部环境了(spring框架、IoC容器)

IOC 解决的问题
  • IoC解决对象之间的耦合问题
DI是什么
  • DI:就是指依赖注⼊,是站在容器的角度去把容器对象所依赖的对象注入到容器对象中
DI和IOC的区别
  • DI是站在容器的角度,IOC是站在对象的角度

AOP

OOP三⼤特征:封装、继承和多态。是⼀种垂直继承体系,可以解决⼤多数的代码重复问题,但是对于横切代码确无能为力。比如要在一些固定的方法的开始前和开始后打印日志,在oop下可以把打印日志的操作封装成一个方法,然后在某些方法中进行调用,但是调用的语句会重复。

aop的出现是为了解决oop下横切代码重复的问题。在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。面向切面编程可以在不改变业务逻辑的情况下悄⽆声息的把横切逻辑代码应⽤到原有的业务逻辑中,达到和原来⼀样的效果

第三部分 IOC 应用

启动 IOC 容器的⽅式
  • Java环境下启动

    • ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐使⽤)
    • FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件
    • AnnotationConfifigApplicationContext:纯注解模式下启动Spring容器
  • Web环境下启动

    • 纯xml或xml+注解启动容器

      <!DOCTYPE web-app PUBLIC
       "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
       "http://java.sun.com/dtd/web-app_2_3.dtd" >
      
      <web-app>
        <display-name>Archetype Created Web Application</display-name>
        <!--配置spring ioc 容器的配置文件-->
        <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
      
      <!-- 使用监听器启动Spring的IOC容器 -->
        <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
      </web-app>
      
    • 纯注解模式下从配置类启动容器

      <!DOCTYPE web-app PUBLIC
       "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
       "http://java.sun.com/dtd/web-app_2_3.dtd" >
      
      <web-app>
        <display-name>Archetype Created Web Application</display-name>
      
        <!--告诉ContextLoaderListener我们使用注解方式启动ioc-->
        <context-param>
          <param-name>contextClass</param-name>
          <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
        </context-param>
      
        <!--配置spring ioc 容器的启动类-->
        <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>com.edu.spring.config.SpringConfig</param-value>
        </context-param>
      <!-- 使用监听器启动Spring的IOC容器 -->
        <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
      </web-app>
      
XML实例化Bean的三种⽅式
  • 使⽤⽆参构造函数
  • 使⽤静态⽅法创建
  • 使⽤实例化⽅法创建
DI依赖注入的两种方式
  • 构造函数注⼊:顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值。
  • set⽅法注⼊:它是通过类成员的set⽅法实现数据的注⼊。(使⽤最多的)
xml中标签与注解的对应

[外链图片转存中...(img-85PBsM11-1714114587763)]

@Autowired和@Resource的区别

@Autowired

  • @Autowired为Spring提供的注解,需要导⼊包org.springframework.beans.factory.annotation.Autowired。@Autowired采取的策略为按照类型注⼊。
  • 如果对应的类型有多个就可以使用@Qualifier,该注解可以告诉Spring具体去装配哪个id的对象。

@Resource

  • @Resource 注解由 J2EE 提供, Jdk 11已经移除Resource 使用的话,需要导⼊包 javax.annotation.Resource。

  • @Resource 默认按照 ByName ⾃动注⼊。

    • 如果同时指定了 name 和 type,则从Spring上下⽂中找到唯⼀匹配的bean进⾏装配,找不到则抛出异常。
    • 如果指定了 name,则从上下⽂中查找名称(id)匹配的bean进⾏装配,找不到则抛出异常。
    • 如果指定了 type,则从上下⽂中找到类似匹配的唯⼀bean进⾏装配,找不到或是找到多个,都会抛出异常。
    • 如果既没有指定name,⼜没有指定type,则⾃动按照byName⽅式进⾏装配;
Bean的作用范围

基本上singleton和prototype用的最多

  • 单例模式:singleton

    对象出⽣:当创建容器时,对象就被创建了。

    对象活着:只要容器在,对象⼀直活着。

    对象死亡:当销毁容器时,对象就被销毁了。

    ⼀句话总结:单例模式的bean对象⽣命周期与容器相同。

  • 原型模式:prototype

    对象出⽣:当使⽤对象时,创建新的对象实例。

    对象活着:只要对象在使⽤中,就⼀直活着。

    对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。

    ⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。

  • request (仅 Web 应用可用)

    每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。

  • session (仅 Web 应用可用)

    每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。

  • application/global-session (仅 Web 应用可用)

    每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。

  • websocket (仅 Web 应用可用)

    每一次 WebSocket 会话产生一个新的 bean

FactoryBean 和后置处理器

FactoryBean

1.Spring中Bean有两种,⼀种是普通Bean,⼀种是⼯⼚Bean(FactoryBean),FactoryBean可以⽣成某⼀个类型的Bean实例(返回给我们),也就是说我们可以借助于它⾃定义Bean的创建过程。

2.FactoryBean使⽤较多,尤其在Spring框架⼀些组件中会使⽤,还有其他框架和Spring框架整合时使⽤

后置处理器

1.Spring提供了两种后处理bean的扩展接⼝,分别为 BeanPostProcessor 和BeanFactoryPostProcessor,两者在使⽤上是有所区别的。

2.在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进⾏后置处理做⼀些事情

3.在Bean对象执行完aware方法(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor进⾏后置处理做⼀些事情

  • BeanPostProcessor

    • 该接⼝提供了两个⽅法postProcessBeforeInitializationpostProcessAfterInitialization,分别在Bean的初始化⽅法前和初始化⽅法后执⾏,具体这个初始化⽅法指的是什么⽅法,类似我们在定义bean时,定义了init-method所指定的⽅法

    • 定义⼀个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进⾏处理。如果要对具体的某个bean处理,可以通过⽅法参数判断,两个类型参数分别为Object和String,第⼀个参数是每个bean的实例,第⼆个参数是每个bean的name或者id属性的值。所以我们可以通过第⼆个参数,来判断我们将要处理的具体的bean。

  • BeanFactoryPostProcessor

    • BeanFactory级别的处理,是针对整个Bean的⼯⼚进⾏处理,比如修改bean对应的BeanDefinition

IOC容器初始化过程?

ioc 容器初始化过程:BeanDefinition 的资源定位、解析和注册。

  • 从XML中读取配置文件。
  • 将bean标签解析成 BeanDefinition,如解析 property 元素, 并注入到 BeanDefinition 实例中。
  • 将 BeanDefinition 注册到容器 BeanDefinitionMap 中。
  • BeanFactory 根据 BeanDefinition 的定义信息创建实例化和初始化 bean。

springBean生命周期

  1. bean 的实例化
  2. 注入对象属性
  3. 处理 XXXXAware 接口,包含 EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、BeanNameAware、BeanFactoryAware、ApplicationContextAware,执行其特定的方法。
  4. 扫描所有 BeanPostProcessor 接口。传入当前对象执行定义的所有 postProcessBeforeInitialization (Object bean, String beanName) 方法。
  5. 执行 @PostConstruct 注解方法
  6. 处理 InitializingBean 接口的 afterPropertiesSet () 方法。
  7. 处理 IOC 配置文件当前 bean 配置的 init-method 方法(很少用)。
  8. 扫描所有的 BeanPostProcessor 接口。传入当前对象执行定义的所有 postProcessAfterInitialization (Object bean, String beanName) 方法。
  9. 容器刷新完成扫描实现 Lifecycle 接口的 bean 根据需要调用其 start 方法。
  10. 容器刷新完成事件广播扫描实现 ApplicationListener 接口的 Bean,并调用其 onApplicationEvent 方法。
  11. 启动最后一步扫描实现了 ApplicationRunner 或 CommandRunner 的 bean,调用其对应方法。
  12. 容器 close 时扫描实现 Lifecycle 接口的 bean 根据需要调用其 stop 方法。
  13. 当 bean 销毁时,一般是例如 Spring 上下文初始化失败时,或在 Spring 容器关闭时(正常关闭 kill -15,而不是 kill -9),会处理 DisposableBean 接口的 destroy 方法以及 @PreDestroy 注解的方法,其中 @PreDestroy 注解的方法较 DisposableBean 接口的 destroy 方法先执行。

第四部分 IOC 源码深度剖析

java 复制代码
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
       // Prepare this context for refreshing.
       prepareRefresh();//刷新上下文环境

       // Tell the subclass to refresh the internal bean factory.
       ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//这里是在子类中启动 refreshBeanFactory() 的地方

       // Prepare the bean factory for use in this context.
       prepareBeanFactory(beanFactory);//准备bean工厂,以便在此上下文中使用

       try {
          // Allows post-processing of the bean factory in context subclasses.
          postProcessBeanFactory(beanFactory);//设置 beanFactory 的后置处理

          // Invoke factory processors registered as beans in the context.
          invokeBeanFactoryPostProcessors(beanFactory);//调用 BeanFactory 的后处理器,这些处理器是在Bean 定义中向容器注册的

          // Register bean processors that intercept bean creation.
          registerBeanPostProcessors(beanFactory);//注册Bean的后处理器,在Bean创建过程中调用

          // Initialize message source for this context.
          initMessageSource();//对上下文中的消息源进行初始化

          // Initialize event multicaster for this context.
          initApplicationEventMulticaster();//初始化上下文中的事件机制

          // Initialize other special beans in specific context subclasses.
          onRefresh();//初始化其他特殊的Bean

          // Check for listener beans and register them.
          registerListeners();//检查监听Bean 并且将这些监听Bean向容器注册

          // Instantiate all remaining (non-lazy-init) singletons.
          finishBeanFactoryInitialization(beanFactory);///实例化所有的(non-lazy-init)单件

          // Last step: publish corresponding event.
          finishRefresh();//发布容器事件,结束Refresh过程
       }

       catch (BeansException ex) {
          if (logger.isWarnEnabled()) {
             logger.warn("Exception encountered during context initialization - " +
                   "cancelling refresh attempt: " + ex);
          }

          // Destroy already created singletons to avoid dangling resources.
          destroyBeans();

          // Reset 'active' flag.
          cancelRefresh(ex);

          // Propagate exception to caller.
          throw ex;
       }

       finally {
          // Reset common introspection caches in Spring's core, since we
          // might not ever need metadata for singleton beans anymore...
          resetCommonCaches();
       }
    }
}

spring时序图

[外链图片转存中...(img-bYWRvcWJ-1714114587764)]

@startuml
'https://plantuml.com/sequence-diagram
'时序图入口方法在AbstractApplicationContext的refresh方法

'bean工厂的创建 obtainFreshBeanFactory
'Bean工厂后置处理器初始化、方法执行:AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors
'Bean后置处理器初始化:AbstractApplicationContext#refresh#registerBeanPostProcessors
'Bean的构造器执行、初始化方法执行、Bean后置处理器的before/after方法、:AbstractApplicationContext#refresh#finishBeanFactoryInitialization
autonumber

AbstractApplicationContext -> AbstractApplicationContext: refresh
activate AbstractApplicationContext

alt 创建BeanFactory开始
AbstractApplicationContext -> AbstractApplicationContext: obtainFreshBeanFactory方法创建BeanFactory
activate AbstractApplicationContext

AbstractApplicationContext -> AbstractRefreshableApplicationContext: refreshBeanFactory方法创建BeanFactory
activate AbstractRefreshableApplicationContext
AbstractRefreshableApplicationContext -> AbstractRefreshableApplicationContext: createBeanFactory\n方法创建一个DefaultListableBeanFactory
activate AbstractRefreshableApplicationContext
deactivate AbstractRefreshableApplicationContext

alt BeanDefinition加载解析及注册
AbstractRefreshableApplicationContext -> AbstractXmlApplicationContext: loadBeanDefinitions\nBeanDefinition加载解析及注册
activate AbstractXmlApplicationContext
AbstractXmlApplicationContext -> AbstractXmlApplicationContext: loadBeanDefinitions\nBeanDefinition加载解析及注册
activate AbstractXmlApplicationContext
AbstractXmlApplicationContext ->AbstractBeanDefinitionReader:loadBeanDefinitions
activate AbstractBeanDefinitionReader
AbstractBeanDefinitionReader ->AbstractBeanDefinitionReader:不停的调重载的loadBeanDefinitions
activate AbstractBeanDefinitionReader
AbstractBeanDefinitionReader ->XmlBeanDefinitionReader:loadBeanDefinitions
activate XmlBeanDefinitionReader
XmlBeanDefinitionReader ->XmlBeanDefinitionReader:loadBeanDefinitions
activate XmlBeanDefinitionReader
XmlBeanDefinitionReader ->XmlBeanDefinitionReader:doLoadBeanDefinitions\n读取xml封装为Document
activate XmlBeanDefinitionReader
XmlBeanDefinitionReader ->XmlBeanDefinitionReader:registerBeanDefinitions\n开始注册
activate XmlBeanDefinitionReader
XmlBeanDefinitionReader ->DefaultBeanDefinitionDocumentReader:registerBeanDefinitions

activate DefaultBeanDefinitionDocumentReader
DefaultBeanDefinitionDocumentReader ->DefaultBeanDefinitionDocumentReader:doRegisterBeanDefinitions
activate DefaultBeanDefinitionDocumentReader
DefaultBeanDefinitionDocumentReader ->DefaultBeanDefinitionDocumentReader:parseBeanDefinitions
activate DefaultBeanDefinitionDocumentReader
DefaultBeanDefinitionDocumentReader ->DefaultBeanDefinitionDocumentReader:parseDefaultElement
activate DefaultBeanDefinitionDocumentReader
DefaultBeanDefinitionDocumentReader ->DefaultBeanDefinitionDocumentReader:parseDefaultElement
activate DefaultBeanDefinitionDocumentReader
DefaultBeanDefinitionDocumentReader ->DefaultBeanDefinitionDocumentReader:processBeanDefinition

activate DefaultBeanDefinitionDocumentReader
DefaultBeanDefinitionDocumentReader ->BeanDefinitionReaderUtils:registerBeanDefinition

activate BeanDefinitionReaderUtils
BeanDefinitionReaderUtils ->DefaultListableBeanFactory:registerBeanDefinition\n将BeanDefinition放到beanDefinitionMap(ConcurrentHashMap)中
deactivate BeanDefinitionReaderUtils

deactivate DefaultBeanDefinitionDocumentReader

deactivate DefaultBeanDefinitionDocumentReader
deactivate DefaultBeanDefinitionDocumentReader
deactivate DefaultBeanDefinitionDocumentReader
deactivate DefaultBeanDefinitionDocumentReader
deactivate DefaultBeanDefinitionDocumentReader


deactivate XmlBeanDefinitionReader

deactivate XmlBeanDefinitionReader


deactivate XmlBeanDefinitionReader

deactivate XmlBeanDefinitionReader



deactivate AbstractBeanDefinitionReader
deactivate AbstractBeanDefinitionReader
deactivate AbstractXmlApplicationContext
deactivate AbstractXmlApplicationContext

end BeanDefinition加载解析及注册结束

deactivate AbstractRefreshableApplicationContext

AbstractApplicationContext -> AbstractRefreshableApplicationContext: getBeanFactory方法获取BeanFactory
activate AbstractRefreshableApplicationContext
AbstractRefreshableApplicationContext -->AbstractApplicationContext:返回DefaultListableBeanFactory
deactivate AbstractApplicationContext


deactivate AbstractRefreshableApplicationContext
end 创建BeanFactory结束


alt BeanFactoryPostProcessor 初始化和方法执行
AbstractApplicationContext -> AbstractApplicationContext: invokeBeanFactoryPostProcessors
activate AbstractApplicationContext
AbstractApplicationContext -> PostProcessorRegistrationDelegate: invokeBeanFactoryPostProcessors
activate PostProcessorRegistrationDelegate
PostProcessorRegistrationDelegate -> AbstractBeanFactory: getBean\n BeanFactoryPostProcessor是nonOrderedPostProcessors\n在getBean的时候初始化

PostProcessorRegistrationDelegate -> PostProcessorRegistrationDelegate: invokeBeanFactoryPostProcessors\n 执行方法
activate PostProcessorRegistrationDelegate
deactivate PostProcessorRegistrationDelegate
deactivate PostProcessorRegistrationDelegate
deactivate AbstractApplicationContext

end BeanFactoryPostProcessor 结束






alt BeanPostProcessor 初始化
AbstractApplicationContext -> AbstractApplicationContext: registerBeanPostProcessors

activate AbstractApplicationContext

AbstractApplicationContext -> PostProcessorRegistrationDelegate: registerBeanPostProcessors
activate PostProcessorRegistrationDelegate
PostProcessorRegistrationDelegate -> AbstractBeanFactory: getBean\n BeanPostProcessor是nonOrderedPostProcessors\n在getBean的时候初始化
deactivate PostProcessorRegistrationDelegate
deactivate AbstractApplicationContext
end BeanPostProcessor 初始化结束










alt Bean的构造器执行、aware方法、初始化方法执行、Bean后置处理器的before/after方法
AbstractApplicationContext -> AbstractApplicationContext: finishBeanFactoryInitialization
activate AbstractApplicationContext
AbstractApplicationContext -> DefaultListableBeanFactory: preInstantiateSingletons
activate DefaultListableBeanFactory
DefaultListableBeanFactory -> AbstractBeanFactory: getBean

activate AbstractBeanFactory
AbstractBeanFactory -> AbstractBeanFactory: doGetBean

activate AbstractBeanFactory
AbstractBeanFactory -> DefaultSingletonBeanRegistry: getSingleton

activate DefaultSingletonBeanRegistry

DefaultSingletonBeanRegistry -> AbstractAutowireCapableBeanFactory: createBean(singletonFactory.getObject()实际调的是createBean)
activate AbstractAutowireCapableBeanFactory

AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory: doCreateBean
activate AbstractAutowireCapableBeanFactory

AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory:createBeanInstance 获取bean实例

activate AbstractAutowireCapableBeanFactory
deactivate AbstractAutowireCapableBeanFactory
AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory:populateBean 填充bean实例
activate AbstractAutowireCapableBeanFactory
deactivate AbstractAutowireCapableBeanFactory

AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory:initializeBean \n开始执行aware方法、初始化方法执行、Bean后置处理器的before/after方法
activate AbstractAutowireCapableBeanFactory
AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory:invokeAwareMethods \n开始执行aware方法
activate AbstractAutowireCapableBeanFactory
deactivate AbstractAutowireCapableBeanFactory

AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization \n开始执行postProcessBeforeInitialization方法
activate AbstractAutowireCapableBeanFactory
deactivate AbstractAutowireCapableBeanFactory

AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory:invokeInitMethods \n开始执行初始化方法
activate AbstractAutowireCapableBeanFactory
deactivate AbstractAutowireCapableBeanFactory

AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsAfterInitialization \n开始执行postProcessAfterInitialization方法
activate AbstractAutowireCapableBeanFactory
deactivate AbstractAutowireCapableBeanFactory
deactivate AbstractAutowireCapableBeanFactory

AbstractAutowireCapableBeanFactory-->AbstractAutowireCapableBeanFactory:返回bean
deactivate AbstractAutowireCapableBeanFactory

AbstractAutowireCapableBeanFactory-->DefaultSingletonBeanRegistry:返回bean
deactivate AbstractAutowireCapableBeanFactory

DefaultSingletonBeanRegistry --> AbstractBeanFactory:返回bean
deactivate DefaultSingletonBeanRegistry

AbstractBeanFactory --> AbstractBeanFactory:返回bean
deactivate AbstractBeanFactory

AbstractBeanFactory --> DefaultListableBeanFactory:返回bean1
deactivate AbstractBeanFactory


deactivate DefaultListableBeanFactory
deactivate AbstractApplicationContext

end Bean 初始化结束






deactivate AbstractApplicationContext




@enduml

lazy-init 延迟加载机制原理

  • lazy-init 延迟加载机制分析

    普通 Bean 的初始化是在容器启动初始化阶段执⾏的,⽽被lazy-init=true修饰的 bean 则是在从容器⾥第⼀次进⾏context.getBean() 时进⾏触发。Spring 启动的时候会把所有bean信息(包括XML和注解)解析转化成Spring能够识别的BeanDefifinition并存到Hashmap⾥供下⾯的初始化时⽤,然后对每个BeanDefifinition 进⾏处理,如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进⾏初始化并依赖注⼊。

    java 复制代码
    for (String beanName : beanNames) {
    			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    			//会判断是否是懒加载
    			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
    				if (isFactoryBean(beanName)) {
    					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
    					if (bean instanceof FactoryBean) {
    						final FactoryBean<?> factory = (FactoryBean<?>) bean;
    						boolean isEagerInit;
    						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
    							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
    											((SmartFactoryBean<?>) factory)::isEagerInit,
    									getAccessControlContext());
    						}
    						else {
    							isEagerInit = (factory instanceof SmartFactoryBean &&
    									((SmartFactoryBean<?>) factory).isEagerInit());
    						}
    						if (isEagerInit) {
    							getBean(beanName);
    						}
    					}
    				}
    				else {
    					getBean(beanName);
    				}
    			}
    		}
  • 总结

    • 对于被修饰为lazy-init的bean Spring 容器初始化阶段不会进⾏ init 并且依赖注⼊,当第⼀次进⾏getBean时候才进⾏初始化并依赖注⼊
    • 对于⾮懒加载的bean,getBean的时候会从缓存⾥头获取,因为容器初始化阶段 Bean 已经初始化完成并缓存了起来
    • 对于scope="pototype" 即使没有开启延迟加载,在容器初始化的时候也不会立即实例化,而是在用到时候才会初始化

Spring IoC循环依赖问题

什么是循环依赖

循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A依赖于B,B依赖于C,C⼜依赖于A。

注意,这⾥不是函数的循环调⽤,是对象的相互依赖关系。循环调⽤其实就是⼀个死循环,除⾮有终结条件。

Spring中循环依赖场景有

  • 构造器的循环依赖(构造器注⼊)
  • Field 属性的循环依赖(set注⼊)

其中,构造器的循环依赖问题⽆法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决属性循环依赖时,spring采⽤的是提前暴露对象的⽅法。

[外链图片转存中...(img-heSFiJmu-1714114587765)]

循环依赖处理机制
  • 单例 bean 构造器参数循环依赖(⽆法解决)

  • prototype 原型 bean循环依赖(⽆法解决)

对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx⽅法产⽣循环依赖,Spring都 会直接报错处理。

AbstractBeanFactory.doGetBean()⽅法:

java 复制代码
if (isPrototypeCurrentlyInCreation(beanName)) {
	throw new BeanCurrentlyInCreationException(beanName);
}
java 复制代码
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
	Object curVal = this.prototypesCurrentlyInCreation.get();
	return (curVal != null &&
 					(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>)
curVal).contains(beanName))));
}

在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进⾏标记这个beanName正在被创建,等创建结束之后会删除标记

java 复制代码
try {
		//创建原型bean之前添加标记
		beforePrototypeCreation(beanName);
		//创建原型bean
		prototypeInstance = createBean(beanName, mbd, args);
}
finally {
		//创建原型bean之后删除标记
		afterPrototypeCreation(beanName);
}

总结:Spring 不⽀持原型 bean 的循环依赖。

  • 单例bean通过setXxx或者@Autowired进⾏循环

    Spring 的循环依赖的理论依据基于 Java 的引⽤传递,当获得对象的引⽤时,对象的属性是可以延后设置的,但是构造器必须是在获取引⽤之前调用.Spring通过setXxx或者@Autowired⽅法解决循环依赖其实是通过提前暴露⼀个ObjectFactory对象来完成的,简单来说ClassA在调⽤构造器完成对象初始化之后,在调⽤ClassA的setClassB⽅法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。

    • Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器。
    • ClassA调⽤setClassB⽅法,Spring⾸先尝试从容器中获取ClassB,此时ClassB不存在Spring容器中。
    • Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
    • ClassB调⽤setClassA⽅法,Spring从容器中获取ClassA ,因为第⼀步中已经提前暴露了ClassA,因此可以获取到ClassA实例
      • ClassA通过spring容器获取到ClassB,完成了对象初始化操作。
    • 这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题
spring为什么要用三级缓存

这三级缓存分别指:

singletonFactories : 单例对象工厂的cache-三级

earlySingletonObjects :提前暴光的单例对象的Cache-二级

singletonObjects:单例对象的cache-一级

为了解决循环依赖,举例,A依赖B,B依赖A

  • 在实例化A之后,会先判断是否要提前暴露,如果需要提前暴露会调用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory方法放到三级缓存中
  • 接着给A填写属性,发现依赖B,触发B的实例化,在B实例化之后也会调用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory方法放到三级缓存中
  • 然后给B填写属性的过程中会调用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)来获取A的引用。getSingleton中会从一级->二级->三级缓存中寻找A的引用,如果A是在三级缓存中会执行扩展方法,然后把A移到二级缓存
  • 在构建Bean结束后会调用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton把bean放入一级缓存,同时从二三级缓存删除

为什么是三级不是二级呢?

一级缓存是必须的,如果是两级就要去掉第二级或者第三级

  • 如果去掉第三级,第三级是单例对象工厂的cache,如果没有这个那就要直接创建单例对象而不是使用对象工厂,这样就会破坏spring的设计原则
  • 如果去掉第二级,那如果对象A依赖了对象B和对象C,同时BC也依赖A,那在BC从三级缓存中获取缓存对象的时候会get出两个不同的对象,就破坏了单例

第五部分AOP应用

代理

代理:代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象。如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求,同时代理模式便于扩展目标对象功能的特点也为多人所用。代理分为静态代理和动态代理

静态代理

概念

由程序员创建或工具生成代理类的源码,再编译代理类,即代理类和委托类的关系再程序运行前就已经存在

原理

假设B实现A接口,B是目标对象,创建一个C对象实现A接口,并且持有B对象,这样就可以在C中对B进行增强

静态代理的总结

  • 优点:可以做到不对目标对象进行修改的前提下,对目标对象进行功能的扩展和拦截。
  • 缺点:因为代理对象,需要实现与目标对象一样的接口,会导致代理类十分繁多,不易维护,同时一旦接口增加方法,则目标对象和代理类都需要维护。
动态代理

概念

在运行期间使用动态生成字节码形式,动态创建代理类。使用的工具有 jdkproxy、cglibproxy 等。

jdk原理

假设B实现A接口,B是目标对象,

Jdk会生成一个实现A的代理类C,C实现A的所有接口,但是接口内会调用B的包装类D(实现InvocationHandler的类)的invoke方法,这样就可以在invoke里面对某些方法进行增强

cglib原理

假设B实现A接口,B是目标对象

cglib会生成一个继承B的代理类C,C重写了B的所有方法。对于B中非private方法都生成了cglib方法("CGLIB"+" 父类方法名 父类方法名 父类方法名"的方法),cglib方法内部只是调用了父类的对应的方法。C中还持有一个D(实现MethodInterceptor的类),D中有一个方法拦截方法intercept,C中重写的方法都调用该方法,这样就可以在intercept里面对某些方法进行增强。

两者的区别

jdk动态代理使用jdk中的类Proxy来创建代理对象,它使用反射技术来实现,不需要导入其他依赖。cglib需要引入相关依赖:asm.jar,它使用字节码增强技术来实现。

动态代理的总结

  • 优点:代理对象无需显示的实现接口,免去了编写很多代理类的烦恼,同时接口增加方法也无需再维护目标对象和代理对象,只需在事件处理器中添加对方法的判断即可。
  • 缺点:效率低。

AOP概念

  • 连接点:⽅法开始时、结束时、正常运⾏完毕时、⽅法异常时等这些特殊的时机点,我们称之为连接点,项⽬中每个⽅法都有连接点,连接点是⼀种候选点

  • 切⼊点:指定AOP思想想要影响的具体⽅法是哪些,描述感兴趣的⽅法

  • Advice增强:

    • 第⼀个层次:指的是横切逻辑
    • 第⼆个层次:⽅位点(在某⼀些连接点上加⼊横切逻辑,那么这些连接点就叫做⽅位点,描述的是具体的特殊时机)
  • Aspect切⾯:切⾯概念是对上述概念的⼀个综合

  • Aspect切⾯= 切⼊点+增强

    ​ = 切⼊点(锁定⽅法) + ⽅位点(锁定⽅法中的特殊时机)+ 横切逻辑

    众多的概念,⽬的就是为了锁定要在哪个地⽅插⼊什么横切逻辑代码

Spring 实现AOP思想使⽤的是动态代理技术

默认情况下,Spring会根据被代理对象是否实现接⼝来选择使⽤JDK还是CGLIB。当被代理对象没有实现任何接⼝时,Spring会选择CGLIB。当被代理对象实现了接⼝,Spring会选择JDK官⽅的代理技术,不过我们可以通过配置的⽅式,让Spring强制使⽤CGLIB。

注意,在bean的生命周期中代理对象是在bean初始化完成后,在BeanPostProcessor后置处理器中生成的,但是在循环依赖A->B->C->A中如果A被代理,那么A是在C注入A的时候A的代理对象就被生成了,并且把A从三级缓存放到二级缓存中,等到A完成后置处理器的后初始化方法后,会去一级和二级缓存中查询最新的A

编程式事务和声明式事务

编程式事务:在业务代码中添加事务控制代码,这样的事务控制机制就叫做编程式事务

声明式事务:通过xml或者注解配置的⽅式达到事务控制的⽬的,叫做声明式事务

事务的概念

事务指逻辑上的⼀组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。从⽽确保了数

据的准确与安全。

事务的四大特性

  • 原⼦性(Atomicity) 原⼦性是指事务是⼀个不可分割的⼯作单位,事务中的操作要么都发⽣,要么都不发⽣。从操作的⻆度来描述,事务中的各个操作要么都成功要么都失败

  • ⼀致性(Consistency) 事务必须使数据库从⼀个⼀致性状态变换到另外⼀个⼀致性状态。

    • 例如转账前A有1000,B有1000。转账后A+B也得是2000。
    • ⼀致性是从数据的⻆度来说的,(1000,1000) (900,1100),不应该出现(900,1000)
  • 隔离性(Isolation) 事务的隔离性是多个⽤户并发访问数据库时,数据库为每⼀个⽤户开启的事务,每个事务不能被其他事务的操作数据所⼲扰,多个并发事务之间要相互隔离。

    • ⽐如:事务1给员⼯涨⼯资2000,但是事务1尚未被提交,员⼯发起事务2查询⼯资,发现⼯资涨了2000块钱,读到了事务1尚未提交的数据(脏读)
  • 持久性(Durability) 持久性是指⼀个事务⼀旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发⽣故障也不应该对其有任何影响。

事务的隔离级别

不考虑隔离级别,会出现以下情况:(以下情况全是错误的),也即为隔离级别在解决事务并发问题

脏读:⼀个线程中的事务读到了另外⼀个线程中未提交的数据。

不可重复读 :⼀个线程中的事务读到了另外⼀个线程中已经提交的update的数据(前后内容不⼀样)

场景:

员⼯A发起事务1,查询⼯资,⼯资为1w,此时事务1尚未关闭

财务⼈员发起了事务2,给员⼯A张了2000块钱,并且提交了事务

员⼯A通过事务1再次发起查询请求,发现⼯资为1.2w,原来读出来1w读不到了,叫做不可重复读

虚读(幻读):⼀个线程中的事务读到了另外⼀个线程中已经提交的insert或者delete的数据(前后条数不⼀样)

场景:

事务1查询所有⼯资为1w的员⼯的总数,查询出来了10个⼈,此时事务尚未关闭

事务2财务⼈员发起,新来员⼯,⼯资1w,向表中插⼊了2条数据,并且提交了事务

事务1再次查询⼯资为1w的员⼯个数,发现有12个⼈

数据库共定义了四种隔离级别:

Serializable(串⾏化):可避免脏读、不可重复读、虚读情况的发⽣。(串⾏化) 最⾼

Repeatable read(可重复读):可避免脏读、不可重复读情况的发⽣。(幻读有可能发⽣) 第⼆该机制下会对要update的⾏进⾏加锁

Read committed(读已提交):可避免脏读情况发⽣。不可重复读和幻读⼀定会发⽣。 第三

Read uncommitted(读未提交):最低级别,以上情况均⽆法保证。(读未提交) 最低

注意:级别依次降低,效率依次升高

MySQL的默认隔离级别是:REPEATABLE READ

查询当前使⽤的隔离级别: select @@tx_isolation;

设置MySQL事务的隔离级别: set session transaction isolation level xxx; (设置的是当前mysql连接会话的,并不是永久改变的)

事务的传播⾏为

事务往往在service层进⾏控制,如果出现service层⽅法A调⽤了另外⼀个service层⽅法B,A和B⽅法本身都已经被添加了事务控制,那么A调⽤B的时候,就需要进⾏事务的⼀些协商,这就叫做事务的传播⾏为。

A调⽤B,我们站在B的⻆度来观察来定义事务的传播⾏为

PROPAGATION_NOT_SUPPORTED 以⾮事务⽅式执⾏操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER 以⾮事务⽅式执⾏,如果当前存在事务,则抛出异常。

PROPAGATION_SUPPORTS 使用当前事务,如果当前没有事务,就以⾮事务⽅式执⾏。

PROPAGATION_MANDATORY 使⽤当前的事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRED 事务执行,如果已经存在⼀个事务中,加⼊到这个事务中,不存在就新建事务。这是最常⻅的选择。

PROPAGATION_REQUIRES_NEW 事务执行,如果当前存在事务,把当前事务挂起,新建一个事务执行。

PROPAGATION_NESTED 事务执行,如果当前存在事务,则在嵌套事务内执⾏。不存在就新建事务。

第六部分 AOP源码深度剖析

代理对象创建调用关系图
java 复制代码
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
调⽤
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
调⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization(后置处理器AbstractAutoProxyCreator完成bean代理对象创建)
调⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
调⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy (在这⼀步把委托对象的aop增强和通⽤拦截进⾏合并,最终给代理对象)
调⽤
org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)
	先调用org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy
  			调用org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy(根据目标对象是否实现了接口,选择代理类型是jdk还是CGLB)
	在调⽤org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)(也可能会是 org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader) )
Spring声明式事务控制剖析

@EnableTransactionManagement 注解通过@import引⼊了TransactionManagementConfigurationSelector类

它的selectImports⽅法导⼊了两个类:AutoProxyRegistrar和ProxyTransactionManagementConfiguration

1.AutoProxyRegistrar类的registerBeanDefinitions⽅法中,引⼊了其他类,通过AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)在spring的 bean中加⼊了一个

InfrastructureAdvisorAutoProxyCreator对象,它继承了AbstractAutoProxyCreator,是⼀个后置处理器类,会给目标对象生成代理对象

2.ProxyTransactionManagementConfiguration 是⼀个添加了@Configuration注解的配置类(注册bean)注册事务增强器(注⼊属性解析器、事务拦截器

  • 属性解析器:AnnotationTransactionAttributeSource,内部持有了⼀个解析器集合Set annotationParsers;具体使⽤的是SpringTransactionAnnotationParser解析器,⽤来解析@Transactional的事务属性
  • 事务拦截器:TransactionInterceptor实现了MethodInterceptor接⼝,会加入拦截器链,该通⽤拦截会在产⽣代理对象之前和aop增强合并,最终⼀起影响到代理对象。TransactionInterceptor的invoke⽅法中会调用invokeWithinTransaction方法,invokeWithinTransaction中封装了事务操作,会触发原有业务逻辑调⽤(增强事务)

AutoProxyRegistrar和ProxyTransactionManagementConfiguration是如何关联的?

InfrastructureAdvisorAutoProxyCreator是一个后置处理器类,会生成代理对象。在代理对象执行目标方法的时候会获取其拦截器链,而拦截器链就是TransactionInterceptor,这样对应的方法就会执行TransactionInterceptor的invoke⽅法,而invoke方法中会调用invokeWithinTransaction执行事务逻辑。

相关推荐
minDuck1 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
为将者,自当识天晓地。20 分钟前
c++多线程
java·开发语言
daqinzl28 分钟前
java获取机器ip、mac
java·mac·ip
激流丶44 分钟前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
Themberfue1 小时前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
让学习成为一种生活方式1 小时前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画1 小时前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
假装我不帅2 小时前
asp.net framework从webform开始创建mvc项目
后端·asp.net·mvc
南宫生2 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法