00Spring

Spring

  • 基于POJO的轻量级和最小侵入性编程;
  • 通过依赖注入和面向接口实现松耦合;
  • 基于切面和惯例进行声明式编程;
  • 通过切面和模板减少样板式代码。

Bean

Bean的装配

  • xml中进行显示装配
  • java中进行显式装配

@Bean

  • 在java显式装配中,默认情况下bean的id与方法名一致,可以通过name进行重命名
  • @Import和@ImportResource: 第一个在java中引入其他的bean装配,第二个在java中引入xml的bean装配

隐式的bean发现机制和自动装配

  • 组件扫描(component scanning) : Spring会自动发现应用上下文中所创建的bean。
  • 自动装配(autowiring) : Spring自动满足bean之间的依赖
@Autowired
  • 解决自动装配的歧义性,@primary注解定义首选的Bean,或者在注入和定义Bean时都使用@Qualifier限定beanId

条件化的Bean

  • 使用@conditional(MyContition.class)注解,MyContition需要实现Condition接口,接口中的match方法会判断是否实例化这个类

运行时值注入

注入外部的值

  • image-20210411165206110
spring的Environment
  • image-20210411171801431
  • getRequiredProperty() 获取的属性必须被定义,否则抛出异常

解析属性占位符

  • ${}格式,配合@Value注解使用

Beand的作用域

  • 单例(Singleton) : 在整个应用中, 只创建bean的一个实例。
  • 原型(Prototype) : 每次注入或者通过Spring应用上下文获取的时候, 都会创建一个新的bean实例。
  • 会话(Session) : 在Web应用中, 为每个会话创建一个bean实例。
  • 请求(Rquest) : 在Web应用中, 为每个请求创建一个bean实例。
  • 使用@Scope注解
    • 用ConfigurableBeanFactory类中的常量来设置原型作用域
    • @Scope注解还可以配置proxyMode来解决将会话或者请求作用域的Bean注入到单例Bean中所遇到的问题
      • Bean是接口,proxyMode设置为ScopedProxyMode.INTERFACES, 这表明这个代理要实现接口, 并将调用委托给实现bean。
      • Bean如果是个类的要使用CGlib,设置ScopedProxyMode.TARGET_CLASS, 以此来表明要以生成目标类扩展的方式创建代理。

创建Bean的流程

  • Spring创建Bean的流程

Bean的生命周期

  • SpringBean的生命周期

三级缓存

  • 三级缓存
    • singletonObjects 它是我们最熟悉的朋友,俗称“单例池”“容器”,缓存创建完成单例Bean的地方。存放完全实例化且属性赋值完成的 Bean ,可以直接使用
    • earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.存放早期 Bean 的引用,尚未装配属性的 Bean
    • singletonFactories 映射创建Bean的原始工厂
  • 另外两个缓存
    • singletonsCurrentlyInCreation: bean 在创建的过程中都会存储在此,创建完成移出
    • alreadyCreated:存放至少被创建一次的 bean,不会重复。即标记 bean 是否创建完成

循环依赖

  • 问题出现在单例的作用域下,属性互相引用,在原型(Prototype)下的场景循环依赖会直接抛出异常
  • 依赖注入的方式不能全是构造器注入的方式(很多博客上说,只能解决setter方法的循环依赖,这是错误的)是可以被解决的
  • 总结
    • Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。
    • 当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
    • 当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时B的getBean(a)会从缓存中获取:
      • 第一步,先获取到三级缓存中的工厂;
      • 第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。
    • 当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束。

DI和IOC

IOC容器

  • 对象的创建交给外部容器完成,这个就做控制反转。
  • 控制是 bean 的创建、管理的权利,控制 bean 的整个生命周期。把这个权利交给了 Spring 容器,而不是自己去控制,就是反转。由之前的自己主动创建对象,变成现在被动接收别人给我们的对象的过程,这就是反转

依赖注入的方式

属性注入

  • 属性注入即通过setXXX( )方法注入bean的属性值或依赖对象。由于属性注入方式具有可选择性和灵活性高的特点,因此它也是实际开发中最常用的注入方式。Spring首先会调用bean的默认构造函数实例化bean对象,然后再通过反射的方法调用set方法来注入属性值。

构造器注入

  •  构造函数注入是除属性注入之外的另一种常用的注入方式,它可以保证一些必要的属性在bean实例化时就得到了设置,并在实例化后就可以使用。使用构造函数注入的前提是:bean必须提供带参的构造函数
  • 对于构造函数的注入,配置文件可以有以下几种方式
    • 按类型匹配入参
    • 按索引匹配入参
    • 联合使用类型和索引匹配入参
    • 通过自身类型反射匹配入参

工厂方法注入

  • 非静态工厂方法
    +
  • 静态工厂方法

AOP

  • 是面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,以减少系统的重复代码,降低模块间的耦合度。Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。

AOP术语

通知(Adivce)

  • 通知描述了切面要完成的任务及执行的时间点
  • 通知的五种类型
    • 前置通知(Before) : 在目标方法被调用之前调用通知功能;
    • 后置通知(After) : 在目标方法完成之后调用通知, 此时不会关心方法的输出是什么;
    • 返回通知(After-returning) : 在目标方法成功执行之后调用通知;
    • 异常通知(After-throwing) : 在目标方法抛出异常后调用通知;
    • 环绕通知(Around) : 通知包裹了被通知的方法, 在被通知的方法调用之前和调用之后执行自定义的行为。

连接点(Join Point)

  • 连接点是在应用执行过程中能够插入切面的一个点

切点(Poincut)

  • 一个切面并不需要通知应用的所有连接点。 切点有助于缩小切面所通知的连接点的范围
  • 切点就是定义了通知在何处插入通知

切面(Aspect)

  • 切面是通知和切点的结合。 通知和切点共同定义了切面的全部内容——它是什么, 在何时和何处完成其功能。

引入(Introduction )

  • 引入允许我们向现有的类添加新方法或属性。 例如, 我们可以创建一个Auditable通知类, 该类记录了对象最后一次修改时的状态。 这很简单, 只需一个方法, setLastModified(Date), 和一个实例变量来保存这个状态。 然后, 这个新方法和实例变量就可以被引入到现有的类中, 从而可以在无需修改这些现有的类的情况下, 让它们具有新的行为和状态。

织入(Weaving)

  • 织入是把切面应用到目标对象并创建新的代理对象的过程。 切面在指定的连接点被织入到目标对象中。 在目标对象的生命周期里有多个点可以进行织入:
    • 编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
    • 类加载期: 切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader) ,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5的加载时织入(load-timeweaving, LTW) 就支持以这种方式织入切面。
    • 运行期: 切面在应用运行的某个时刻被织入。 一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面的。

4种类型AOP支持

  • 基于代理的经典Spring AOP(太老已经淘汰)
  • 纯POJO切面:使用xml配置的方式
  • @AspectJ注解驱动的切面;
  • 注入式AspectJ切面(适用于Spring各版本)

切点表达式

  • AspectJ指 示器 描 述
    arg() 限制连接点匹配参数为指定类型的执行方法
    @args() 限制连接点匹配参数由指定注解标注的执行方法
    execution() 用于匹配是连接点的执行方法
    this() 限制连接点匹配AOP代理的bean引用为指定类型的类
    target 限制连接点匹配目标对象为指定类型的类
    @target() 限制连接点匹配特定的执行对象, 这些对象对应的类要具有指定类 型的注解
    within() 限制连接点匹配指定的类型
    @within() 限制连接点匹配指定注解所标注的类型(当使用Spring AOP时, 方 法定义在由指定的注解所标注的类里)
    @annotation 限定匹配带有指定注解的连接点
  • image-20210411180451855
  • 使用within()限制匹配

    • image-20210411180620950

使用注解创建切面

  • @Aspect : 声明了这是一个切面,切面种声明方法的注解

    • 注解 通知
      @After 通知方法会在目标方法返回或抛出异常后调用
      @AfterReturning 通知方法会在目标方法返回后调用
      @AfterThrowing 通知方法会在目标方法抛出异常后调用
      @Around 通知方法会将目标方法封装起来
      @Before 通知方法会在目标方法调用之前执行
  • 例如:

    • image-20210411180651465

    • 可以利用@Pointcut,@Pointcut注解能够在一个@AspectJ切面内定义可重用的切点

    • image-20210411180737008

启用AOP

  • 在配置类的类级别上通过使用EnableAspectJ-AutoProxy注解启用自动代理功能

  • image-20210411180956259

使用环绕通知

  • image-20210411181213594

  • 通知方法中可以做任何的事情, 当要将控制权交给被通知的方法时, 它需要调用ProceedingJoinPoint的proceed()方法 ,为了业务逻辑也可以不调用或者多次调用

处理通知中的参数

  • image-20210411182526631

  • image-20210411182535138

AOP原理

  • 使用代理设计模式是实现的,CGlib

AOP失效的情况

  • 内部方法调用
    • 解决方案:使用 AopContext.currentProxy():需开启 exposeProxy 配置
  • 非 Spring 管理的对象
  • 异步方法

Sring容器

bean工厂

应用上下文

Spring profile

@profile

  • 使用spring.profiles.active和spring.profiles.default来配置哪个profile被激活
    • 作为DispatcherServlet的初始化参数;
    • 作为Web应用的上下文参数;
    • 作为JNDI条目;
    • 作为环境变量;
    • 作为JVM的系统属性;
    • 在集成测试类上, 使用@ActiveProfiles注解设置

Spring事务

  • 级别
    PROPAGATION_REQUIRED–支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
    PROPAGATION_SUPPORTS–支持当前事务,如果当前没有事务,就以非事务方式执行。
    PROPAGATION_MANDATORY–支持当前事务,如果当前没有事务,就抛出异常。
    PROPAGATION_REQUIRES_NEW–新建事务,如果当前存在事务,把当前事务挂起。
    PROPAGATION_NOT_SUPPORTED–以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    PROPAGATION_NEVER–以非事务方式执行,如果当前存在事务,则抛出异常。

    PROPAGATION_NESTED–如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作是Spring所提供的一个特殊变量。 它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)

Spring事务

  • @Transactional

事务传播行为

  • 事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播

TransactionSynchronization

  • Spring 的 TransactionSynchronization 是事务管理中的核心机制,用于在事务的关键生命周期阶段(如提交、回滚、挂起、恢复等)触发回调逻辑。它允许开发者将自定义代码与事务的生命周期事件绑定,从而实现事务同步操作(如资源清理、日志记录、异步任务触发等)。
  • TransactionSynchronization 是一个接口,定义了一组回调方法,Spring 会在事务的不同阶段自动调用这些方法。它的核心作用是为事务提供扩展点,使开发者能够在事务提交前、提交后、回滚后等时机执行自定义逻辑。
  • 需要注册

Spring中的设计模式

  • 工厂
  • 单例
  • 适配器
  • 代理模式
  • 观察者模式:事件驱动模型 lientener
  • 模板方法:JDBC

SpeingMVC

StpingMVC的工作流程

  1. 客户端的所有请求都交给前端控制器 DispatcherServlet 来处理,它会负责调用系统的其他模块来真正处理用户的请求。
  2. DispatcherServlet 收到请求后,将根据请求的信息(包括 URL、HTTP 协议方法、请求头、请求参数、Cookie 等)以及 HandlerMapping 的配置找到处理该请求的 Handler(任何一个对象都可以作为请求的 Handler)。
  3. 在这个地方 Spring 会通过 HandlerAdapter 对该处理器进行封装。 HandlerAdapter 是一个适配器,它用统一的接口对各种 Handler 中的方法进行调用。
  4. Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给 DispatcherServlet,ModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。
  5. ModelAndView 的视图是逻辑视图,DispatcherServlet 还要借助 ViewResolver 完成从逻辑视图到真实视图对象的解析工作
  6. 当得到真正的视图对象后,DispatcherServlet 会利用视图对象对模型数据进行渲染。
  7. 客户端得到响应,可能是一个普通的 HTML 页面,也可以是 XML 或 JSON 字符串,还可以是一张图片或者一个 PDF 文件。

00Spring
https://x-leonidas.github.io/2022/02/01/11技术栈/spring/00Spring/
作者
听风
发布于
2022年2月1日
更新于
2025年6月17日
许可协议