文章目录
- 一、Spring 框架
- 1.1 Spring 是什么
- 1.2 Spring 框架的优点
- 1.3 Spring 框架用到的设计模式
- 1.4 Spring 核心
- 1.5 IoC 是什么
- 1.6 DI 是什么
- 1.7 IoC 和 DI 的关系
- 1.8 AOP 是什么
- 1.9 AOP 底层原理
- 1.10 JDK Proxy 和 Cglib 区别
- 1.11 Spring AOP 和 AspectJ AOP 有什么区别
- 二、Spring Bean
- 2.1 Bean 的作用域
- 2.2 Bean 的生命周期
- 2.3 Bean 的线程安全问题
- 2.4 Bean 循环依赖
- 2.5 Bean 循环依赖示例
- 2.6 三级缓存
- 三、Spring 事务
- 3.1 Spring 管理事务的方式
- 3.2 Spring 事务传播行为
- 3.3 Spring 事务隔离级别
- 3.4 @Transactional 原理
- 3.5 @Transactional 失效
- 3.6 @Transactional(rollbackFor = Exception.class)
- 四、Spring Boot
- 4.1 Spring Boot 理解
- 4.2 Spring Boot 优点
- 4.3 SpringBoot 自动装配
- 4.4 SpringBoot 如何实现自动装配
- 4.5 @EnableAutoConfiguration 注解
- 4.6 @Configuration 注解
- 4.7 @Autowired 和 @Resource 的区别
- 4.8 如何实现一个 starter
一、Spring 框架
1.1 Spring 是什么
- Spring 是一个开源的轻量级 Java 开发框架,旨在提高系统的开发效率和可维护性。
- Spring 的核心思想是不重复造轮子,开箱即用。
- Spring 的核心模块:Spring Core、Spring Web、Spring AOP、Spring Test、数据访问等。
1.2 Spring 框架的优点
- 容器化:Spring 是一个容器,包含并且管理应用对象的生命周期。
- 组件化:Spring 实现了使用简单的组件组合成一个复杂的应用。
- 非侵入式:基于 Spring 开发的应用中的对象可以不依赖于 Spring 的 API。
- 依赖注入:DI 依赖注入,控制反转(IoC)最经典的实现。
- 面向切面编程:Aspect Oriented Programming – AOP。
1.3 Spring 框架用到的设计模式
- 工厂模式:通过 BeanFactory、ApplicationContext 创建 bean 对象属于工厂模式。
- 单例/原型模式:创建 bean 对象设置作用域时,就可以声明单例模式或原型模式。
- 代理模式:Spring AOP 功能的实现。
- 责任链模式:Spring AOP 拦截器的执行。
- 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
- 模板方法模式:Spring 中以 Template 结尾的类就使用到了模板方法模式。
- 策略模式:在创建代理类时如果代理的是接口使用JDK自身的动态代理,如果不是则使用的是cglib实现动态代理。
1.4 Spring 核心
Spring 的核心是提供了一个容器,通常称为 Spring 应用上下文,它们会创建和管理应用组件。这些组件也可以称为 bean,会在 Spring 应用上下文中装配在一起,从而形成一个完整的应用程序。
将 bean 装配在一起的行为是基于依赖注入模式实现的。此时组件不会再去创建和管理它所依赖的组件,而是由容器来创建和维护所有的组件,并将其注入到需要它们的bean中。
1.5 IoC 是什么
IoC 又叫控制反转,是一种设计思想,而不是一个具体的技术实现。Ioc 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理,有效的降低了代码的耦合度,提高了程序的可维护性。
1.6 DI 是什么
DI 又叫依赖注入,是指 bean 之间的依赖关系由容器在运行时决定,即容器动态的将依赖关系注入到 bean 中,提高了组件的复用性。在 Java 中依赖注入有以下三种实现方式:构造器注入、Setter 方法注入、接口注入。
1.7 IoC 和 DI 的关系
依赖注入(DI )是实现控制反转(IoC)的方法和手段,在 JDK1.3 之后增加了反射机制,允许程序在运行的时动态的生成对象、执行对象的方法、改变对象的属性,Spring 就是通过反射来实现注入的。
1.8 AOP 是什么
AOP 是指面向接口编程,是对面向对象的一种补充,在不改变业务代码的基础上,在程序运行期间,动态的将某段代码插入指定方法的指定位置,使业务逻辑和系统模块解耦,提高了程序的复用性。
1.9 AOP 底层原理
AOP 是基于动态代理实现的,如果被代理的对象实现了接口,那么 AOP 会使用 JDK Proxy 去创建代理对象,如果对象没有实现接口,AOP 会使用 Cglib 生成一个被代理对象的子类。
1.10 JDK Proxy 和 Cglib 区别
- 实现不同:JDK Proxy 是由 Java 内部的反射机制实现的,Cglib 底层则是借助 ASM 来实现的。
- 范围不同:JDK Proxy 代理的前提必须是实现接口,Cglib 不能对声明为 final 的类进行代理。
- 效率不同:JDK1.8 之前 Cglib 代理效率高,JDK1.8 的时候 JDK Proxy 效率更高。
- 优势不同:反射机制在生成类的过程中比较高效,ASM 在生成类之后的相关操作比较高效。
1.11 Spring AOP 和 AspectJ AOP 有什么区别
- Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。
- Spring AOP 基于动态代理,而 AspectJ 基于字节码操作。
- Spring AOP 相对来说更简单,AspectJ 功能更加强大。
- 当切面过多时,AspectJ 比 Spring AOP 快很多。
二、Spring Bean
2.1 Bean 的作用域
- 单例(Singleton):整个应用程序,只创建 bean 的一个实例,Spring 默认是单例的。
- 原型(Prototype):每次注入都会创建一个新的 bean 实例。
- 请求(Request):每个请求创建一个 bean 实例,只在 Web 系统中有效。
- 会话(Session):每个会话创建一个 bean 实例,只在 Web 系统中有效。
- 公共(global-session):全局 session 作用域,Spring5 已经没有了。
2.2 Bean 的生命周期
- 实例化 Instantiation:当容器启动时,通过构造器或者工厂方法创建Bean实例。
- 属性赋值 Populate:创建实例后,容器会通过依赖注入或者其他方式,将配置的属性值和引用注入到Bean中。
- 初始化 Initialization:在属性设置完成后,容器会调用Bean的初始化方法。
- 销毁 Destruction:当应用程序关闭或者手动移除Bean时,容器会调用Bean的销毁方法。
// 1.AbstractAutowireCapableBeanFactory#createBean()
// 2.AbstractAutowireCapableBeanFactory#createBean()
// 3.AbstractAutowireCapableBeanFactory#doCreateBean()
// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
// 实例化阶段
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 属性赋值阶段
populateBean(beanName, mbd, instanceWrapper);
// 初始化阶段
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
2.3 Bean 的线程安全问题
当多个线程同时操作对象的的非静态成员变量时,会存在线程安全问题,可以使用同步机制或者 ThradLocal 来解决。
- 同步机制是通过对象的锁保证同一时间只有一个线程访问变量(时间换空间)。
- ThreadLocal 为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突(空间换时间)。
2.4 Bean 循环依赖
所谓的循环依赖,就是两个或两个以上的 bean 互相依赖对方,最终形成闭环。比如 A 依赖 B 而 B 也依赖 A。单例模式的 Bean 可以通过三级缓存来解决循环依赖问题。
构造器注入构成的循环依赖是无法解决的,只能抛出 BeanCurrentlyInCreationException 异常表示循环依赖。Spring 解决循环依赖依靠的是 bean 的中间态,而中间态指的是已经实例化,但还没初始化的状态。
2.5 Bean 循环依赖示例
以 A、B 对象相互依赖为例说明创建过程:
- 从容器中获取实例 A 时,因为 A 不存在,所以会走 A 创建流程。
- 实例化 A 并将其放到二级缓存 earlySingletonObjects 中,然后对 A 进行初始化。
- 因为实例 A 依赖实例 B,所以从容器中获取实例 B,因为 B 不存在,所以会走 B 创建流程。
- 实例化 B 然后进行初始化,因为 B 依赖 A 所以从容器中获取 A,因为 A 在二级缓存中,所以 B 可以完成初始化并放在一级缓存中。
- 因为 B 已经初始化完成并返回,因此 A 也初始化成功,并且从二级缓存移动到一级缓存中。
2.6 三级缓存
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry 类。
- 一级缓存:singletonObjects 存放已经经历完整生命周期的 Bean 对象。
- 二级缓存:earlySingletonObjects 存放早期暴露出来的 Bean 对象(属性未初始化)。
- 三级缓存:singletonFactories 存放可以生成 Bean 的工厂。
三、Spring 事务
3.1 Spring 管理事务的方式
- 编程式事务:在代码中硬编码,通过 TransactionTemplate 或 TransactionManager 手动管理事务。
- 声明式事务 : 基于 @Transactional 注解,底层通过 AOP 实现。
3.2 Spring 事务传播行为
支持当前事务:
- required:如果当前存在事务则加入该事务,如果当前没有事务则创建一个新的事务(默认级别)。
- supports:如果当前存在事务则加入该事务,如果当前没有事务则以非事务的方式继续运行。
- mandatory:如果当前存在事务则加入该事务,如果当前没有事务则抛出异常。
不支持当前事务: - requires_new:总是创建一个新的事务,如果当前存在事务,则把当前已存在的事务挂起。
- not_supported:以非事务方式运行,如果当前存在事务则把当前事务挂起。
- _never:以非事务方式运行,如果当前存在事务则抛出异常。
其他情况: - nested:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行,如果当前没有事务则创建一个新的事务执行。
3.3 Spring 事务隔离级别
- default:使用后端数据库默认的隔离级别,默认为此事务级别。
- read_uncommitted:允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
- read_committed:允许读取并发事务已经提交的数据,可能会导致幻读或不可重复读。
- repeatable_read:可以阻止脏读和不可重复读,但幻读仍有可能发生。
- serializable:最高的隔离级别,所有的事务依次逐个执行,可以防止脏读、不可重复读以及幻读,但是这将严重影响程序的性能。
3.4 @Transactional 原理
@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。
3.5 @Transactional 失效
在同一个类中,没有注解的方法内部调用有注解的方法时,会导致事务会失效,这是由于Spring AOP 代理的原因造成的,因为只有当被注解的方法在类以外被调用的时候,Spring 事务管理才生效。
3.6 @Transactional(rollbackFor = Exception.class)
@Transactional 注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,在 @Transactional 注解中如果不配置 rollbackFor 属性,那么事务只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception 可以让事务在遇到非运行时异常时也回滚。
四、Spring Boot
4.1 Spring Boot 理解
Spring Boot 用来简化 Spring 应用程序的初始搭建以及开发过程。
4.2 Spring Boot 优点
- 自动配置:尽可能自动配置Spring和第三方库,无需其他配置。
- 独立运行:Spring Boot内嵌了servlet容器,只要打成一个可执行的jar包就能独立运行。
- 应用监控:Spring Boot提供一系列端点可以监控服务及应用,做健康检测。
- 无代码生成和XML配置:配置过程中无代码生成,也无需XML配置文件就能完成所有配置。
4.3 SpringBoot 自动装配
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。
4.4 SpringBoot 如何实现自动装配
SpringBoot 实现自动装配依赖核心注解 SpringBootApplication,这个注解明确表明这是一个Spring Boot应用,并且包含了3个其他的注解:
- @EnableAutoConfiguration:启用 Spring Boot 自动配置机制。
- @SpringBootConfiguration:将该类声明为配置类,会为 Spring 应用上下文提供 bean,这个注解实际上是 @Configuration 注解的特殊形式。
- @ComponentScan:启用组件扫描,Spring 会自动发现 bean 并将它们注册到 Spring 应用上下文中。
4.5 @EnableAutoConfiguration 注解
@EnableAutoConfiguration 通过引入 AutoConfigurationImportSelector 类实现核心功能,该类实现了 ImportSelector.selectImports() 方法,该方法通过 SpringFactoriesLoader 加载 META-INF/spring.factories 配置文件,获取所有符合条件的类,通过动态代理的方式注入到 Spring 容器中,从而实现自动装配。
4.6 @Configuration 注解
@Configuration 注解会告知 Spring 这是一个配置类,会为 Spring 应用上下文提供 bean。配置类的方法使用 @Bean 注解进行了标注,表明这些方法所返回的对象会以 bean 的形式添加到 Spring 的应用上下文中。
4.7 @Autowired 和 @Resource 的区别
两个注解都可以用来装配 bean,都可以写在字段和 setter 方法上。
- 来源不同:@Autowired 源于 spring,@Resource 源于 Java。
- 装配方式不同:@Autowired 默认按照类型装配,@Resource 默认按照名称进行装配。
- 注解参数不同:@Autowired 只包含一个参数 required,表示是否开启自动注入;@Resource 包含七个参数,其中最重要的是 name 和 type。
4.8 如何实现一个 starter
- 创建一个 spring-boot 项目,引入需要的相关依赖;
- 创建一个配置类,配置需要注入 spring 的类;
- 在 resource 目录下创建 META-INF/spring.factories 文件,注入配置类;
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.thread.configuration.ThreadConfiguration
@Configuration
public class ThreadConfiguration {
@Bean
@ConditionalOnClass(ThreadPoolExecutor.class)
public ThreadPoolExecutor threadPoolExecutor() {
return new ThreadPoolExecutor(10, 10, 10L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100));
}
}