Spring 详解

加载 bean

1
2
3
4
5
6
// Spring framework
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(AppConfig.class);
// 获取 Bean 对象
UserService userService=context.getBean("userService");
// 创建对象
UserService userService=new UserService();

​ context 容器中去获取对象

​ 加载顺序为 类路径 ==> 构造函数 ==> 生成普通对象 ==> 依赖注入 (@AutoAwired) ==> 初始化前(@PostConstruct) ==> 初始化(实现
initializingBean 接口,重写 afterPropertiesSet() 方法)==> 初始化后(AOP) ==> 代理对象 ==> 调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Aa{
@Autowired
private Bb bb;

public Aa(){}

public Aa(Bb bb){this.bb = bb}

public void test(){
sx();
}

public void sx(){}
}

构造函数

​ 若唯一构造器则则以唯一构造器构造对象;若多个构造器则以无参构造器构造对象,若无无参构造器则报错
​ 先通过类型寻找,找到 Map<beanName, bean>,再通过对象名去寻找

生成普通对象

​ 当有参构造时,如构造器 Aa(Bb bb) 此时 spring 会查询 IOC 容器中是否有对应对象,查询顺序为 查询类名,若该类名下不止一个对象,则查询对象名,若皆不
匹配则报错

依赖注入

​ 通过 @Autowired 等注解来判断属性是否需要赋值

初始化前

​ 通过注解 @postConstruct,指明当前方法为初始化前执行

初始化

​ 通过实现 initializingBean 接口,重写 afterPropertiesSet() 方法

初始化后

UserServiceProxy ==> 代理对象 ==> 代理对象.target = 普通对象

AOP 逻辑

  1. 查询所有切面 bean
  2. 查询所有切面方法
  3. 查询所有被切方法
  4. 通过匹配该类来判断是否需要生成代理对象
  5. 同时这步会生成缓存 map,用于存储切面方法

cglib 代理

  1. 继承代理类
  2. 重写切面方法
  3. 在切面方法中重新调用被切方法,此时为保证被代理对象的唯一性,会将被代理对象赋值给代理对象,作为其属性
1
2
3
4
5
6
7
8
9
10
11
12
// 代理对象简写
public AaProxy extends Aa{
private Aa target;
public void test(){
// 切面前方法
...
// 执行原普通对象即被代理对象的方法
target.test()
// 切面后方法
...
}
}

事务逻辑

  1. 将 jdbcTemplate 与事务管理器赋值同一数据库连接 (此时必须使用 configuration 注解,保证注入同一数据库连接)
  2. 在代理对象 test 方法中,首先拿到通过事务管理器数据库连接,再将自动连接改为 false,且在最后进行手动提交
  3. 在普通对象 (被代理对象) 的 test 方法中,因为用的是同一数据库连接,则也不会自动提交
事务失效

而在同类中的事务调用失效问题,即是因为 sx() 在调用在普通对象那一层,则 sx() 方法的调用对象为普通对象,而不是代理对象,所以并不能实现事务管理。

解决方法可以选择新建类,实现不同类之间调用,也可以使用自己注入自己,即

1
2
3
4
public class Aa{
@Autowried
private Aa aa;
}

​ 此时的 aa 则为代理对象,再调用 aasx() 方法即可

模拟 Spring 底层原理

1
2
3
4
5
6
// Spring framework
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(AppConfig.class);
// 获取 Bean 对象
UserService userService=context.getBean("userService");
// 创建对象
UserService userService=new UserService();

​ 在容器创建时,会去加载非懒加载单例 bean

  • 懒加载,通过 @Lazy 表明为懒加载,用时才加载
  • 原型 bean(多例),通过 @Scope(“prototype”),每次调用都会新生成

简单流程

​ AnnotationConfigApplicationContext 创建流程
​ 通过 AppConfig.class 获取需要扫描的包 ==> 通过包名和类加载器路径去获取 class 文件路径 ==> 遍历 class 文件 ==> 通过
class 文件获取基本信息 BeanDefinition,并存入 BeanDefinitionMap<beanName, BeanDefinition> ==> 如果是单例则直接创建
bean,存入 beanMap<beanName, bean>,通过注解判断 ==> createBean 时依赖注入,通过 Autowired 注解,直接 getBean(
beanName) ==> 同时 aop 也在这一步,通过 BeanPostProcessor 接口,将所有 beanPostProcessor 类存入 BeanPostProcessorList,
遍历执行 before 和 after 方法 ==> 通过 InitializingBean 接口,实现初始化时调用方法 afterPropertiesSet

核心概率解析

BeanDefinition

​ 表示为 Bean 定义,存在多个属性描述 Bean :

  • class, 表示 bean 类型
  • scope,表示作用域
  • lazyInit,是否为懒加载
  • initMethodName,表示 Bean 初始化要执行的方法
  • destroyMethodName,表示销毁时要执行的方法

生成 bean 对象方式:

  • @Component 注解
  • @Bean 注解
  • 标签
  • register 注册

BeanFactory 与 ApplicationContext 的区别

 1. ApplicationContext 继承了 beanFactory 接口
 2. ApplicationContext 还继承了很多其他接口,拥有兵工厂没有的功能,比如事件发布、获取环境化等功能
 3. **DefaultListableBeanFactory** 最常用的类,ApplicationContext 中也有调用

Bean 生命周期源码解析

​ 流程为:

生成 BeanDefinition

​ spring 启动时

合并 BeanDefinition

加载类

实例化前

实例化

BeanDefinition 的后置处理

实例化后

自动注入

处理属性

执行 Aware

初始化前

初始化

初始化后

总结 BeanPostProcessor