文章目录
- 核心原理
- AnnotationConfigApplicationContext
- IoC 容器加载流程
- Spring 中如何创建一个对象
- Bean 的创建过程 (生命周期)
- 单例 原型
- 推断构造方法
- 依赖注入
- AOP 动态代理
- 判断是否需要 AOP 的大致流程
- CGLib 做 AOP 的大致流程
- 事务
- 事务代理对象执行方法的流程
- 事务注解排至失效的原因
- 为何下方加了 @Configuration 事务才能生效
- 核心概念
- BeanDefinition
- BeanDefinitionReader
- AnnotatedBeanDefinitionReader
- XmlBeanDefinitionReader
- ClassPathBeanDefinitionScanner
- BeanFactory
- DefaultListableBeanFactory
- ApplicationContext
- AnnotationConfigApplicationContext
- ClassPathXmlApplicationContext
- 国际化 MessageSource
核心原理
AnnotationConfigApplicationContext
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
我们很少按照上面的方式使用 Spring, 而是使用 Spring MVC, 或者 Spring Boot, 但是它们本质上都是基于这种方式的, 都需要在内部去创建一个 ApplicationContext
- SpringBoot 创建的是 AnnotationConfigApplicationContext
- SpringMVC 创建的是 XmlWebApplicationContext, 和 ClassPathXmlApplicationContext 类似都是基于 xml 的
AnnotationConfigApplicationContext 是研究学习的主类
IoC 容器加载流程
IoC容器加载流程可以分成两个步骤
- 准备, 注册一些底层基础架构工具类, 在容器初始化流程中使用, 然后注册配置类, 然后刷新容器
- 扫描, 将配置的各种 Bean 解析成为 BeanDefinition, 存入 BeanDefinitionMap
- 遍历 BeanDefinitionMap, 生产单例, 并缓存到单例池 singletonObjects
Spring 中如何创建一个对象
new AnnotationConfigApplicationContext(Config.class) 的过程就是 IoC 容器的加载流程, 扫描生成 BeanDefinition, 遍历生成单例 Bean. 最后人为调用 getBean(beanName) 去缓存中拿到对应的 Bean
Bean 的创建过程 (生命周期)
class -> 反射newInstance -> 原始对象 -> 依赖注入(属性赋值) -> 一堆Aware -> 初始化前(@PostConstruct) -> 初始化(InitailizedBean) -> 初始化后(AOP) -> 代理对象(代理对象.target=原始对象) -> Bean
- 实例化, 通过反射调用类的某个构造方法创建对象, 如果有多个构造方法, 会进行选择, 叫做推断构造方法
- 依赖注入(属性赋值), 遍历对象内的字段, 有 @Autowired 的自动赋值
- Aware 织入, 判断对象是否是(即类是否实现了) BeanNameAware, ApplicationContextAware 等各种 Aware, 是的话就强转调用结构定义的 set 方法
- @PostConstruct 处理, 判断是否有方法有该注解, 有的话则执行该方法
- InitializingBean 处理, 判断对象是否是该接口的实例, 是的话则执行接口定义的 afterPropertiesSet 方法
- 自定义的 init-method 方法
- 判断是否需要 AOP, 不需要时返回当前对象, 需要时则生成代理对象, 并返回
- 如果是单例 Bean 则存入单例池
单例 原型
Bean 的作用域分为单例和原型两种
- 单例: 每次 getBean 拿到的都是同一个对象, 首次创建后就会缓存到单例池
- 原型: 每次 getBean 都会重新创建一次, 不会放到单例池
懒加载的 Bean 在 IoC 容器加载时不会被创建和缓存, 在使用时才会创建和缓存
推断构造方法
实例化时默认使用无参构造器(写了有参默认就没有无参了, 除非显式定义)
有无参构造器就使用无参构造器, 没无参构造器则判断有几个有参构造器, 只有一个的话就使用这一个有参构造器, 有多个的话因为不能确认使用哪个, 所以报错找不到默认无参构造器
有多个有参构造器的话, 也可以加 @Autowired 来指定使用哪个有参构造器
有参构造器的参数对象哪里来?
- 先根据类型到容器中找, 如果只有一个则直接使用, 如果有多个则根据名称来过滤, 如果又匹配名称的则直接使用, 如果没有匹配的, 则报错
依赖注入
先按类型到容器中过滤, 匹配的如果只有一个则直接使用, 多个则再按名称匹配, 有匹配到的直接使用, 没有则报错
AOP 动态代理
在创建 Bean 的最后一步, 会判断是否需要做 AOP, 需要则做动态代理
判断是否需要 AOP 的大致流程
- 找出所有切面 Bean
- 遍历其中每个方法, 判断是否写了 @Before, @After 等注解
- 如果写了, 则判断其所对应的 PointCut 和当前创建的 Bean 是否匹配
- 如果匹配, 则说明当前 Bean 需要做 AOP
CGLib 做 AOP 的大致流程
- 生成继承原类的代理类, 代理类持有原类对象 target. 注意, 原类对象是经过 Bean 创建流程的, 包括依赖注入, 初始化等流程
- 代理类中覆盖原类中的方法, 最终会调用到 target 的对应方法, 但是会在前后把切面逻辑都加上, 大致如下
- 最后创建 Bean 返回的是持有原始对象的代理对象
public class UserService {
@Autowired
private OrderService orderService;
public void test() {
sout;
}
}
class UserServiceProxy extend UserService {
UserService target;
public void test() {
// @Before 的逻辑
target.test();
// @After 的逻辑
}
}
代理类继承自原始类, 所以原始类的字段在代理类中也有, 但是 Spring 并不会为代理类做依赖注入, 因为没有必要
代理对象仅仅用于强化原始对象的某方法, 如果在切面逻辑中需要用到原始对象的依赖注入的字段, 也可以通过 JoinPoint.getTarget()
拿到原始对象来操作, 而原始对象中各字段已经做过依赖注入了
事务
某方法添加了 @Transactional 注解后, 会在 AOP 阶段给本类生成代理类和代理对象
事务代理对象执行方法的流程
- 判断当前方法上有没有 @Transactional 注解
- 有的话由事务管理器创建一个数据库连接 (此连接不是来自连接池?)
- 设置连接的 autoCommit 为 false (无事务注解时, 连接是从连接池获取, 每执行一条 SQL 自动提交一次)
- 执行方法, 执行方法中的 SQL
- 调用连接的提交或回滚方法
事务注解排至失效的原因
事务是否会失效, 就看执行 @Transactional 注解方法的是哪个对象
- 代理对象: 不会失效
- 原始对象: 失效, 因为执行的就是普通方法, 没有代理对象做的强化了
最常见的例子, 类中有两个事务方法 a 和 b, 而 a 中会调用 b, 单独执行 a 和 b 事务都不会失效, 但是在 a 中执行 b 时, b 的事务注解上的配置会失效
因为执行 a 的流程是这样的, 拿到类的代理对象, 执行其 a, 先走切面逻辑, 创建连接, 设置不自动提交, 然后才执行 target.a 即原始对象的 a 方法, 此时的主体是原始对象而非代理对象. 执行到 b 方法时, 本质是 this.b, 主体还是原始对象, 并没有切面逻辑, 所以在 a 里面的 b 方法的事务注解配置都会失效
当然还有很多其他原因, 需要具体分析
其他的 AOP 失效很多也是一样的原因, 都是自调用导致的
有解决办法, 就是自引用, 类中依赖注入自身 self, 此时的 self 是代理对象, 在 a 中调用 b 的时候, 用 self.b, 这样主体是代理对象, 有切面强化逻辑, b 的事务配置就会生效了
为何下方加了 @Configuration 事务才能生效
@Configuration
public class Config {
@Bean
public TransactionManager transationManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource())
}
@Bean
public DataSource dataSource() {
return new DataSource();
}
}
- 不加时, 调用两次 dataSource() 生成两个不同的数据源, 最终事务管理器和模板使用了不同的数据源
- 加时, 会有特殊的处理, dataSource() 会被认为是一个 Bean, 传入两者的是同一个对象
在 JdbcTemplate 中获取连接时, 会检查当前是否为事务环境, 是的话会从 TransactionSynchronizationManager.getResource(dataSource);
中获取线程绑定的连接, 即事务管理器创建的那个连接, 需要使用同一个数据源对象才能拿到同一个连接, 这样事务管理器的提交和回滚操作才会对 JdbcTemplate 生效
核心概念
Spring 源码里有很多抽象和工具, 需要提前有一定了解, 读源码时能轻松一些
BeanDefinition
BeanDefinition 用来记录 Bean 配置的各种信息
- class,表示Bean类型
- scope,表示Bean作用域,单例或原型等
- lazyInit:表示Bean是否是懒加载
- initMethodName:表示Bean初始化时要执行的方法
- destroyMethodName:表示Bean销毁时要执行的方法
- 还有很多…
定义 Bean 的方式有申明式和编程式两种, 通过各种方式定义的 Bean 最终都会被解析为 BeanDefinition 并缓存起来
- 申明式:
- < bean/>
- @Bean
- @Component(@Service,@Controller)
- 编程式
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 生成一个BeanDefinition对象,并设置beanClass为User.class,并注册到ApplicationContext中 AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); beanDefinition.setBeanClass(User.class); context.registerBeanDefinition("user", beanDefinition); System.out.println(context.getBean("user"));
BeanDefinitionReader
用于根据某些规则将资源解析成为 BeanDefinition
AnnotatedBeanDefinitionReader
可以将某个类解析成为 BeanDefinition, 包括类上的注解(@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description)
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context);
// 将User.class解析为BeanDefinition
reader.register(User.class);
System.out.println(context.getBean("user"));
XmlBeanDefinitionReader
可以解析 < bean/> 标签配置的 Bean
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
int i = reader.loadBeanDefinitions("spring.xml");
System.out.println(context.getBean("user"));
ClassPathBeanDefinitionScanner
扫描器, 但是它的作用和 BeanDefinitionReader 类似, 它可以进行扫描, 扫描某个包路径, 对扫描到的类进行解析
如果扫描到的类上存在 @Component 注解, 那么就会把这个类解析为一个 BeanDefinition
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.scan("com.coder");
System.out.println(context.getBean("userService"));
BeanFactory
Spring 容器的根接口, Bean 工厂, 负责创建 Bean 和获取 Bean, 提供各种 getBean() 方法的定义
DefaultListableBeanFactory
BeanFactory 有一个最核心的实现类 DefaultListableBeanFactory, 可以直接当作 BeanFactory 来使用, 可以替代 ApplicationContext 来使用, 就是功能会少一点而已
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
beanFactory.registerBeanDefinition("user", beanDefinition);
System.out.println(beanFactory.getBean("user"));
DefaultListableBeanFactory 的架构体系如上, 有很多接口(能力)和类
- AliasRegistry: 支持别名功能, 定义了 alias 的注册/获取/判断/移除等功能, 在这里支持一个 BeanDefinition / Bean 有多个名称的功能
- SimpleAliasRegistry, 维护着
Map<String, String> aliasMap
, 在这里是 alias 与 beanName 的多对一关系, 便于从别名找到原名(别名也能起别名), 当然也可以从原名找到所有别名 - BeanDefinitionRegistry: 定义了 BeanDefinition 的注册/获取/存在/移除等功能
- SingletonBeanRegistry: 定义了单例 Bean 的注册/存在/获取/获取数量/获取名称等功能
- BeanFactory: 定义了 Bean 的获取/存在/获取别名/判断作用域等功能
- ListableBeanFactory: 扩展了 BeanFactory, 提供了方法用于枚举和检索容器中的 bean 实例, 可以方便地获取所有 bean 的名称、按类型获取 bean、按条件检索 bean 等操作
- HierarchicalBeanFactory: 扩展了 BeanFactory, 提供了层次化的容器结构和继承机制, 每个子容器可以独立管理自己的 bean 定义和实例。子容器可以继承父容器中定义的 bean,并可以在子容器中覆盖或扩展父容器中的 bean 定义。可以实现更好的模块化和组织化,并灵活管理和定制 bean 的定义和作用域
- AutowireCapableBeanFactory: 扩展了 BeanFactory, 提供了自动装配 bean 的能力, 可以实现依赖注入、自动装配和解耦等功能
- ConfigurableBeanFactory, 除了 BeanFactory Bean 之外,还提供用于配置 BeanFactory 的工具. 添加了设置父BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、设置Spring EL表达式解析器(表示该BeanFactory可以解析EL表达式)、设置类型转化服务(表示该BeanFactory可以进行类型转化)、可以添加BeanPostProcessor(表示该BeanFactory支持Bean的后置处理器),可以合并BeanDefinition,可以销毁某个Bean等等功能
- ConfigurableListableBeanFactory: 除了 ConfigurableBeanFactory 之外, 它还提供了分析和修改 Bean 定义以及预实例化单例的工具
- DefaultSingletonBeanRegistry: 主要用于管理和维护单例 bean 的注册和获取, singletonObjects 在这里
- FactoryBeanRegistrySupport: 主要用于支持 FactoryBean 的注册和获取, factoryBeanObjectCache 在这里
- AbstractBeanFactory: 功能已经很全面了, 但是不能自动装配和获取 beanNames, beanPostProcessors 在这里
- AbstractAutowireCapableBeanFactory: 拥有了自动装配的功能
- DefaultListableBeanFactory: 是 Spring 容器的一个关键组件,负责管理 BeanDefinition 的注册、合并和查找,以及 Bean 的创建、装配和销毁。它提供了丰富的功能和灵活的配置选项,是 Spring 应用程序中常用的 BeanFactory 实现之一, beanDefinitionMap 在这里
ApplicationContext
BeanFactory 有一个最核心的子接口 ApplicationContext, 其定义如下
public interface ApplicationContext
extends EnvironmentCapable,
ListableBeanFactory,
HierarchicalBeanFactory,
MessageSource,
ApplicationEventPublisher,
ResourcePatternResolver
- HierarchicalBeanFactory:拥有获取父BeanFactory的功能
- ListableBeanFactory:拥有获取beanNames的功能
- ResourcePatternResolver:资源加载器,可以一次性获取多个资源(文件资源等等)
- EnvironmentCapable:可以获取运行时环境(没有设置运行时环境功能)
- ApplicationEventPublisher:拥有广播事件的功能(没有添加事件监听器的功能)
- MessageSource:拥有国际化功能
ApplicationContext 的定位是 Spring 的应用上下文, 负责管理和组织应用程序的各个部分. 从代码层面来说, ApplicationContext 是一个 BeanFactory, 从架构层面来说, ApplicationContext 是比 BeanFactory 更加高级的存在, 它统御 BeanFactory, EnvironmentCapable, MessageSource 等这些组件完成相应的功能, BeanFactory 只是它的一个零件而已
照着这个思路来看, GenericApplicationContext 不继承 DefaultListableBeanFactory 而是将之作为一个属性, 从 BeanFactory 继承来的功能全部委托其持有的 DefaultListableBeanFactory 来执行, 就是非常合理的事情了
ApplicationContext 接口继承了 ListableBeanFactory 和 HierarchicalBeanFactory, 但它的定位是一个高位 BeanFactory, 只是聚焦于 BeanFactory 一定程度的基础功能即可, 并不需要中低层更强大的更加细节的全部功能
ApplicationContext 有两个重要的实现类
- AnnotationConfigApplicationContext
- ClassPathXmlApplicationContext
AnnotationConfigApplicationContext
- Lifecycle: 定义启动/停止生命周期控制方法的通用接口
- ConfigurableApplicationContext: 增加了,添加事件监听器、添加BeanFactoryPostProcessor、设置Environment,获取ConfigurableListableBeanFactory等功能
- AbstractApplicationContext: 实现通用上下文功能, 大名鼎鼎的 refresh 方法就在这里了
- GenericApplicationContext: 通用的应用上下文具体实现类
- AnnotationConfigRegistry: 用于注释配置应用程序上下文, 可以单独注册某个类为 BeanDefinition(可以处理该类上的 @Configuration @Bean 注解)
- AnnotationConfigApplicationContext: 是一种方便且强大的应用上下文实现,适用于注解驱动的开发方式。它通过扫描和处理注解,实现了自动化的 Bean 注册和装配,减少了繁琐的 XML 配置
ClassPathXmlApplicationContext
同样继承了 AbstractApplicationContext,但是相对于AnnotationConfigApplicationContext 而言,功能没有AnnotationConfigApplicationContext 强大,比如不能注册 BeanDefinition