1. Spring Framework总览
1.1 特性总览
1.1.1 核心特性
-
IoC 容器
-
Spring 事件
-
资源管理
-
国际化
-
校验
-
数据绑定
-
类型转换
-
Spring 表达式
-
面向切面编程
1.1.2 数据存储
-
JDBC
-
事务抽象
-
DAO 支持
-
O/R 映射
-
XML 编列
1.1.3 Web 技术
-
Web Servlet 技术
-
Spring MVC
-
WebSocket
-
SockJS
-
-
Web Reactive 技术
-
Spring WebFlux
-
WebClient
-
WebSocket
-
1.1.4 技术整合
-
远程调用
-
Java 消息服务(ActiveMQ)
-
Java 连接架构
-
Java 管理扩展
-
Java 邮件客户端
-
本地任务
-
本地调度
-
缓存抽象
-
Spring 测试
1.1.5 测试
-
模拟对象
-
TestContext 框架
-
Spring MVC 测试
-
Web 测试客户端
1.2 Spring 版本特性
Spring Framework 版本 | Java 标准版 | Java 企业版 | 特性 |
---|---|---|---|
1.x | 1.3+ | J2EE 1.3+ | 1.3 引入接口动态代理 |
2.x | 1.4.2+ | J2EE 1.3+ | |
3.x | 5+ | J2EE 1.4 和 Java EE 5 | Java 5 引入注解 |
4.x | 6+ | Java EE 6 和 7 | |
5.x | 8+ | Java EE 7 |
1.3 Spring 模块化设计
-
Spring-core
-
Spring-aop
-
Spring-aspects
-
Spring-context
-
Spring-context-indexer
-
Spring-context-support
-
Spring-expression
-
Spring-instrument(Java agent支持)
-
Spring-jcl(日志框架)
-
Spring-jdbc
-
Spring-jms
-
Spring-messaging
-
Spring-orm
-
Spring-oxm(xml编列)
-
Spring-test
-
Spring-tx(事务)
-
Spring-web
-
Spring-webflux
-
Spring-webmvc
-
Spring-websocket
1.4 Java 语言特性
Java 5 语法特性
语法特性 | Spring 支持版本 | 代表实现 |
---|---|---|
注解 | 1.2+ | @Transaction |
枚举 | 1.2+ | Propagation(事件传播) |
for-each 语法 | 3.0+(3.0 开始强制要求 Java 5) | AbstractApplicationContext |
自动装箱 | 3.0+ | |
泛型 | 3.0+ | ApplicationListener |
Java 6 语法支持
语法特性 | Spring 支持版本 | 代表实现 |
---|---|---|
接口 @Override | 4.0+ |
Java 7 语法特性
语法特性 | Spring 支持版本 | 代表实现 |
---|---|---|
Diamond 语法 | 5.0+ | DefaultListableBeanFactory |
try-with-resource 语法 | 5.0+ | ResourceBundleMessageSource |
Java 8 语法特性
语法特性 | Spring 支持版本 | 代表实现 |
---|---|---|
Lambda 语法 | 5.0+ | PropertyEditorRegistrySupport |
1.5 面试题
1.5.1 什么是 Spring Framework
Spring 是使得创建企业级 Java 应用变得简单的框架,框架核心是 Ioc 和 AOP,当然还支持包括事件、国际化、资源管理、校验、数据绑定以及类型转换等功能,提供你开发企业级应用所需的一切功能
1.5.2 Spring Framework 有哪些核心模块
-
spring-core:Spring 基础 API 模块,如资源管理,泛型处理
-
spring-beans:Spring Bean 相关,如依赖查找,依赖注入
-
spring-aop:Apring AOP 处理,如动态代理,AOP字节码提升
-
spring-context:事件驱动、注解驱动、模块驱动
-
spring-expression:Spring 表达式语言模块
1.5.3 Spring Framework 的优势和不足是什么
2. 重新认识 IOC
2.1 IoC 发展简介
2.2 IoC 主要实现策略
-
依赖查找
-
依赖注入
2.3 IoC 容器的职责
-
依赖处理
-
依赖查找
-
依赖注入
-
-
生命周期管理
-
容器
-
托管的资源(Java Beans 或其他资源)
-
-
配置
-
容器
-
外部化配置
-
托管的资源(Java Beans 或其他资源)
-
2.4 IoC 容器的实现
-
Java SE
-
Java Beans
-
Java ServiceLoader SPI
-
JNDI(Java Naming and Directory Interface)
-
-
Java EE
-
EJB(Enterprise Java Beans)
-
Servlet
-
-
开源
-
Apache Avalon
-
PicoContainer
-
Google Guice
-
Spring Framework
-
2.5 传统 IoC 容器实现
Java Beans 作为 IoC 容器,特性如下:
-
依赖查找
-
生命周期管理
-
配置元信息
-
事件
-
自定义
-
资源管理
-
持久化
规范:
-
JavaBeans:Java SE Desktop Technologies
-
BeanContext:Extensible Runtime Containment and Services Protocol forJavaBeans Version 1.0
2.6 轻量级 IoC 容器
2.7 依赖查找 VS. 依赖注入
类型 | 依赖处理 | 实现便利性 | 代码侵入性 | API 依赖性 | 可读性 |
---|---|---|---|---|---|
依赖查找 | 主动获取 | 相对繁琐 | 侵入业务代码 | 依赖容器 API | 良好 |
依赖注入 | 被动提取 | 相对便利 | 低侵入性 | 不依赖容器 API | 一般 |
2.8 构造器注入 VS. Setter 注入
Spring 团队推荐使用构造器注入,对象是不可变的确保依赖对象不是空的,缺点是参数过多,代码看起来不是很良好,需要适当进行重构
Setter 注入应该仅用于可选对象的注入
2.9 面试题精选
2.9.1 什么是 IoC
简单的说,IoC 是控制反转,类似于好莱坞原则,主要有依赖查找和依赖注入实现
2.9.2 依赖查找和依赖注入的区别
依赖查找是主动或手动的依赖查找方式,通常需要依赖容器或标准 API 实现。而依赖注入则是手动或自动依赖绑定的方式,无需以来特定的容器和 API
2.9.3 Spring 作为 IoC 容器有什么优势
-
典型的 IoC 管理,依赖查找和依赖注入
-
AOP 抽象
-
事务抽象
-
事件机制
-
SPI 扩展
-
强大的第三方整合
-
易测试性
-
更好的面向对象
3. Spring IOC容器概述
3.1 依赖查找
-
根据 Bean 名称查找
-
实时查找(getBean("xxx"))
-
延迟查找(ObjectFactory)
-
-
根据 Bean 类型查找
-
单个 Bean 对象(getBean(xxx.class))
-
集合 Bean 对象(getBeansByType(xxx.class))
-
-
根据 Bean 名称 + 类型查找(getBean("xxx", xxx.class))
-
根据 Java 注解查找(getBeansWithAnnotation(xxx.class))
-
单个 Bean 对象
-
集合 Bean对象
-
3.2 依赖注入
-
根据 Bean 名称注入(ref="xxx"或者autowire="byName")
-
根据 Bean 类型注入
-
单个 Bean 对象
-
集合 Bean 对象(util:list、util:set等,autowire="byType")
-
-
注入容器内建 Bean 对象(Environment等)
-
注入非 Bean 对象(BeanFactory、ApplicationContext等)
-
注入类型
-
实时注入
-
延迟注入(ObjectFactory)
-
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"> <!-- 集合注入,通过类型注入 --> <bean id="userRepository" class="xxx.xxx.xxx.UserRepository" autowire="byType"/> <!-- <property name="users"> <util:list> <ref bean="superUser"/> <ref bean="user"/> </util:list> </property> --> </beans>
3.3 依赖来源
-
自定义 Bean(XML、注解等)
-
容器内建 Bean 对象(environment对象等,通过手动注册BeanDefinition)
-
容器内建依赖(BeanFactory等,直接添加到BeanFactory中)
3.4 配置元信息
-
Bean 定义配置(BeanDefinition)
-
基于 XML 文件
-
基于 Properties 文件
-
基于 Java 注解
-
基于 Java API
-
-
IoC 容器配置
-
基于 XML 文件
-
基于 Java 注解
-
基于 Java API
-
-
外部化配置(@Value)
-
基于 Java 注解
-
3.5 BeanFactory和ApplicationContext
ApplicationContext 是 BeanFactory 子接口,功能上是 BeanFactory 的超集,并且在AbstractRefreshableApplicationContext 中通过组合的方式将 BeanFactory 组合进来,通过代理的方式将 BeanFactory 重写的方法指定给内部组合的 BeanFactory 来执行,比如在 AbstractApplicationContext 中 getBean()
方法都是这样实现的:
//--------------------------------------------------------------------- // Implementation of BeanFactory interface //--------------------------------------------------------------------- @Override public Object getBean(String name) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(name); } @Override public <T> T getBean(String name, Class<T> requiredType) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(name, requiredType); } @Override public Object getBean(String name, Object... args) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(name, args); } @Override public <T> T getBean(Class<T> requiredType) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(requiredType); } @Override public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(requiredType, args); } @Override public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) { assertBeanFactoryActive(); return getBeanFactory().getBeanProvider(requiredType); } @Override public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) { assertBeanFactoryActive(); return getBeanFactory().getBeanProvider(requiredType); } @Override public boolean containsBean(String name) { return getBeanFactory().containsBean(name); } @Override public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { assertBeanFactoryActive(); return getBeanFactory().isSingleton(name); } @Override public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { assertBeanFactoryActive(); return getBeanFactory().isPrototype(name); } @Override public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException { assertBeanFactoryActive(); return getBeanFactory().isTypeMatch(name, typeToMatch); } @Override public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException { assertBeanFactoryActive(); return getBeanFactory().isTypeMatch(name, typeToMatch); } @Override @Nullable public Class<?> getType(String name) throws NoSuchBeanDefinitionException { assertBeanFactoryActive(); return getBeanFactory().getType(name); } @Override @Nullable public Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException { assertBeanFactoryActive(); return getBeanFactory().getType(name, allowFactoryBeanInit); } @Override public String[] getAliases(String name) { return getBeanFactory().getAliases(name); }
即 BeanFactory 是 Spring 最基本的容器,ApplicationContext 拥有 BeanFactory 所有功能,是 BeanFactory 的超集,同时还有许多自身特有的功能,功能在下一小节叙述。
3.6 ApplicationContext作用
ApplicationContext 除了提供 IoC 容器角色,还提供以下功能:
-
面向切面(AOP)
-
配置元信息(Configuration Metadata)
-
资源管理(Resources)
-
事件(Event)
-
国际化(i18n)
-
注解(Annotation)
-
Environment 抽象(Environment Abstraction)
3.7 BeanFactory 和 ApplicationContext选择
-
BeanFactory 是 Spring 的底层 IoC 容器
-
ApplicationContext 是具备应用特性的 BeanFactory 超集
3.7.1 BeanFactory使用
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); int beanDefinitionCount = reader.loadBeanDefinitions("classpath:spring.xml"); // 接下来就能使用factory了
3.7.2 ApplicationContext使用
@Configuration public class AnnotationContextAsIoCContainerDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册配置类 context.register(AnnotationContextAsIoCContainerDemo.class); context.refresh(); // 接下来就能使用context了 } @Bean public User user() { return new User("foo", 18); } }
3.8 IOC 容器启停过程
3.8.1 启动
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // Prepare this context for refreshing. // 记录启动时间,初始化PropertiySources,校验Environment对象等 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 创建BeanFactory -> DefaultListableBenaFactory,在AbstractRefreshableApplicationContext中创建,此处只完成实例化,还未完成初始化 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. // 添加一些内建的bean(environment、systemProperties等)以及依赖(BeanFactory、ResourceLoader等) prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. // 注册针对容器的扩展点(对容器进行调整) postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // Invoke factory processors registered as beans in the context. // 调用针对容器的扩展点 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 注册针对bean的扩展点(对bean进行调整) registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // Initialize message source for this context. // 初始化国际化信息 initMessageSource(); // Initialize event multicaster for this context. // 初始化事件发布 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. // 注册监听 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 实例化所有单例对象 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // 发布事件 finishRefresh(); } 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(); contextRefresh.end(); } } }
3.8.3 停止
public void close() { synchronized (this.startupShutdownMonitor) { // 关闭 doClose(); // If we registered a JVM shutdown hook, we don't need it anymore now: // We've already explicitly closed the context. if (this.shutdownHook != null) { try { // 移除shutdownhook Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException ex) { // ignore - VM is already shutting down } } } } protected void doClose() { // Check whether an actual close attempt is necessary... if (this.active.get() && this.closed.compareAndSet(false, true)) { if (logger.isDebugEnabled()) { logger.debug("Closing " + this); } if (!NativeDetector.inNativeImage()) { LiveBeansView.unregisterApplicationContext(this); } try { // Publish shutdown event. publishEvent(new ContextClosedEvent(this)); } catch (Throwable ex) { logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex); } // Stop all Lifecycle beans, to avoid delays during individual destruction. if (this.lifecycleProcessor != null) { try { this.lifecycleProcessor.onClose(); } catch (Throwable ex) { logger.warn("Exception thrown from LifecycleProcessor on context close", ex); } } // Destroy all cached singletons in the context's BeanFactory. // 销毁所有Bean destroyBeans(); // Close the state of this context itself. // 关闭所有BeanFactory closeBeanFactory(); // Let subclasses do some final clean-up if they wish... onClose(); // Reset local application listeners to pre-refresh state. if (this.earlyApplicationListeners != null) { this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } // Switch to inactive. this.active.set(false); } }
3.9 面试题
3.9.1 什么是 Spring IOC 容器
依赖注入、依赖查找以及一些其他特性共同构成了 Spring IOC 容器,官方答案如下:
Spring 框架实现 IoC 原则,IoC 也称之为 DI(依赖注入,此处官方文档有点不太严谨,因为 IoC 不只是依赖注入,还有依赖查找),这个过程会伴随状态的依赖,此处主要指通过构造器参数、工厂方法或者是属性的setter方法去注入一些其他的对象,从而实现依赖注入,容器会将依赖注入的信息放到创建的 Bean 中来。Spring 框架是一个 IoC 容器的实现,DI 是它的实现原则
官方文档中 IoC 不提以来查找的原因是由于依赖查找在 Java EE时 代已经实现了,DI 部分实现,只不过要和 Java EE 传统容器加以区别
3.9.2 BeanFactory 和 FactoryBean 区别
BeanFactory 是 IoC 的底层容器,ApplicationContext 中通过继承 BeanFactory 接口并且在实现类中通过组合 BeanFactory 对象方式提供 BeanFactory 所有功能以及新增自己的一些功能(代理)
FactoryBean 则是创建 Bean 的一种方式,帮助实现复杂的初始化逻辑,通过 FactoryBean 创建的 Bean 是否有会包含 Bean 生命周期?后续进行讲解。
3.9.3 Spring IoC 容器启动时做了哪些准备
IoC 配置元信息读取和解析(xml、注解等读取和解析)、IoC 容器生命周期、Spring 事件发布、国际化等
4. Spring Bean 基础
4.1 什么是 BeanDefinition
BeanDefinition 是 Spring Framework 中定义Bean 的配置元信息的接口,包含:
-
Bean 类名(全限定名)
-
Bean 行为配置元素(作用域、自动绑定模式、生命周期)
-
其他 Bean 引用,依赖
-
配置设置,属性
4.2 BeanDefinition 元信息
4.2.1 元信息说明
属性(Property) | 说明 |
---|---|
Class | Bean 全类名,必须是具体类,不能用抽象类或接口 |
Name | Bean 的名称或者 ID |
Scope | Bean 作用域(Singleton、prototype) |
Constructor arguments | Bean 构造器参数(依赖注入) |
Properties | Bean 属性设置(依赖注入) |
AutoWiring mode | Bean 自动绑定模式(byType、byName等) |
Lazy initialization mode | Bean延迟初始化模式(延迟或者非延迟) |
Initialization method | Bean 初始化回调方法 |
Destruction method | Bean 销毁回调方法名称 |
4.2.2 BeanDefinition构建
-
通过 BeanDefinitionBuilder
-
通过AbstractBeanDefinition 以及派生类
// 通过 BeanDefinitionBuilder 创建 BeanDefinition BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class); AbstractBeanDefinition beanDefinition = builder.addPropertyValue("name", "foo") .addPropertyValue("age", 28).getBeanDefinition(); beanDefinition.addQualifier(new AutowireCandidateQualifier("user")); beanDefinition.setScope("singleton"); // 通过 AbstractBeanDefinition 以及派生类创建 BeanDefinition GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition(); genericBeanDefinition.setBeanClass(User.class); MutablePropertyValues values = new MutablePropertyValues(); values.add("name", "foo") .add("age", 29); genericBeanDefinition.setPropertyValues(values);
4.3 id 和 name
4.3.1 Bean 标识符
每个 Bean 拥有一个或者多个标识符(identifier),这些标识符在 Bean 所在的容器必须是唯一的。通常发,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名扩充,依赖注入以及依赖查找时可以使用
在基于 XML 的配置元信息中,开发人员可用 id 或者 name 属性来规定 Bean 的标识符。通畅 Bean 的标识符由字母组成,允许出现特殊字符,如果想要引入 Bean 的别名,可在 name 属性使用半角逗号或分号来间隔
Bean 的 id 或 name 属性并非必须指定,如果留空的话,容器会为 Bean 自动生成一个唯一的名称。Bean 的命名景观没有限制,不过官方建议采用驼峰的方式,更符合 Java 的命名约定。
4.3.2 Bean 名称生成器(BeanNameGenerator)
由 Spring Framework 2.0.3 引入,框架内建两种实现:
-
DefaultBeanNameGenerator:默认通用 BeanNameGenerator 实现
-
AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator 实现,起始于 Spring Framework 2.5
4.3.3 id 和 name
id 和 name 都是 bean 的标识符,id 最多一个,name 可有多个
4.4 别名
别名的价值:
-
复用现有的 BeanDefinition
-
更具有场景化的命名方法,比如:
-
<alias name="myApp-dataSource" alias="subsystemA-dataSource">
-
<alias name="myApp-dataSource" alias="subsystemB-dataSource">
-
4.5 注册 Bean
4.5.1 注册方式
-
XML 配置元信息
-
<bean id="", class="">
-
-
Java 注解配置元信息
-
@Bean
-
@Component
-
@Import
-
-
Java API 配置元信息
-
命名方式:BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)
-
非命名方式:BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition, BeanDefinitionRegistry)
-
配置类方式:AnnotatedBeanDefinitionReader#register(Class...)(就是注解注册中通过context.register("config", Config.class)方法进行注册)
-
4.5.2 注解注册
@Import(AnnotationBeanDefinitionDemo.Config.class) public class AnnotationBeanDefinitionDemo { public static void main(String[] args) { // 创建context try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { // 注册配置类 context.register(AnnotationBeanDefinitionDemo.class); context.registerBean("config", Config.class); // 刷新容器(启动容器) context.refresh(); Config config = context.getBean(Config.class); User user = context.getBean(User.class); System.out.println(user); System.out.println(config); } } static class Config { @Bean public User user() { User user = new User(); user.setAge(28); user.setName("foo"); return user; } } }
由上面可以得出一个结论,Spring 不会进行重复注册,手动 registerBean 以及使用 @Import 注解同时注入了 Config 对象,但是不会报错,并且成功注入
4.5.3 Java API 方式注入
@Import(AnnotationBeanDefinitionDemo.Config.class) public class AnnotationBeanDefinitionDemo { public static void main(String[] args) { try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { context.register(AnnotationBeanDefinitionDemo.class); context.registerBean("config", Config.class); // 如果此处名字和下面@Bean中名字相同,会覆盖 registerUserBeanDefinition(context, "newUser"); registerUserBeanDefinition(context); context.refresh(); System.out.println("Config 类型所有 Beans:" + context.getBeansOfType(Config.class)); System.out.println("User 类型所有 Beans:" + context.getBeansOfType(User.class)); } } public static void registerUserBeanDefinition(BeanDefinitionRegistry registry, String beanName) { AbstractBeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(User.class) .addPropertyValue("age", 18) .addPropertyValue("name", "fooNew").getBeanDefinition(); if (StringUtils.hasText(beanName)) { // 命名 Bean 注册方法 registry.registerBeanDefinition(beanName, definition); } else { // 非命名 Bean 注册方法 BeanDefinitionReaderUtils.registerWithGeneratedName(definition, registry); } } public static void registerUserBeanDefinition(BeanDefinitionRegistry registry) { registerUserBeanDefinition(registry, null); } @Component static class Config { @Bean public User user() { User user = new User(); user.setAge(28); user.setName("foo"); return user; } } } // 运行结果: Config 类型所有 Beans:{config=com.gcl.bean.AnnotationBeanDefinitionDemo$Config@327b636c} User 类型所有 Beans:{newUser=User{name='fooNew', age=18}, com.gcl.bean.User#0=User{name='fooNew', age=18}}
4.6 实例化 Bean
4.6.1 方法
-
常规方式
-
通过构造器(配置元信息:XML、Java 注解和 Java API)
-
通过静态工厂方法(配置元信息:XML 和 Java API)
-
通过 Bean 工厂方法(配置元信息:XML 和 Java API)
-
通过 FactoryBean(配置元信息:XML、Java 注解和 Java API)
-
-
特殊方式
-
通过 ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和 Java API)
-
通过 AutowireCapableBeanFactory#createBean(java.lang.String, int boolean)
-
通过 BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)
-
4.6.2 常规方式
public class BeanInstantiationDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean-create.xml"); System.out.println(context.getBean("user", User.class)); System.out.println(context.getBean("factoryUser")); System.out.println(context.getBean("userByFactoryBean")); System.out.println(context.getBean("user") == context.getBean("factoryUser")); } } public class User { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } // 静态工厂方法创建bean public static User user() { User user = new User(); user.setName("static"); user.setAge(22); return user; } } // 抽象工厂创建bean public interface UserFactory { User createUser(); } public class DefaultUserFactory implements UserFactory { @Override public User createUser() { return User.user(); } } //FactoryBean创建bean public class UserFactoryBean implements FactoryBean<User> { private static final User USER = User.user(); @Override public User getObject() throws Exception { return USER; } @Override public Class<?> getObjectType() { return User.class; } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 静态方法实例化 Bean --> <bean id="user" class="com.gcl.bean.User" factory-method="user"/> <!-- 工厂方法实例化 Bean --> <bean id="userFactory" class="com.gcl.bean.factory.DefaultUserFactory"/> <bean id="factoryUser" factory-bean="userFactory" factory-method="createUser"/> <!-- FactoryBean 实例化 Bean --> <bean id="userByFactoryBean" class="com.gcl.bean.factory.UserFactoryBean"/> </beans>
4.6.3 非常规方式
代码
public class SpecialBeanInstantiationDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:special-bean-create.xml"); // 通过 applicationContext 可以获取到 AutowireCapableBeanFactory 对象 AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory(); // 获取到的是 FactoryBean#getObject 方法返回的对象 ServiceLoader<UserFactory> loader = context.getBean("userFactoryServiceLoader", ServiceLoader.class); displayServiceLoader(loader); // 通过 AutowireCapableBeanFactory 对象实例化 Bean,通过该方法创建的 Bean 可以享受 Bean生命周期的所有阶段 UserFactory factory = beanFactory.createBean(DefaultUserFactory.class); System.out.println(factory.createUser()); } private static void displayServiceLoader(ServiceLoader<UserFactory> loader) { Iterator<UserFactory> iterator = loader.iterator(); while (iterator.hasNext()) { UserFactory factory = iterator.next(); System.out.println(factory.createUser()); } } }
SPI 文件
com.gcl.bean.factory.DefaultUserFactory
XML 配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- ServiceLoaderFactoryBean 对象,获取时,其实真正获取到的是 ServiceLoader 对象,因为 FactoryBean 对应的 Bean 是 getObject 方法返回的对象 --> <bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean"> <!-- ServiceLoader<UserFactory> 配置 ServiceLoader 对象泛型类型 --> <property name="serviceType" value="com.gcl.bean.factory.UserFactory"/> </bean> </beans>
4.7 初始化 Bean
-
@PostConstruct 标注方法
-
实现 InitializingBean 接口的 afterPropertiesSet 方法
-
自定义初始化方法
-
XML 配置:
<bean init-method="init" .../>
-
Java 注解:@Bean(initMethod="init")
-
Java API:AbstractBeanDefinition#setInitMethodName(String)(其实上述两个自定义方法底层就是调用的setInitMethodName 方法)
-
上述三种方式执行顺序?@PostConstructor > InitializingBean > 自定义初始化方法。
public class DefaultUserFactory implements UserFactory, InitializingBean { @Override public User createUser() { return User.user(); } @PostConstruct public void init() { System.out.println("@PostConstructor : UserFactory 初始化中..."); } public void initUserFactory() { System.out.println("@自定义初始化方法 : UserFactory 初始化中..."); } @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean#afterPropertiesSet 初始化方法 : UserFactory 初始化中..."); } } @Configuration public class BeanInitializationDemo { public static void main(String[] args) { // 创建 BeanFactory 容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册配置类 context.register(BeanInitializationDemo.class); // 启动容器 context.refresh(); // 依赖查找 UserFactory factory = context.getBean(UserFactory.class); System.out.println(factory.createUser()); // 关闭容器 context.close(); } @Bean(initMethod = "initUserFactory") public UserFactory factory() { return new DefaultUserFactory(); } } // 运行结果为: @PostConstructor : UserFactory 初始化中... InitializingBean#afterPropertiesSet 初始化方法 : UserFactory 初始化中... @自定义初始化方法 : UserFactory 初始化中... User{name='static', age=22}
由结果可知三种实例化方式的优先级为:
@PostConstructor > InitializingBean > 自定义初始化方法(init-mehtod、initMethod)
4.8 延迟初始化 Bean
-
XML 配置:
<bean lazy-init="true" .../>
-
Java 注解:@Lazy(true)
以上一小节例子为例:
public class BeanInitializationDemo { public static void main(String[] args) { // 创建 BeanFactory 容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册配置类 context.register(BeanInitializationDemo.class); // 启动容器 context.refresh(); System.out.println("Spring 应用上下文以启动..."); // 依赖查找 UserFactory factory = context.getBean(UserFactory.class); System.out.println(factory.createUser()); // 关闭容器 context.close(); } @Bean(initMethod = "initUserFactory") @Lazy public UserFactory factory() { return new DefaultUserFactory(); } } // 如果不加@Lazy注解,“Spring 应用上下文以启动...”这句话后于初始化方法输出,如下: @PostConstructor : UserFactory 初始化中... InitializingBean#afterPropertiesSet 初始化方法 : UserFactory 初始化中... @自定义初始化方法 : UserFactory 初始化中... Spring 应用上下文以启动... User{name='static', age=22} // 如果加上 @Lazy 注解,输出结果如下: Spring 应用上下文以启动... @PostConstructor : UserFactory 初始化中... InitializingBean#afterPropertiesSet 初始化方法 : UserFactory 初始化中... @自定义初始化方法 : UserFactory 初始化中... User{name='static', age=22}
可以看到延迟初始化 Bean 在容器启动时并未进行初始化,只进行了实例化,只有在真正使用时,才会触发初始化逻辑,非延迟初始化则是在容器启动时就进行初始化操作。
4.9 销毁 Bean
-
@PreDestroy 标注方法
-
实现 DisposableBean 接口的 destroy 方法
-
自定义销毁方法
-
XML 配置:
<bean destroy="destroy" .../>
-
Java 注解:@Bean(destroy="destroy")
-
Java API:AbstractBeanDefinition#setDestroyMethodName(String)(上述两种方法底层就是调用的 setDestroyMethodName 方法)
-
public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean { @Override public User createUser() { return User.user(); } @PostConstruct public void init() { System.out.println("@PostConstructor : UserFactory 初始化中..."); } @PreDestroy public void preDestroy() { System.out.println("@PreDestroy : UserFactory 销毁后..."); } public void initUserFactory() { System.out.println("自定义初始化方法 : UserFactory 初始化中..."); } public void destroyUserFactory() { System.out.println("自定义销毁方法 : UserFactory 销毁后..."); } @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean#afterPropertiesSet 初始化方法 : UserFactory 初始化中..."); } @Override public void destroy() throws Exception { System.out.println("DisposableBean#destroy 销毁方法 : UserFactory 销毁后..."); } } @Configuration public class BeanInitializationDemo { public static void main(String[] args) { // 创建 BeanFactory 容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册配置类 context.register(BeanInitializationDemo.class); // 启动容器 context.refresh(); System.out.println("Spring 应用上下文以启动..."); // 依赖查找 UserFactory factory = context.getBean(UserFactory.class); System.out.println(factory.createUser()); // 关闭容器 context.close(); } @Bean(initMethod = "initUserFactory", destroyMethod = "destroyUserFactory") @Lazy public UserFactory factory() { return new DefaultUserFactory(); } } // 输出结果为: Spring 应用上下文以启动... @PostConstructor : UserFactory 初始化中... InitializingBean#afterPropertiesSet 初始化方法 : UserFactory 初始化中... 自定义初始化方法 : UserFactory 初始化中... User{name='static', age=22} @PreDestroy : UserFactory 销毁后... DisposableBean#destroy 销毁方法 : UserFactory 销毁后... 自定义销毁方法 : UserFactory 销毁后...
销毁 Bean 优先级:@PreDestroy > DisposableBean > 自定义销毁方法(destroy-method、destroyMethod)
4.10 回收 Bean
-
关闭 Spring 容器(应用上下文)
-
执行 GC
-
Speing Bean 覆盖的 finalize() 方法
也就是说在 Spring 关闭以后,当发生 GC 时会回收容器中的 Bean,回收方法按照 GC 方法来进行,可达性分析以及 finalize 自救
4.11 面试题
4.11.1 如何注册 Bean
通过 BeanDefinition (XML、注解@Bean,@Component等、Java API方式(BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)等))和外部单体对象来注册(外部单体对象生命周期并不由 Spring 来管理,但是也可以被容器托管),如下:
public class SingletonBeanRegistrationDemo { public static void main(String[] args) { // 创建 Spring 容器(上下文) AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 创建外部单体对象 DefaultUserFactory factory = new DefaultUserFactory(); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 注册外部单体对象 beanFactory.registerSingleton("userFactory", factory); context.refresh(); // 依赖查找 UserFactory factory1 = context.getBean(UserFactory.class); System.out.println(factory == factory1); } } // 打印结果:true
4.11.2 什么是 Spring BeanDifinition
BeanDefinition 实际上是关于 Bean 定义的元数据信息,包括scope、lazy、init-method、destroy-method、beanName、primary、role、autowire等
4.11.3 Spring 容器是如何注册 Bean 的
IOC 配置元信息读取和解析、依赖查找和注入以及 Bean 生命周期等(后续详解)
5. 依赖查找
5.1 依赖查找前世今生
-
单一类型依赖查找
-
JNDI ——javax.naming.Context#lookup(javax.naming.Name)
-
JavaBeans—— java.beans.beancontext.BeanContext
-
-
集合类型依赖查找
-
java.beans.beancontext.BeanContext
-
-
层次依赖查找
-
java.beans.beancontext.BeanContext
-
public interface BeanContext extends BeanContextChild, Collection, DesignMode, Visibility { // ... }
java.beans.beancontext.BeanContext 针对 GUI 程序以及普通计算程序,Spring 的实现很大程度上参考了其实现,其实现了 Collection 接口,因此其内部所有成员都是 Bean,BeanContext 负责对其进行 crud 操作
5.2 单一类型依赖查找
Spring 中单一类型依赖查找依赖 BeanFactory 接口,可以根据名称、类型(实时和延迟查找)、名称+类型进行查找。
延迟查找举例如下:
public class ObjectProviderDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ObjectProviderDemo.class); lookupByObjectProvider(context); context.close(); } @Bean public String helloWorld() { return "Hello World"; } private static void lookupByObjectProvider(AnnotationConfigApplicationContext context) { ObjectProvider<String> beanProvider = context.getBeanProvider(String.class); System.out.println(beanProvider.getObject()); } }
5.3 集合类型依赖查找(ListableBeanFactory)
Spring 中集合类型依赖查找依赖 ListableBeanFactory 接口,查找方式如下:
-
根据 Bean 类型查找
-
获取同类型 Bean 名称列表(BeanDefinition 中进行查找,Bean 还未实例化)
-
getBeanNamesForType(Class)
-
Spring 4.2 getBeanNamesForType(ResolvableType)
-
-
获取同类型 Bean 实例列表(Bean 已经实例化或者会触发 Bean 的实例化)
-
getBeanOfType(Class)以及重载方法
-
-
-
根据注解查找
-
Spring 3.0 获取标注类型 Bean 名称列表(BeanDefinition 中进行查找,Bean 还未实例化)
-
getBeanNamesForAnnotation(Class<? extends Annotation>)
-
-
Spring 3.0 获取标注类型 Bean 实例列表(Bean 已经实例化或者会触发 Bean 的实例化)
-
getBeansWithAnnotation(Class<? extends Annotation>)
-
-
Spring 3.0 获取指定名称+标注类型 Bean 实例(Bean 已经实例化或者会触发 Bean 的实例化)
-
findAnnotationOnBean(String, Class<? extends Annotation>)
-
-
5.4 层次性依赖查找
层次型依赖查找依赖 HierarchicalBeanFactory 接口
-
父 BeanFactory:getParentBenaFactory
-
层次性查找
-
根据 Bean 名称查找
-
基于 containsLocalBean 方法实现(当前 BeanFactory 查找,不包含父类 BeanFactory)
-
-
根据 Bean 类型查找实例列表
-
单一类型:BeanFactoryUtils#beanOfType
-
集合类型:BeanFactoryUtils#beansOfTypeIncludingAncestors
-
-
根据 Java 注解查找名称列表
-
BeanFactoryUtils#beanNamesForTypeIncludingAncestors
-
-
真正使用时通过使用 ConfigurableListableBeanFactory 来使用,接口间继承关系如下:
// 父子级联容器接口(类似双亲委派,优先在父容器中查找,找不到才在子容器中查找) public interface HierarchicalBeanFactory extends BeanFactory { /** * Return the parent bean factory, or {@code null} if there is none. */ @Nullable BeanFactory getParentBeanFactory(); /** * Return whether the local bean factory contains a bean of the given name, * ignoring beans defined in ancestor contexts. * <p>This is an alternative to {@code containsBean}, ignoring a bean * of the given name from an ancestor bean factory. * @param name the name of the bean to query * @return whether a bean with the given name is defined in the local factory * @see BeanFactory#containsBean */ boolean containsLocalBean(String name); } // 可配置容器接口,包含父子级联功能以及单例注册功能,自己新增的功能有配置BeanFacory的属性,比如BeanPostProcessor、PropertyEditor等 public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry { // ... } // 可配置容器接口,包含集合容器功能、自动注入(非Spring管理的Bean实现依赖注入,被注入的对象必须是Spring容器内的对象)、可配置容器接口(父子级联、单例注册) public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory { // ... }
根据名称查找举例:
public class HierarchicalDependencyLookupDemo { public static void main(String[] args) { // 创建 BeanFactory 容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(HierarchicalDependencyLookupDemo.class); // 获取 HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory ConfigurableListableBeanFactory factory = context.getBeanFactory(); System.out.println("当前 BeanFactory 的 parent :" + factory.getParentBeanFactory()); // 添加 parentBeanFactory HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory(); factory.setParentBeanFactory(parentBeanFactory); System.out.println("当前 BeanFactory 的 parent :" + factory.getParentBeanFactory()); displayContainsBean(factory, "user"); displayLocalBean(factory, "user"); displayLocalBean(parentBeanFactory, "user"); // 启动上下文 context.refresh(); // 关闭上下文 context.close(); } // 创建父容器 private static HierarchicalBeanFactory createParentBeanFactory() { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml"); return factory; } private static void displayLocalBean(HierarchicalBeanFactory factory, String beanName) { System.out.printf("当前 BeanFactory[%s] 是否包含 Local Bean[name: %s] : %s\n", factory, beanName, factory.containsLocalBean(beanName)); } private static void displayContainsBean(HierarchicalBeanFactory factory, String beanName) { System.out.printf("当前 BeanFactory[%s] 是否包含 Bean[name: %s] : %s\n", factory, beanName, containsBean(factory, beanName)); } // 类似双亲委派,优先从父容器中查找,递归查找 private static boolean containsBean(HierarchicalBeanFactory factory, String beanName) { BeanFactory parentBeanFactory = factory.getParentBeanFactory(); if (parentBeanFactory instanceof HierarchicalBeanFactory) { HierarchicalBeanFactory hierarchicalBeanFactory = (HierarchicalBeanFactory) parentBeanFactory; if (containsBean(hierarchicalBeanFactory, beanName)) { return true; } } return factory.containsLocalBean(beanName); } } // 输出结果: 当前 BeanFactory 的 parent :null 当前 BeanFactory 的 parent :org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1: defining beans [user,superUser,userRepository,objectFactory]; root of factory hierarchy 当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@25359ed8: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,hierarchicalDependencyLookupDemo]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1] 是否包含 Bean[name: user] : true 当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@25359ed8: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,hierarchicalDependencyLookupDemo]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1] 是否包含 Local Bean[name: user] : false 当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1: defining beans [user,superUser,userRepository,objectFactory]; root of factory hierarchy] 是否包含 Local Bean[name: user] : true
5.5 延迟依赖查找
延迟查找依赖的接口:
-
org.springframework.beans.factory.ObjectFactory
-
org.springframework.beans.factory.ObjectProvider
-
Spring 5 对 Java 8 特性扩展
-
函数式接口
-
getIfAvailable(Supplier)
-
ifAvailable(Consumer)
-
-
Stream 扩展——stream()
-
-
思考:非延迟初始化的 bean 是否能够实现延迟查找?
public class ObjectProviderDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(ObjectProviderDemo.class); context.refresh(); lookupByObjectProvider(context); lookupIfAvailable(context); lookupByStreamOps(context); context.close(); } private static void lookupByStreamOps(AnnotationConfigApplicationContext context) { ObjectProvider<String> provider = context.getBeanProvider(String.class); provider.stream().forEach(System.out::println); } private static void lookupIfAvailable(AnnotationConfigApplicationContext context) { ObjectProvider<User> beanProvider = context.getBeanProvider(User.class); User user = beanProvider.getIfAvailable(User::user); System.out.println("当前 User 对象:" + user); } @Bean @Primary public String helloWorld() { return "Hello World"; } @Bean public String message() { return "Message"; } private static void lookupByObjectProvider(AnnotationConfigApplicationContext context) { ObjectProvider<String> beanProvider = context.getBeanProvider(String.class); System.out.println(beanProvider.getObject()); } }
5.6 安全依赖查找
依赖查找安全性对比:
依赖查找类型 | 代表实现 | 是否安全 |
---|---|---|
单一类型查找 | BeanFactory#getBean | 否 |
ObjectFactory#getObject | 否 | |
ObjectProvider#getIfAvailable | 是 | |
集合类型查找 | ListableBeanFactory#getBeansOfType | 是 |
ObjectProvider#stream | 是 |
安全与否在于调用方法进行依赖查找时是否会触发异常。
层次类型依赖查找安全性依赖于单一或者集合类型依赖查找安全性
推荐使用 ObjectProvider 进行依赖查找,因为可以实现单一和集合类型查找,并且查找是安全的
5.7 内建可查找依赖
AbstractApplicationContext 内建可查找的依赖
Bean名称 | Bean 实例 | 使用场景 |
---|---|---|
environment | Environment 对象 | 外部化配置以及 Profiles |
SystemProperties | java.util.Properties 对象 | Java 系统属性 |
systemEnvironment | java.util.Map 对象 | 操作系统环境变量 |
messageSource | MessageSource 对象 | 国际化文案 |
lifecycleProcessor | LifecycleProcessor 对象 | Lifecycle Bean 处理器 |
applicationEventMulticaster | ApplicationEventMulticaster 对象 | Spring 事件广播器 |
注解驱动 Spring 应用上下文内建可查找依赖(部分)(componentScan或者使用ApplicaitonContext激活这些内部 Bean)(名称很长也比较难记,可以在AnnotationConfigUtils类中进行查找)
Bean 名称 | Bean 实例 | 使用场景 |
---|---|---|
org.springframework.context.annotation.internalConfigurationClassPostProcessor(BeanFactory生命周期) | ConfigurationClassPostProcessor对象 | 处理 Spring 配置类 |
org.springframework.context.annotation.internalAutoWiredAnnotationBeanPostProcessor | AutoWiredAnnotationBeanPostProcessor 对象 | 处理 @Autowired 以及 @ Value 注解 |
org.springframework.context.annotation.internalCommonAnnotationBeanPostProcessor | CommonAnnotationBeanPostProcessor 对象 | (条件激活)处理 JSR-250 注解,如 @PostConstructor 等 |
org.springframework.context.annotation.internalEventListenerMethodProcessor | EventListenerMethodProcessor 对象 | 处理标注 @EventListener 的 Spring 事件监听方法 |
org.springframework.context.annotation.internalDefaultEventListenerFactory | DefaultEventListenerFactory | @EventListener 事件监听方法适配为 ApplicationListener |
org.springframework.context.annotation.internalPersistenceAnnotationBeanPostProcessor | PersistenceAnnotationBeanPostProcessor | (条件激活)处理 JPA 注解场景 |
5.8 依赖查找经典异常
BeansException 子类型(非 check 异常,Runtime 异常)
异常类型 | 触发条件(举例) | 场景举例 |
---|---|---|
NoSuchBeanDefinitionException | 当查找 Bean 不存在于 IoC 容器时 | BeanFactory#getBean ObjectFactory#getObject |
NoUniqueBeanDefinitionException | 类型依赖查找时,IoC 容器存在多个 Bean 实例 | BeanFactory#getBean |
BeanInstantiationException | 当 Bean 所对应的类型非具体类型时 | BeanFactory#getBean |
BeanCreationException | 当 Bean 初始化过程出错 | Bean 初始化方法执行异常时 |
BeanDefinitionStoreException | 当 BeanDefinition 配置元信息非法时 | XML 配置资源无法打开时 |
5.9 面试题
5.9.1 ObjectFactory 和 BeanFactory 区别
ObjectFactory 是 Spring 比较早期的接口,ObjectFactory 和 BeanFactory 均提供依赖查找功能,不同点:
-
BeanFactory 仅关注一个或一种类型的 Bean 依赖查找,并且自身不具备依赖查找的能力,能力由 BeanFactory 支撑
-
BeanFactory 提供单一类型、集合类型以及层次性等多种依赖查找功能
从实用也可以看出:
<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName" value="user"/> </bean>
public class ObjectFactoryCreatingFactoryBean extends AbstractFactoryBean<ObjectFactory<Object>> { @Nullable private String targetBeanName; /** * Set the name of the target bean. * <p>The target does not <i>have</i> to be a non-singleton bean, but realistically * always will be (because if the target bean were a singleton, then said singleton * bean could simply be injected straight into the dependent object, thus obviating * the need for the extra level of indirection afforded by this factory approach). */ public void setTargetBeanName(String targetBeanName) { this.targetBeanName = targetBeanName; } @Override public void afterPropertiesSet() throws Exception { Assert.hasText(this.targetBeanName, "Property 'targetBeanName' is required"); super.afterPropertiesSet(); } @Override public Class<?> getObjectType() { return ObjectFactory.class; } @Override protected ObjectFactory<Object> createInstance() { BeanFactory beanFactory = getBeanFactory(); Assert.state(beanFactory != null, "No BeanFactory available"); Assert.state(this.targetBeanName != null, "No target bean name specified"); return new TargetBeanObjectFactory(beanFactory, this.targetBeanName); } /** * Independent inner class - for serialization purposes. * 此处生成 ObjectFactory 对象,当通过 Spring 容器获取对象时,其实获取的是 ObjectFactory#getObject方法返回的对象 * 此处是从容器中获取对象的 */ @SuppressWarnings("serial") private static class TargetBeanObjectFactory implements ObjectFactory<Object>, Serializable { private final BeanFactory beanFactory; private final String targetBeanName; public TargetBeanObjectFactory(BeanFactory beanFactory, String targetBeanName) { this.beanFactory = beanFactory; this.targetBeanName = targetBeanName; } @Override public Object getObject() throws BeansException { // 从 Spring 容器中根据 name 获取 bean 返回 return this.beanFactory.getBean(this.targetBeanName); } } }
5.9.2 BeanFactory#getBean 方法是否线程安全
是线程安全的,操作过程中会通过 synchronized 加锁,jdk 5 update 6 中新增偏向锁功能,该功能可以大大降低 synchronized 关键字的重量,在只有一个线程访问时,可以将只看为没有锁(只有加锁时会执行一个 CAS 操作将锁偏向当前线程,后续该线程运行到加锁位置只会比较偏向锁线程是否为当前线程,是的话就通过 CAS 替换一次 lock record,后续不再加锁),因此不建议将启动过程放在子线程执行,就在 main 主线程执行性能是最好的
5.9.4 Spring 的依赖查找和依赖注入在来源上有什么区别
依赖查找只能查询容器中有的 Bean(手动注入或者框架内建),但是依赖注入除了容器中有的 Bean 以外,还支持注入非 Bean 对象(内建依赖)
6. 依赖注入
两层意思:
-
注入 Spring Bean 或者可依赖对象(@Autowired)
-
注入外部化配置(@Value)
6.1 依赖注入的模式
-
手动模式
-
XML 资源配置元信息
-
Java 注解配置元信息
-
API 配置元信息(普通用户使用较少,框架开发者使用较多,需掌握)
-
-
自动模式(官方不推荐使用自动绑定模式)
-
Autowiring(自动绑定)
-
依赖注入类型:
依赖注入类型 | 配置元数据举例 |
---|---|
Setter 方法 | <property name = "user" ref="userBean"/> |
构造器 | <constructor-arg name="user" ref="userBean"/> |
字段 | @Autowired User user; |
方法 | @Autowired public void user(User user) {...} |
接口回调 | class MyBean implements BeanFactoryAware {...} |
6.2 自动绑定
6.2.1 自动绑定原因
Spring 容器能够根据自动绑定在合作的 Bean(依赖 Bean 和被依赖 Bean) 之间,通过某种策略自动处理这些合作者之间的关系
自动绑定优点:
-
减少属性或者构造器参数的设置
-
自动绑定能够更新配置
6.2.2 自动绑定模式
模式 | 说明 |
---|---|
no | 默认值,未激活 Autowiring,需要手动指定依赖注入对象 |
byName | 根据被注入属性的名称作为 Bean 名称进行依赖查找,并将对象设置到该属性 |
byType | 根据被注入属性的类型作为依赖类型进行查找,并将对象设置到该属性 |
constructor | 特殊 byType 类型,用于构造器参数 |
参考枚举类:org.springframework.beans.factory.annotation.Autowire
public enum Autowire { /** * Constant that indicates no autowiring at all. */ NO(AutowireCapableBeanFactory.AUTOWIRE_NO), /** * Constant that indicates autowiring bean properties by name. */ BY_NAME(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME), /** * Constant that indicates autowiring bean properties by type. */ BY_TYPE(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE); private final int value; Autowire(int value) { this.value = value; } public int value() { return this.value; } /** * Return whether this represents an actual autowiring value. * @return whether actual autowiring was specified * (either BY_NAME or BY_TYPE) */ public boolean isAutowire() { return (this == BY_NAME || this == BY_TYPE); } }
6.2.3 自动绑定限制以及不足(XML 特有)
-
自动绑定会被属性或者构造器参数等注入覆盖
-
无法绑定 String、原生类型、Class 类型以及这些类型的数组类型
-
缺乏精确性
-
自动绑定的信息很难在一些工具类中进行呈现,比如自动生成的文档等
-
当绑定对象不止一个会有歧义,并且会报错
6.3 Setter 方法依赖注入
-
手动模式
-
XML 资源配置元信息
-
Java 注解配置元信息
-
API 配置元信息
-
-
自动模式
-
byName
-
byType
-
6.3.1 XML 方式
public class XmlDependencySetterInjectionDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions("classpath:dependency-setter-injection.xml"); System.out.println(factory.getBean(UserHolder.class)); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="beans-ioc-lookup.xml"/> <bean class="com.gcl.dependency.injection.UserHolder"> <property name="user" ref="superUser"/> </bean> </beans>
6.3.2 Java 注解
public class AnnotationDependencySetterInjectionDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(AnnotationDependencySetterInjectionDemo.class); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml"); context.refresh(); System.out.println(context.getBean(UserHolder.class)); context.close(); } @Bean public UserHolder userHolder(User user) { // 构造器方式 // return new UserHolder(user); // setter 方式 UserHolder holder = new UserHolder(); holder.setUser(user); return holder; } }
6.3.3 API 方式
public class ApiDependencySetterInjectionDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册 BeanDefinition context.registerBeanDefinition("userHolder", createBeanDefinitionUserHolder()); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml"); context.refresh(); System.out.println(context.getBean(UserHolder.class)); context.close(); } private static BeanDefinition createBeanDefinitionUserHolder() { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class); builder.addPropertyReference("user", "superUser"); return builder.getBeanDefinition(); } }
6.3.4 自动注入方式
public class AutowiringByNameDependencySetterInjectionDemo { public static void main(String[] args) { DefaultListableBeanFactory context = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions("autowiring-dependency-setter-injection.xml"); System.out.println(context.getBean(UserHolder.class)); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="beans-ioc-lookup.xml"/> <!-- byName --> <bean class="com.gcl.dependency.injection.UserHolder" autowire="byName"/> <!-- byType --> <!-- <bean class="com.gcl.dependency.injection.UserHolder" autowire="byType"/>--> </beans>
6.4 构造器依赖注入
-
手动模式
-
XML 资源配置元信息
-
Java 注解配置元信息
-
API 配置元信息
-
-
自动模式
-
constructor
-
6.4.1 XML 方式
public class XmlDependencyConstructorInjectionDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions("classpath:dependency-constructor-injection.xml"); System.out.println(factory.getBean(UserHolder.class)); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="beans-ioc-lookup.xml"/> <bean class="com.gcl.dependency.injection.UserHolder"> <constructor-arg name="user" ref="user"/> </bean> </beans>
6.4.2 Java 注解方式
public class AnnotationDependencyConstructorInjectionDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(AnnotationDependencyConstructorInjectionDemo.class); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml"); context.refresh(); System.out.println(context.getBean(UserHolder.class)); context.close(); } @Bean public UserHolder userHolder(User user) { // 构造器方式 return new UserHolder(user); } }
6.4.3 API 方式
public class ApiDependencyConstructorInjectionDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.registerBeanDefinition("userHolder", createBeanDefinitionUserHolder()); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml"); context.refresh(); System.out.println(context.getBean(UserHolder.class)); context.close(); } private static BeanDefinition createBeanDefinitionUserHolder() { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class); // 按照构造器参数顺序进行设置 builder.addConstructorArgReference("superUser"); return builder.getBeanDefinition(); } }
6.4.4 自动注入方式
public class AutowiringByConstructorDependencySetterInjectionDemo { public static void main(String[] args) { DefaultListableBeanFactory context = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions("autowiring-dependency-constructor-injection.xml"); System.out.println(context.getBean(UserHolder.class)); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="beans-ioc-lookup.xml"/> <bean class="com.gcl.dependency.injection.UserHolder" autowire="constructor"/> </beans>
6.5 字段注入
只有手动注入方式,以注解为主:
-
@Autowired(忽略静态字段)
-
@Resource
-
@Inject
public class AnnotationDependencyFieldInjectionDemo { // Autowire 会忽略静态字段 @Autowired private UserHolder userHolder; @Resource private UserHolder userHolder2; public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(AnnotationDependencyFieldInjectionDemo.class); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml"); context.refresh(); AnnotationDependencyFieldInjectionDemo demo = context.getBean(AnnotationDependencyFieldInjectionDemo.class); System.out.println(demo.userHolder); System.out.println(demo.userHolder == demo.userHolder2); context.close(); } @Bean public UserHolder userHolder(User user) { // 构造器方式 return new UserHolder(user); } }
6.6 方法注入
手动注入:
-
@Autowired
-
@Resource
-
@Inject(可选)、
-
@Bean
public class AnnotationDependencyMethodInjectionDemo { private UserHolder userHolder; private UserHolder userHolder2; @Autowired public void initUserHolder(UserHolder userHolder) { this.userHolder = userHolder; } @Resource public void initUserHolder2(UserHolder userHolder2) { this.userHolder2 = userHolder2; } @Bean public UserHolder userHolder(User user) { // 构造器方式 return new UserHolder(user); } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(AnnotationDependencyMethodInjectionDemo.class); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml"); context.refresh(); AnnotationDependencyMethodInjectionDemo demo = context.getBean(AnnotationDependencyMethodInjectionDemo.class); System.out.println(demo.userHolder); System.out.println(demo.userHolder == demo.userHolder2); context.close(); } }
6.7 回调注入
Aware 系列接口回调注入(内建依赖)
内建接口 | 说明 |
---|---|
BeanFactoryAware | 获取 IoC 容器——BeanFactory |
ApplicationContextAware | 获取 Spring 应用上下文——ApplicationContext 对象 |
EnvironmentAware | 获取 Environment 对象 |
ResourceLoaderAware | 获取资源加载器对象——ResourceLoader |
BeanClassLoaderAware | 获取加载当前 Bean Class 的 ClassLoader |
BeanNameAware | 获取当前 Bean的名称 |
MessageSourceAware | 获取 MessageSource 对象,用于 Spring 国际化 |
ApplicationEventPublisherAware | 获取 ApplicationEventPublisher 对象,用于 Spring 事件 |
EmbeddedValueResolverAware | 获取 StringValueResolver 对象,用于占位符处理 |
6.8 依赖注入类型选择
-
低依赖:构造器注入(Spring 官方推荐注入方式)
-
多依赖:Setter 方法注入
-
便利性:字段注入(Spring 和 Spring 都不推荐使用字段注入)
-
声明类:方法注入
6.9 基础类型注入
-
原生类型(Primitive):boolean、byte、char、short、int、long、float、double
-
标量类型(Scalar):Number、Character、Boolean、Enum、Locale、Charset、Currency、Properties、UUID
-
常规类型(General):Object、String、TimeZone、Calendar、Optional等
-
Spring 类型:Resource、InputSource、Formatter
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- bean definitions here --> <bean id="user" class="com.gcl.bean.User"> <property name="name" value="foo"/> <property name="age" value="18"/> <!-- 枚举类型注入 --> <property name="city" value="BEIJING"/> <!-- Resource 对象注入--> <property name="configFileLocation" value="classpath:user-config.properties"/> </bean> <bean id="superUser" class="com.gcl.bean.SuperUser" parent="user" primary="true"> <property name="level" value="1"/> </bean> <bean id="userRepository" class="com.gcl.bean.UserRepository" autowire="byType"/> <!-- <property name="users">--> <!-- <util:list>--> <!-- <ref bean="user"/>--> <!-- <ref bean="superUser"/>--> <!-- </util:list>--> <!-- </property>--> <!-- </bean>--> <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName" value="user"/> </bean> </beans>
6.10 集合类型注入
-
数组类型(Array):原生类型、标量类型、常规类型、Spring 类型
-
集合类型(Collection)
-
Collection:List、Set(SortedSet、NavigableSet、EnumSet)
-
Map:Properties
-
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- bean definitions here --> <bean id="user" class="com.gcl.bean.User"> <property name="name" value="foo"/> <property name="age" value="18"/> <property name="city" value="BEIJING"/> <property name="cities" value="BEIJING,HANGZHOU"/> <property name="cityList"> <list> <value>BEIJING</value> <value>HANGZHOU</value> <value>SHANGHAI</value> </list> </property> <property name="citySet"> <set> <value>HANGZHOU</value> <value>SHANGHAI</value> </set> </property> <property name="cityMap"> <map> <entry key="beijing" value="BEIJING"/> <entry key="shanghai" value="SHANGHAI"/> </map> </property> <property name="configFileLocation" value="classpath:user-config.properties"/> </bean> <bean id="superUser" class="com.gcl.bean.SuperUser" parent="user" primary="true"> <property name="level" value="1"/> </bean> <bean id="userRepository" class="com.gcl.bean.UserRepository" autowire="byType"/> <!-- <property name="users">--> <!-- <util:list>--> <!-- <ref bean="user"/>--> <!-- <ref bean="superUser"/>--> <!-- </util:list>--> <!-- </property>--> <!-- </bean>--> <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName" value="user"/> </bean> </beans>
6.11 限定注入
-
@Qualifier 限定注入
-
通过 Bean 名称限定
-
通过分组限定
-
-
基于 @Qualifier 扩展限定
-
自定义注解——如 Spring Cloud @LoadBalance
-
public class AnnotationQualifierDependencyInjectionDemo { @Autowired @Qualifier("user") private User user; @Autowired private Collection<User> allUsers; @Autowired @Qualifier private Collection<User> qualifiedUsers; // 通过使用 @Qualifier 进行逻辑分组 @Bean @Qualifier public User user1() { User user = new User(); user.setName("user1"); return user; } // 通过使用 @Qualifier 进行逻辑分组 @Bean @Qualifier public User user2() { User user = new User(); user.setName("user2"); return user; } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(AnnotationQualifierDependencyInjectionDemo.class); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml"); context.refresh(); AnnotationQualifierDependencyInjectionDemo bean = context.getBean(AnnotationQualifierDependencyInjectionDemo.class); System.out.println(bean.user); System.out.println(bean.allUsers); System.out.println(bean.qualifiedUsers); context.close(); } }
6.12 延迟注入
-
使用 API ObjectFactory 实现延迟注入
-
单一类型
-
集合类型
-
-
使用 API ObjectProvider 实现延迟注入(推荐使用,安全)
-
单一类型
-
集合类型
-
6.13 依赖处理过程
-
入口——DefaultListableBeanFactory#resolveDependency
-
依赖描述符——DependencyDescriptor
-
自动绑定候选对象处理器——AutowireCandidateResolver
6.14 @Autowire 注入原理
-
元信息解析(MergedBeanDefinitionPostProcessor#postProcessorMergedBeanDefinition 方法中进行)
-
依赖查找(注入过程中包含依赖查找)
-
依赖注入(字段、方法)(AutowiredAnnotationBeanPostProcessor#postProcessorProperties 方法中进行)
6.15 JSR-330 @Inject 注入原理
public AutowiredAnnotationBeanPostProcessor() { this.autowiredAnnotationTypes.add(Autowired.class); this.autowiredAnnotationTypes.add(Value.class); try { this.autowiredAnnotationTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader())); logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
@Inject 注解跟 @Autowired 注解注入流程是相似的,如果引入了 JSR-330 jar 包,就可以使用该注解进行注入
6.16 Java 通用注解注入原理(@Resource等)
CommonAnnotationBeanPostProcessor(比 AutowiredAnnotationBeanPostProcessor 支持更多的注解,除了注入注解,还支持生命周期注解)
-
注入注解
-
javax.xml.ws.WebServiceRef
-
javax.ejb.EJB
-
javax.annotation.Resource
-
-
生命周期注解(构建 LifecycleElement)
-
javax.annotation.PostConstructor
-
javax.annotation.PreDestroy
-
6.17 自定义依赖注入注解
-
基于 AutowiredAnnotationBeanPostProcessor 实现
-
自定义实现
-
生命周期处理
-
InstantiationAwareBeanPostProcessor
-
MergedBeanDefinitionPostProcessor
-
-
元数据
-
InjectElement
-
InjectMetadata
-
-
6.18 面试题
6.18.1 有多少种依赖注入方式
-
构造器注入(少依赖,强制依赖)
-
Setter 注入(多依赖,非强制依赖)
-
字段注入
-
方法注入
-
接口回调注入
6.18.2 你偏好构造器注入还是 Setter 注入?
两种依赖注入均可使用,如果是必须依赖,推荐使用构造器注入,Setter注入可选依赖
依赖注入少用外部注解,包括 Java 注解和 Spring 注解,sh奥用注解就少依赖,使用原始 API、原始构造器参数来进行操作,可以解决依赖问题,同时还能解决线程安全问题(构造器可以确保线程安全问题)
6.18.3 Spring 依赖注入来源有哪些?
依赖查找通过 getBean,依赖注入通过 resolveDependency
-
自定义 Bean(XML、注解、API)
-
内建 Bean
-
内建依赖
7. Spring IoC依赖来源
7.1 依赖查找来源
7.1.1 来源
来源 | 配置元数据 |
---|---|
Spring DeanDefinition | <bean id = "user" class="com.gcl.bean.User"/> |
@Bean public User user {...} | |
BeanDefinitionBuilder | |
单例对象 | API实现 |
7.1.2 Spring 内建 BeanDefinition 以及内建单例对象
详情见 5.7 小结
如何通过 XML 开启注解功能?首先注解功能是由 AnnotationConfigUtils#registerAnnotationConfigProcessors 方法注册启动的,而该方法在 AnnotationConfigBeanDefinitionParser、ComponentScanBeanDefinitionParser、AnnotationBeanDefinitionReader、ClasspathBeanDefinitionScanner 中被调用,启动注解方式有两种:
<context:annotation-config/> <!-- 不存在的路径也可以,只要有该元素即可 --> <context:component-scan base-package="xxx"/>
7.2 依赖注入来源
来源 | 配置元数据 |
---|---|
Spring DeanDefinition | <bean id = "user" class="com.gcl.bean.User"/> |
@Bean public User user {...} | |
BeanDefinitionBuilder | |
单例对象 | API实现 |
非 Spring 容器管理对象 |
public class DependencySourceDemo { // 注入在 postProcessorProperties 方法执行,早于 Setter 注入,也早于 @PostConstructor // 这四个对象在 AbstractApplicationContext 中进行注册,但是无法从容器中获取 @Resource private BeanFactory factory; @Resource private ResourceLoader loader; @Resource private ApplicationContext context; @Resource private ApplicationEventPublisher publisher; @PostConstruct public void init() { System.out.println("beanFactory == applicationContext : " + (factory == context)); System.out.println("beanFactory == applicationContext.getBeanFactory : " + (factory == context.getAutowireCapableBeanFactory())); System.out.println("loader == applicationContext : " + (loader == context)); System.out.println("publisher == applicationContext : " + (publisher == context)); } @PostConstruct public void getBean() { getBean(BeanFactory.class); getBean(ApplicationEventPublisher.class); getBean(ApplicationContext.class); getBean(ResourceLoader.class); } private <T> T getBean(Class<T> type) { try { return factory.getBean(type); } catch (NoSuchBeanDefinitionException e) { System.err.println("当前类型" + type.getName() + "无法在 BeanFactory 中查找到!"); } return null; } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(DependencySourceDemo.class); context.refresh(); System.out.println(context.getBean(User.class)); context.close(); } @Bean public User user() { User user = new User(); user.setName("foo"); user.setAge(18); return user; } } // 运行结果: beanFactory == applicationContext : false beanFactory == applicationContext.getBeanFactory : true loader == applicationContext : true publisher == applicationContext : true 当前类型org.springframework.beans.factory.BeanFactory无法在 BeanFactory 中查找到! 当前类型org.springframework.context.ApplicationEventPublisher无法在 BeanFactory 中查找到! 当前类型org.springframework.context.ApplicationContext无法在 BeanFactory 中查找到! 当前类型org.springframework.core.io.ResourceLoader无法在 BeanFactory 中查找到! User{name='foo', age=18}
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // ... // BeanFactory interface not registered as resolvable type in a plain factory. // MessageSource registered (and found for autowiring) as a bean. beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // ... }
7.3 Spring 容器管理和游离对象
来源 | Spring Bean 对象 | 生命周期管理 | 配置元信息 | 使用场景 |
---|---|---|---|---|
Spring BeanDefinition | 是 | 是 | 是 | 依赖查找、依赖注入 |
单体对象 | 是 | 否 | 否 | 依赖查找、依赖注入 |
Resolvable Dependency(游离对象) | 否 | 否 | 否 | 依赖注入 |
7.4 Spring BeanDefinition 作为依赖来源
-
元数据:BeanDefinition
-
注册:BeanDefinitionRegistry#registerBeanDefinition
-
类型:延迟和非延迟
-
顺序:Bean 生命周期顺序按照注册顺序
注册时会使用 Map 保存 name 和 BeanDefiniton 的映射,并且还会用一个 ArrayList 保存 name 顺序(Map无序),供后续实例化使用,并且需要注意在 Springboot 2.1 以后不允许注入同名 bean
7.5 单例对象作为依赖来源
-
要素
-
来源:外部普通 Java 对象(不一定是 POJO)
-
注册:SingletonBeanRegistry#registerSingleton
-
-
限制
-
无生命周期管理
-
无法实现延迟初始化 Bean
-
public class SingletonBeanRegistrationDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 创建外部单体对象 DefaultUserFactory factory = new DefaultUserFactory(); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 注册外部单体对象 beanFactory.registerSingleton("userFactory", factory); context.refresh(); // 依赖查找 UserFactory factory1 = context.getBean(UserFactory.class); System.out.println(factory == factory1); } }
7.6 非 Spring 容器管理对象作为依赖来源
-
要素
-
注册:ConfigurableListableBeanFactory#registerResolvableDependency
-
-
限制
-
无生命周期管理
-
无法实现延迟初始化
-
无法通过依赖查找
-
public class ResolvableDependencySourceDemo { @Resource private String value; @Resource private BeanFactory factory; @PostConstruct public void init() { System.out.println(value); try { factory.getBean(String.class); } catch (Exception e) { System.out.println("err"); } } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); ConfigurableListableBeanFactory factory = context.getBeanFactory(); factory.registerResolvableDependency(String.class, "Hello world"); context.register(ResolvableDependencySourceDemo.class); context.refresh(); context.close(); } }
7.7 外部化配置作为依赖来源(@Value)
-
要素
-
类型:非常规 Spring 对象依赖来源
-
-
限制
-
无生命周期管理
-
无法实现延迟初始化 Bean
-
无法通过依赖查找
-
@Configuration @PropertySource(value = "default.properties", encoding = "UTF-8") public class ExternalConfigurationDependencySourceDemo { @Value("${usr.name:text}") private String name; @Value("${user.age:18}") private int age; public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(ExternalConfigurationDependencySourceDemo.class); context.refresh(); ExternalConfigurationDependencySourceDemo demo = context.getBean(ExternalConfigurationDependencySourceDemo.class); System.out.println(demo.name); System.out.println(demo.age); context.close(); } }
7.8 面试题
7.8.1 依赖注入和依赖查找来源是否相同
不同,依赖查找来源仅限于 Spring BeanDefinition 以及单例对象,而依赖注入的来源还包括 Resolvable Dependency 以及 @Value 所标注的外部化配置
7.8.2 单例对象能在 IoC 容器启动后注册吗?
可以,单例对象与 BeanDefinition 不同, BeanDefinition 会被 ConfigurableListableBeanFactory#freezeConfiguration 方法影响从而冻结注册,单例对象则没有该限制
7.8.3 Spring 依赖注入有哪些来源
Spring BeanDefinition
单例对象
Resolvable Dependency
@Value 外部化配置
8. Spring Bean 作用域
来源 | 说明 |
---|---|
sigleton | 默认 Spring Bean 作用域,一个 BeanFactory 有且仅有一个实例 |
prototype | 原型作用域,每次依赖查找和依赖注入生成新 Bean 对象 |
request | 将 Spring Bean 存储在 ServletRequest 上下文中 |
session | 将 Spring Bean 存储在 HttpSession 中 |
application | 将 Spring Bean 存储在 ServletContext 中 |
主要是 Singleton 和 Prototype 这两种,其余的是在后续版本中才加入的,并且在 BeanDefinition 中也只有 sigleton 和 prototype 两种作用域的定义
8.1 Singleton
在一定范围内是唯一的,Spring 中就是在一个 BeanFactory 内部是唯一的
8.2 Prototype
@Configuration @PropertySource("default.properties") public class BeanScopeDemo { @Value("${usr.name:default}") private String name; @Value("${usr.age:28}") private int age; @Bean public User singletonUser() { return new User(this.name, this.age); } @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public User prototypeUser() { return new User(this.name, this.age); } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(BeanScopeDemo.class); context.refresh(); System.out.println(context.getBean("singletonUser", User.class) == context.getBean("singletonUser", User.class)); System.out.println(context.getBean("prototypeUser", User.class) == context.getBean("prototypeUser", User.class)); context.close(); } } public class User implements BeanNameAware { private transient String beanName; private String name; private int age; public User() { } public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } @PostConstruct public void init() { System.out.println("用户 Bean[" + beanName + "]初始化..."); } @PreDestroy public void destroy() { System.out.println("用户 Bean[" + beanName + "]销毁..."); } @Override public void setBeanName(String name) { this.beanName = name; } } // 运行结果: 用户 Bean[singletonUser]初始化... true 用户 Bean[prototypeUser]初始化... 用户 Bean[prototypeUser]初始化... false 用户 Bean[singletonUser]销毁...
Spring 容器没有办法管理 protoType Bean 完整的生命周期,也没有办法记录实例的存在。销毁回调方法将不会执行,可以利用 BeanPostProcessor 进行清扫工作。
结论:
-
Singleton Bean 无论依赖查找还是依赖注入,均为同一个对象,Prototype Bean 无论依赖查找还是依赖注入,均为新生成对象
-
如果依赖注入集合类型对象,Singleton Bean 和 Prototype Bean 均会存在一个且只有一个
-
无论是 Singleton 还是 Prototype Bean 均会执行初始化方法回调,不过仅 Singleton Bean 会执行销毁方法回调
8.3 Request
配置
-
XML——
<bean class="..." scope="request"/>
-
Java 注解——@RequestScope 或 @Scope(WebApplicationContext.SCOPE_REQUEST)
实现
-
API——RequestScope
对象会被代理,页面渲染时是新对象,通过 Model 传递给 JSP 的对象每次都是新的,但是代理对象是不变的
8.4 Session
配置
-
XML——
<bean class="..." scope="session"/>
-
Java 注解——@SessionScope 或 @Scope(WebApplicationContext.SCOPE_SESSION)
实现
-
API——SessionScope
对象会被代理,页面渲染时是新对象,通过 Model 传递给 JSP 的对象在每次会话中都是相同的,不同会话中不同,但是代理对象是不变的
8.5 Application
配置
-
XML——
<bean class="..." scope="application"/>
-
Java 注解——@ApplicationScope 或 @Scope(WebApplicationContext.SCOPE_APPLICATION)
实现
-
API——ServletContextScope
JSP EL 变量搜索路径 page -> request -> session -> application(ServletContext)
ApplicationContext 作用域其实就是将 Bean 放到 ServletContext 中进行存储,因此我们不必使用 Model 进行数据的传输,可以直接在 JSP 页面中使用 Application 作用域的bean
8.6 自定义 Bean 作用域
-
实现 Scope
-
org.springfamework.beans.factory.config.Scope
-
-
注册 Scope
-
API——org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope
-
配置
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="..." value="..."/> </map> </property> </bean>
-
8.7 面试题
8.7.1 Spring 内建的 Bean 作用域有几种?
六种:sigleton、prototype、request、session、application 以及 websocket
从设计模式:单例和原生(原型)
从web模式:request、session、application、websocket
8.7.2 singleton Bean 是否在一个应用中是唯一的
不是,只是在一个 BeanFactory 中是唯一的,整个应用可能包含多个应用上下文(层次上下文),不同上下文中互不影响
同样的,一个静态字段在 JVM 中是否唯一?不是唯一的,静态字段相对于 ClassLoader 唯一,但是一个 JVM 进程可能包含多个 ClassCloader
8.7.3 application 作用域 Bean 是否能被其他方案替代
是可以的,实际上,application bean 与 singleton bean 没有本质区别,无非是在某处进行了存储,跟 Ioc 容器中的 bean 是同一个
9. Spring Bean 生命周期
9.1 Spring Bean 元信息配置阶段
-
面向资源
-
XML 配置
-
Properties 配置
-
-
面向注解
-
面向 API
基于 Properties 配置元信息举例:
public class BeanMetadataConfigurationDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(factory); System.out.println(reader.loadBeanDefinitions("user.properties")); System.out.println(factory.getBean(User.class)); System.out.println(Arrays.toString(factory.getBeanDefinitionNames())); } }
usr.(class)=com.gcl.bean.lifecycle.bean.User usr.name=foo usr.age=1
9.2 Spring Bean 元信息解析阶段
-
面向资源 BeanDefinition 解析
-
BeanDefinitionReader
-
XML 解析器——BeanDefinitionParser
-
-
面向注解 BeanDefinition 解析
-
annotatedBeanDefinition
-
面向注解 BeanDefinition 解析举例:
public class AnnotatedBeanDefinitionParsingDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 基于 Java 注解的 AnnotatedBeanDefinitionReader 实现 AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(factory); // 注册类 reader.register(AnnotatedBeanDefinitionParsingDemo.class, User.class); // 普通 Class 作为 Component 注册到 Spring IoC 容器后,通常 Bean 名称为类名首字母小写 // Bean 名称生成来自于 BeanNameGenerator,注解实现 AnnotationBeanNameGenerator System.out.println(factory.getBean("annotatedBeanDefinitionParsingDemo", AnnotatedBeanDefinitionParsingDemo.class)); System.out.println(factory.getBean("user", User.class)); } }
9.3 Spring Bean 注册阶段
Bean 注册是在 DefaultListableBeanFactory#registerBeanDefinition 中进行的,默认允许覆盖(重复注册),内部有两个数据结构:
/** Map of bean definition objects, keyed by bean name. */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); /** List of bean definition names, in registration order. */ private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
map 用于保存 BeanDefinition 和 name,list 用于顺序保存 Bean 的name,为后续实例化提供顺序(map无序)
9.4 Spring BeanDefinition 合并阶段
Spring 中 BeanDefinition 有两种类型:root 和 generic,其中 root 表示没有 parent,generic 则可有可不有,Spring 中 BeanDefiniton 的合并就是针对 generic 类型 BeanDefinition,如果有 parent,则将 parent 属性合并到 generic 中,当然该操作是一个递归的操作,会一直递归合并直到 root 或者直到 BeanDefinition 没有 parent 为止。
合并过程在 AbstractBeanFactory#getMergedBeanDefinition 中实现(唯一实现)
public BeanDefinition getMergedBeanDefinition(String name) throws BeansException { String beanName = transformedBeanName(name); // Efficiently check whether bean definition exists in this factory. if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) { return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName); } // Resolve merged bean definition locally. return getMergedLocalBeanDefinition(beanName); }
找到存在该 BeanDefinition 定义的 BeanFactory,从中进行合并操作
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException { // Quick check on the concurrent map first, with minimal locking. RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName); if (mbd != null && !mbd.stale) { return mbd; } return getMergedBeanDefinition(beanName, getBeanDefinition(beanName)); } /** Map from bean name to merged RootBeanDefinition. */ private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
会将 mbd 保存在 mergedBeanDefinitions 中,如果存在,直接返回,否则进行合并
合并过程首先判断该 BeanDefinition 是否有 parent,如果没有,则用当前 BeanDefinition 创建一个 RootBeanDefinition,否则查询其 parent,利用其 parent BeanDefinition 创建 RootBeanDefinition,并且将子 BeanDefinition 和其合并,合并中利用子类已有的配置覆盖父类中的配置,即配置优先级为子类 > 父类。
9.5 Spring Bean Class 加载阶段
Spring Bean Class 加载通过 Java 的 ClassLoader 进行,通过 XML 配置的 BeanDefinition,其 beanClass 初始为 String,经过 ClassLoader 加载以后变成 Class 类型,并且此时 AbstractBeanDefinition#hasBeanClass 为 true
9.6 Spring Bean 实例化前阶段
public class BeanInstantiationLifecycleDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor()); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions("merged-bean-definition.xml"); System.out.println(factory.getBean("superUser")); } static class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if (beanName.equals("superUser") && beanClass == SuperUser.class) { return new SuperUser(); } return null; } } }
该方法并不常用,因为该方法执行后,后续的实例化过程就不会执行
9.7 Spring Bean 实例化阶段
-
传统方式
-
实例化策略——InstantiationStrategy
-
-
构造器依赖注入
AbstractBeanFactory#getBean ->
AbstractBeanFactory#doGetBean -> 得到 mbd,然后通过 mbd、beanName 以及 args 创建对象
AbstractBeanFactory#createBean ->
AbstractAutowireCapableBeanFactory#createBean ->
AbstractAutowireCapableBeanFactory#doCreateBean -> 返回 BeanWrapper,将对象包装成 BeanWrapper 后返回
AbstractAutowireCapableBeanFactory#createBeanInstance -> 构造器自动注入还是无参构造器创建对象
AbstractAutowireCapableBeanFactory#instantiateBean -> 获取策略实例化对象,默认cglib
CglibSubclassingInstantiationStrategy#instantiate -> cglib策略创建对象,调用父类策略创建对象
SimpleInstantiationStrategy#instantiate -> 此处通过反射创建对象
9.8 Spring Bean 实例化后阶段
InstantiationAwareBeanPostProcessor#postProcessorAfterInstantiation
该阶段决定 Bean 是否允许对属性进行赋值,默认允许
public class BeanInstantiationLifecycleDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor()); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions("merged-bean-definition.xml"); System.out.println(factory.getBean("superUser")); System.out.println(factory.getBean("user")); } static class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override // 此处默认返回null,如果返回不为null,则表示后续的实例化过程不用进行,使用返回的对象当作bean进行注入 // 该方法跟后续的 postProcessAfterInstantiation 互斥 public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if (beanName.equals("superUser") && beanClass == SuperUser.class) { return new SuperUser(); } return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { // 默认返回 true,表示允许属性赋值,此处将 User 对象设置为 false,表示不允许对名称为"user",类型为User的对象赋值 return !beanName.equals("user") || bean.getClass() != User.class; } } } // 运行结果: SuperUser{level=0} User{name='null', age=0} User{name='null', age=0}
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.gcl.bean.lifecycle.bean.User"> <property name="name" value="foo"/> <property name="age" value="18"/> </bean> <bean id="superUser" class="com.gcl.bean.lifecycle.bean.SuperUser" parent="user"> <property name="level" value="1"/> </bean> </beans>
9.9 Spring Bean 属性赋值前阶段
-
Bean 属性元信息
-
PropertyValues
-
-
Bean 属性赋值前回调
-
Spring 1.2-5.0:InstantiationAwareBeanPostProcessor#postProcessPropertyValues
-
Spring 5.1:InstantiationAwareBeanPostProcessor#postProcessProperties
-
注意:如果 postProcessAfterInstantiation 方法返回 false,则该回调不会被调用,是互斥的。
postProcessBeforeInstantiation 方法如果返回对象不为 null,则不仅会跳过 postProcessAfterInstantiation 方法,同时还会跳过 postProcessProperties 方法,因为完全跳过实例化整个过程,所以后续有关实例化的所有回调都会被忽略
public class BeanInstantiationLifecycleDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor()); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions("merged-bean-definition.xml"); System.out.println(factory.getBean("superUser")); System.out.println(factory.getBean("user")); } static class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { if (ObjectUtils.nullSafeEquals("user", beanName) && bean.getClass() == User.class) { //假设 <property name="name" value="foo"/> 在 XML 中配置,那么此处 pvs 中有一个 PropertyValue(name="foo") MutablePropertyValues propertyValues = null; if (pvs instanceof MutablePropertyValues) { propertyValues = (MutablePropertyValues) pvs; } else { propertyValues = new MutablePropertyValues(); } // 等价于 <property name="name" value="bar"/>,不允许配置不存在的属性,不然报错 propertyValues.addPropertyValue("name", "bar"); propertyValues.addPropertyValue("age", "19"); return propertyValues; } return null; } } } // 运行结果: SuperUser{level=1} User{name='foo', age=18} User{name='bar', age=19}
9.10 Spring Bean Aware 接口回调阶段(属于初始化阶段)
Spring Aware 接口:(接口注入)(按照执行顺序排列)
-
BeanNameAware
-
BeanClassLoaderAware
-
BeanFactoryAware
-
EnvironmentAware
-
EmbeddedValueResolverAware
-
ResourceLoaderAware
-
ApplicationEventPublisherAware
-
MessageSourceAware
-
ApplicationContextAware
从 EnvironmentAware 开始,隶属 ApplicationContext 生命周期,因此只使用 BeanFactory 是无法注入这些 Bean 的。即 BeanFactory 只能注入前三个 Aware 回调,ApplicationContext 则可以注入所有回调。
class ApplicationContextAwareProcessor implements BeanPostProcessor { private final ConfigurableApplicationContext applicationContext; private final StringValueResolver embeddedValueResolver; /** * Create a new ApplicationContextAwareProcessor for the given context. */ public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) { this.applicationContext = applicationContext; this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory()); } @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){ return bean; } AccessControlContext acc = null; if (System.getSecurityManager() != null) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); } if (acc != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareInterfaces(bean); return null; }, acc); } else { invokeAwareInterfaces(bean); } return bean; } private void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } } }
AbstractApplicationContext#prepareBeanFactory 方法中通过 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
添加上面的 Processor
9.11 Spring Bean 初始化前阶段
-
已完成
-
Bean 实例化
-
Bean 属性赋值
-
Bean Aware 接口回调
-
-
方法回调
-
BeanPostProcessor#postProcessorBeforeInitialization
-
9.12 Spring Bean 初始化阶段
-
@PostContructor
-
实现 InitializingBean 接口的 afterProperties 方法
-
自定义初始化方法
public class BeanInitializationLifecycleDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); factory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor()); reader.loadBeanDefinitions("merged-bean-definition.xml"); System.out.println(factory.getBean("user")); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.gcl.bean.lifecycle.bean.User" init-method="init"> <property name="name" value="foo"/> <property name="age" value="18"/> </bean> <bean id="superUser" class="com.gcl.bean.lifecycle.bean.SuperUser" parent="user"> <property name="level" value="1"/> </bean> </beans>
9.13 Spring Bean 初始化后阶段
BeanPostProcessor#postProcessorAfterInitialization
初始化方法 AbstractAutowireCapableBeanFactory#initializeBean,该方法中调用了四个方法
-
invokeAwareMethods
-
applyBeanPostProcessorsBeforeInitialization
-
invokeInitMethods
-
applyBeanPostProcessorsAfterInitialization
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
因此生命周期会按照上述来进行。
9.14 Spring Bean 初始化完成阶段
Spring 4.1+:SmartInitializingSingleton#afterSingletonsInstantiated
public class BeanInstantiationLifecycleDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor()); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions("merged-bean-definition.xml"); factory.preInstantiateSingletons(); System.out.println(factory.getBean("user")); } } public class User implements InitializingBean, SmartInitializingSingleton { private String name; private int age; public User() { } public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } @PostConstruct public void postConstructor() { System.out.println("post"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("initialization bean"); } public void init() { System.out.println("init-method"); } @Override public void afterSingletonsInstantiated() { System.out.println("after"); } } // 运行结果: initialization bean init-method initialization bean init-method after after User{name='bar', age=19}
使用 SmartInitializingSingleton 要么在 ApplicaitonContext 场景中使用,如果在 BeanFactory 场景中使用,需要手动调用 preInstantiateSingletons 方法触发 Bean 的实例化,在该方法中会通过 beanDefinitionNames 依次进行初始化操作
public void preInstantiateSingletons() throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); } // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... 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 { // 此处执行 Bean 的初始化操作,初始化 Bean 中首先会创建依赖 Bean,创建完以后,再执行实例化操作 // 实例化操作执行过程中,会创建 BeanWrapper,实例化后通过 populateBean 执行属性赋值,调用 // InstantiationAwareBeanPostProcessors // BeanPostProcessors // 属性赋值完以后调用 initializeBean 方法进行初始化 // invokeAwareMethods // applyBeanPostProcessorsBeforeInitialization // invokeInitMethods:InitializingBean#afterPropertiesSet -> init-method // applyBeanPostProcessorsAfterInitialization getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { // 此处执行 Smart 的回调 smartSingleton.afterSingletonsInstantiated(); } } } }
9.15 Spring Bean 销毁前阶段
-
DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
9.16 Spring Bean 销毁阶段
-
@PreDestroy(跟 DestructionAwareBeanPostProcessor 是在同一等级执行的)
-
实现 DisposableBean 接口的 destroy 方法
-
自定义销毁方法
销毁最终调用 DisposableBeanAdapter#destroy 方法进行
@Override public void destroy() { if (!CollectionUtils.isEmpty(this.beanPostProcessors)) { for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) { // @PreConstructor 和 实现了 DestructionAwareBeanPostProcessor 接口的销毁逻辑在这里执行 processor.postProcessBeforeDestruction(this.bean, this.beanName); } } // 实现 DispoableBean 接口的销毁逻辑在这里执行 if (this.invokeDisposableBean) { if (logger.isTraceEnabled()) { logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'"); } try { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((DisposableBean) this.bean).destroy(); return null; }, this.acc); } else { ((DisposableBean) this.bean).destroy(); } } catch (Throwable ex) { String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'"; if (logger.isDebugEnabled()) { logger.warn(msg, ex); } else { logger.warn(msg + ": " + ex); } } } // 自定义销毁方法在这里执行 if (this.destroyMethod != null) { invokeCustomDestroyMethod(this.destroyMethod); } else if (this.destroyMethodName != null) { Method methodToInvoke = determineDestroyMethod(this.destroyMethodName); if (methodToInvoke != null) { invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke)); } } }
9.17 Spring Bean 垃圾收集阶段
-
关闭 Spring 容器(应用上下文)
-
执行GC
-
Spring Bean 覆盖的 finalize 方法被回调(不保证一定被执行)
9.18 面试题
9.18.1 BeanPostProcessor 使用场景
BeanPostProcessor 提供 Spring Bean 初始化前和初始化后的生命周期回调,分别对应 postProcessBeforeInitialization 以及 postProcessAfterInitialization 方法,允许对关心的 Bean 进行扩展,甚至是替换
其中 ApplicationContext 相关的 Aware 回调也是基于 BeanPostProcessor 实现的,即 ApplicationContextAwareProcessor
BeanPostProcessor 针对所有 Bean,所有 Bean 初始化都会走这里的逻辑(前提是进行了自定义实现以及注入),可以在 Bean 实例化前后加入一些操作,例如 ApplicationContextAwareProcessor 做法如下:
class ApplicationContextAwareProcessor implements BeanPostProcessor { private final ConfigurableApplicationContext applicationContext; private final StringValueResolver embeddedValueResolver; /** * Create a new ApplicationContextAwareProcessor for the given context. */ public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) { this.applicationContext = applicationContext; this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory()); } @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){ return bean; } AccessControlContext acc = null; if (System.getSecurityManager() != null) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); } if (acc != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareInterfaces(bean); return null; }, acc); } else { invokeAwareInterfaces(bean); } return bean; } private void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } } }
会拦截所有 bean,如果 bean 实现了上面六种接口中至少一种,就进行响应的 aware 回调处理
9.18.2 BeanFactoryPostProcessor 和 BeanPostProcessor 区别
BeanFactoryPostProcessor 是 Spring BeanFactory(实际为 ConfigurableListableBeanFatory)的后置处理器,用于扩展 BeanFactory,或通过 BeanFactory 进行依赖查找和依赖注入
BeanFactoryPostProcessor 必须有 Spring ApplicationContext 执行,BeanFactory 无法直接于其进行交互
而 BeanPostProcessor 则直接与 beanFactory 关联,属于 N 对 1 的关系
public interface BeanFactoryPostProcessor { /** * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for overriding or adding * properties even to eager-initializing beans. * @param beanFactory the bean factory used by the application context * @throws org.springframework.beans.BeansException in case of errors */ void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
从接口定义可知,该接口方法被回调时是 BeanDefiniton 都加载完成但还没有任何一个 Bean 进行实例化,此时可以对 BeanFactory 进行一定的操作
-
增加 BeanPostProcessor
-
依赖查找或依赖注入
通过 AbstractApplicationContext#refresh 方法中 invokeBeanFactoryPostProcessors 方法启动
BeanFactory 只能手动添加 BeanPostProcessor,但是在 ApplicationContext 中,在 AbstractApplicationContext#refresh 方法中会通过方法 registerBeanPostProcessors 来进行自动的扫描和注册
BeanPostProcessor 可以为其指定顺序 Order,可通过 @Order 注解、实现 Ordered 接口或者 PriorityOrdered 接口来指定顺序
9.18.3 BeanFactory 如何处理 Bean 生命周期
BeanFactory 默认实现为 DefaultListableBeanFactory,其中 Bean 生命周期与方法映射如下:
-
BeanDefinition 注册阶段——registerBeanDefinition
-
BeanDefinition 合并阶段——getMergedBeanDefinition
-
Bean 实例化前阶段——resolveBeforeInstantiation
-
Bean 实例化阶段——createBeanInstacence
-
Bean 实例化后阶段——populateBean
-
Bean 属性赋值前阶段——populateBean
-
Bean 属性复制阶段——populateBean
-
Bean Aware 接口回调阶段——initializeBean
-
Bean 初始化前阶段——initializeBean
-
Bean 初始化阶段——initializeBean
-
Bean 初始化后阶段——initializeBean
-
Bean 初始化完成阶段——preInstrantiateSingletons
-
Bean 销毁前阶段——destroyBean
-
Bean 销毁阶段——destroyBean
10. Spring 配置元信息
10.1 Spring 配置元信息
-
Spring Bean 配置元信息——BeanDefinition
-
Spring Bean 属性元信息——PropertyValues
-
Spring 容器配置元信息
-
Spring 外部化配置元信息+PropertySource
-
Spring Profile 元信息——@Profile
10.2 Spring Bean 配置元信息
-
GenericBeanDefinition:通用型 BeanDefinition
-
RootBeanDefinition:无 Parent 的 BeanDefinition 或者合并后的 BeanDefinition
-
AnnotatedBeanDefinition:注解标注的 BeanDefinition
GenericBeanDefinition 和 RootBeanDefinition 都继承了 AbstractBeanDefinition,AbstractBeanDefinition 提供了对于 Definiton 的写操作,GenericBeanDefinition 简单重写了 setParent 方法,RootBeanDefinition 则增加了许多字段(IoC 中 BeanDefitnition 通常需要 merge,merge 后的 BeanDefinition 就是 RootBeanDefinition,也叫做 mbd),这些字段用于创建 Bean 时提供一些辅助性操作(提升性能等)
DefaultListableBeanFactory 中存储所有 BeanDefinition,AbstractBeanDefinition 中存储所有 mergedBeanDefinition。
public interface AnnotatedBeanDefinition extends BeanDefinition { /** * Obtain the annotation metadata (as well as basic class metadata) * for this bean definition's bean class. * @return the annotation metadata object (never {@code null}) */ AnnotationMetadata getMetadata(); /** * Obtain metadata for this bean definition's factory method, if any. * @return the factory method metadata, or {@code null} if none * @since 4.1.1 */ @Nullable MethodMetadata getFactoryMethodMetadata(); }
AnnotatedBeanDefinition 可以通过反射或者 ASM 方式获取 AnnotationMetadata,Spring 5.2 及以后 ASM 方式标记过时,新增 SimpleAnnotationMetadataReadingVisitor 类使用
getFactoryMethodMetadata 元数据是否来自某个方法,比如 Factory 中方法,可有可无,因为并不是每个 Bean 都是通过 Factory method 来生成的
10.3 Spring Bean 属性元信息
-
Bean 属性元信息——PropertyValues
-
可修改实现——MutablePropertyValues
-
元素成员——PropertyValue
-
-
Bean 属性上下文存储——AttributeAccessor
-
Bean 元信息元素——BeanMetadataElement
属性元信息中后两个属性上下文存储以及 Bean 元信息元素两种元信息都是起辅助作用的,上下文信息可以在创建 Bean 的过程中进行使用,元信息元素可以标记 BeanDefinition 的来源,具体使用可参照下例:
public class BeanConfigurationMetadataDemo { public static void main(String[] args) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class); builder.addPropertyValue("name", "bar"); // 获取 AbstractBeanDefinition AbstractBeanDefinition definition = builder.getBeanDefinition(); // 附加属性(不影响 Bean populate、initialize) definition.setAttribute("name", "foo"); // 当前 BeanDefinition 来自哪里,辅助作用 definition.setSource(BeanConfigurationMetadataDemo.class); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.registerBeanDefinition("user", definition); factory.addBeanPostProcessor(new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof User && beanName.equals("user")) { User user = (User) bean; BeanDefinition beanDefinition = factory.getBeanDefinition("user"); if (beanDefinition.getSource() == BeanConfigurationMetadataDemo.class) { String name = (String) beanDefinition.getAttribute("name"); user.setName(name); return user; } } return bean; } }); System.out.println(factory.getBean("user", User.class)); } } // 运行结果: User{name='foo', age=0}
10.4 Spring 容器配置元信息
Spring XML 配置元信息——beans 元素相关
beans 元素属性 | 默认值 | 使用场景 |
---|---|---|
profile | null | Spring Profile 配置值 |
default-lazy-init | default | 当 outter beans "default-lazy-init" 属性存在时,继承该值,否则为"false" |
default-merge | deafult | 当 outter beans "default-merge" 属性存在时,继承该值,否则为"false" |
default-autowire | default | 当 outter beans "default-autowire" 属性存在时,继承该值,否则为"false" |
default-autowire-candidates | null | 默认 Spring Beans 名称 pattern |
default-init-method | null | 默认 Spring Beans 自定义初始化方法 |
default-destroy-method | null | 默认 Spring Beans 自定义销毁方法 |
上面这些属性,都是在 XML 的 beans 标签中设置的
Spring XML 配置元信息——应用上下文相关
XML 元素 | 使用场景 |
---|---|
<context:annotation-config/> | 激活 Spring 注解驱动 |
<context:component-scan/> | Spring @Component 以及自定义注解扫描 |
<context:load-time-weaver/> | 激活 Spring LoadTimeWeaver |
<context:mbean-export/> | 暴露 Spring Beans 作为 JMX Beans |
<context:mbean-server/> | 将当前平台作为 MBeanServer |
<context:property-placeholder/> | 加载外部化配置资源作为 Spring 属性配置 |
<context:property-override/> | 利用外部化配置资源覆盖 Spring 属性值 |
可以在 BeanDefinitionParserDelegate 中查看其解析规则
10.5 基于 XML 文件装载 Spring Bean 配置元信息
Spring Bean 配置元信息
XML 元素 | 使用场景 |
---|---|
<beans:beans> | 单 XML 资源下的多个 Spring Beans 配置 |
<beans:bean> | 单个 Spring Bean 定义(BeanDefinition)配置 |
<beans:alias> | 为 Spring Bean 定义(BeanDefinition)映射别名 |
<beans:import> | 加载外部 Spring XML 资源配置 |
底层实现:XmlBeanDefinitionReader
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { // ... // 从输入流注册 BeanDefinition return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); // ... } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { // ... // 获取 DOM 操作需要的 Document 元素 Document doc = doLoadDocument(inputSource, resource); // 执行注册 int count = registerBeanDefinitions(doc, resource); // ... } public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 创建 DocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 保存现有 BeanDefinition 数量 int countBefore = getRegistry().getBeanDefinitionCount(); // 注册 BeanDefinitions documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 返回注册 BeanDefinition 数量 return getRegistry().getBeanDefinitionCount() - countBefore; } @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); } protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { // 获取设置的 profiles 跟 环境变量中 profile 进行比较,相同才往后执行,不同就注解返回不解析 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); // 真正解析的地方 parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; } // XML -> BeanDefinition protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } // Spring 默认解析的元素 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } } // 自定义 XML 解析 @Nullable public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); }
10.6 基于 Properties 文件装载 Spring Bean 配置元信息(不推荐)
Spring Bean 配置元信息
Properties 属性名 | 使用场景 |
---|---|
(class) | Bean 类全限定名 |
(abstract) | 是否为抽象的 BeanDefinition |
(parent) | 指定 parent BeanDefinition 名称 |
(lazy-init) | 是否为延迟初始化 |
(ref) | 引用其他 Bean 的名称 |
(scope) | 设置 Bean 的 scope 属性 |
$n | n 表示第 n+1 个构造器参数 |
底层实现:PropertiesBeanDefinitionReader
不推荐使用,配置很复杂并且局限性很大
10.7 基于 Java 注解装载 Spring Bean 配置元信息
Spring 模式注解:
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@Repository | 数据仓储模式注解 | 2.0 |
@Component | 通用组件模式注解 | 2.5 |
@Service | 服务模式注解 | 2.5 |
@Controller | Web 控制器模式注解 | 2.5 |
@Configuration | 配置类模式注解 | 3.0 |
Spring Bean 依赖注入注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@Autowired | Bean 依赖注入,支持多种依赖查找方式 | 2.5 |
@Qualifier | 细粒度的 @Autowired 依赖查找 | 2.5 |
Java 注解 | 场景说明 | 起始版本 |
---|---|---|
@Resource | 类似于 @Autowired | 2.5 |
@Inject | 类似于 @Autowired | 2.5 |
AutowiredAnnotationBeanPostProcessor 处理 @Autowired、@Value、@Inject 注解
CommonAnnotationBeanPostProcessor 处理 @Resource 注解,javax.xml.ws.WebServiceRef注解、javax.ejb.EJB,主要处理 j2se 或者 Java EE 相关的注解或者 API
Spring Bean 条件装配注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@Profile | 配置化条件装配 | 3.1 |
@Conditional | 编程条件装配 | 4.0 |
@Profile 通过 @Conditional 实现
实现 Condition 接口
class ProfileCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 读取注解所有的信息 MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { for (Object value : attrs.get("value")) { if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) { return true; } } return false; } return true; } }
Spring Bean 生命周期回调注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@PostConstructor | 替换 XML 元素 <bean init-method="..."/> 或 InitializingBean | 2.5 |
@PreDestroy | 替换 XML 元素 <bean destroy-method="..."/> 或 DisposableBean | 2.5 |
CommonAnnotationBeanPostProcessor 中处理这两个注解
10.8 Spring Bean 配置元信息底层实现
Spring BeanDefinition 解析与注册
实现场景 | 实现类 | 起始版本 |
---|---|---|
XML 资源 | XmlBeanDefinitionReader | 1.0 |
Properties 资源 | PropertiesBeanDefinitionReader | 1.0 |
Java 注解 | AnnotatedBeanDefinitionReader | 3.0 |
Spring 注解驱动主要开始于 Spring 2.5 版本
Java 注解和类相关,并不和资源相关,因此 AnnotatedBeanDefinitionReader 和 BeanDefinitionReader 这个接口是没有关系的,因为 BeanDenitionReader 是基于资源(Resource)的,XmlBeanDefinitionReader 和 PropertiesBeanDefinitionReader 都属于 BeanDenitionReader 实现
10.8.1 Spring XML 资源 BeanDefinition 解析与注册
核心 API——XmlBeanDefinitionReader
-
资源——Resource
-
底层——BeanDefinitionDocumentReader
-
XML 解析——Java DOM Level 3 API
-
BeanDefinition 解析——BeanDefinitionParserDelegate
-
BeanDefinition 注册——BeanDefinitionRegistrar
-
10.8.2 Spring Properties 资源 BeanDefinition 解析与注册
核心 API——PropertiesBeanDefinitionReader
-
资源
-
字节流——Resource
-
字符流——EncodedResource
-
-
底层
-
存储——java.util.Properties
-
BeanDefinition 解析——API 内部实现
-
BeanDefinition 注册——BeanDefinitionRegistry
-
10.8.3 Spring Java 注册 BeanDefinition 解析与注册
核心 API——AnnotatedBeanDefinitionReader
-
资源——类对象 java.lang.Class
-
底层
-
条件评估——ConditionEvaluattor
-
Bean 范围解析——ScopeMetadataResolver
-
BeanDefinition 解析——内部 API 实现
-
BeanDefinition 处理——AnnotationConfigUtils.processCommonDefinitionAnnotations
-
BeanDefinition 注册——BeanDefinitionRegistry
-
public class AnnotatedBeanDefinitionReader { // Bean 注册容器 private final BeanDefinitionRegistry registry; // Bean 名称生成器 private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE; // Scope 解析器,是否单例以及代理模式 private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); // 评估器,评估是否需要跳过注册 private ConditionEvaluator conditionEvaluator; // ... }
10.9 基于 XML 文件装载 Spring IoC 容器配置元信息
spring.handlers 文件中配置解析命名空间的 handler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
spring.schema 文件中配置命名空间对应的 xsd 文件
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
handler 继承 NamespaceHandlerSupport,重写 init 方法,init 方法内部注册具体的解析类
public class UtilNamespaceHandler extends NamespaceHandlerSupport { private static final String SCOPE_ATTRIBUTE = "scope"; @Override public void init() { // 注册解析 list、map 等的 parser 类 registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser()); registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser()); registerBeanDefinitionParser("list", new ListBeanDefinitionParser()); registerBeanDefinitionParser("set", new SetBeanDefinitionParser()); registerBeanDefinitionParser("map", new MapBeanDefinitionParser()); registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser()); } // ... }
解析类继承 AbstractSingleBeanDefinitionParser,重写 doParse 方法
private static class ListBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return ListFactoryBean.class; } @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { List<Object> parsedList = parserContext.getDelegate().parseListElement(element, builder.getRawBeanDefinition()); builder.addPropertyValue("sourceList", parsedList); String listClass = element.getAttribute("list-class"); if (StringUtils.hasText(listClass)) { builder.addPropertyValue("targetListClass", listClass); } String scope = element.getAttribute(SCOPE_ATTRIBUTE); if (StringUtils.hasLength(scope)) { builder.setScope(scope); } } }
此处解析完成,BeanDefinitionBuilder 属性也设置完成,返回 AbstractBeanDefinitionParser#parse 方法将结果封装成 BeanDefinitionHolder 进行注册。整体流程如下:
XmlBeanDefinitionReader#loadBeanDefinition(Resource) -> doLoadBeanDefinitions() -> doLoadDocument() -> registerBeanDefinitions() -> DefaultBeanDefinitionDocumentReader#registerBeanDefinitions() -> doRegisterBeanDefinitions() -> parseBeanDefinitions() -> BeanDefinitionParserDelegate#parseCustomElement() -> 根据 namespaceUri 获取 handler -> NamespaceHandlerSupport#parse -> 查找 parser -> BeanDefinitionParser#parse()(此处将parseInternal 解析完成的 BeanDefinition 封装成 BeanDefinitionHolder 进行注册) -> parseInternal() -> doParse()(子类重写的方法,解析逻辑在此实现)
parseCustonElement 流程:
-
获取 namespace
-
通过 namespace 获取 handler
-
构造 ParserContext
-
解析元素,获取 BeanDefinition
10.10 基于 Java 注解装载 Spring IoC 容器配置元信息
10.10.1 Spring IoC 容器装配注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@ImportResource | 替换 XML 元素 <import> | 3.0 |
@Import | 导入 Configuration Class | 3.0 |
@ComponentScan | 扫描指定 package 下标注 Spring 模式注解的类 | 3.1 |
@ImportResource 导入 xml 文件,@Import 导入 Java 类(配置类或者普通类)
10.10.2 Spring IoC 属性配置注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@PropertySource | 配置属性抽象 PropertySource | 3.1 |
@PropertySources | @PropertySource 集合注解 | 4.0 |
从 Java 8 开始,一个类上支持同时使用多个 @PropertySource 注解
10.11 基于 Extensible XML authoring 扩展原理
扩展步骤:
-
编写 XML Schema 文件:定义 XML 结构
-
自定义 NamespaceHandler 实现:命名空间绑定
-
自定义 BeanDefinitionParser 实现:XML 元素与 BeanDefinition 解析
-
注册 XML 扩展:命名空间与 XML Schema 映射
10.11.1 定义 xsd
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns="http://gcl.com/schema/users" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://gcl.com/schema/users"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/> <xsd:complexType name="User"> <xsd:attribute name="id" type="xsd:long" use="required"/> <xsd:attribute name="name" type="xsd:string" use="required"/> <xsd:attribute name="age" type="xsd:int"/> </xsd:complexType> <xsd:element name="user" type="User"/> </xsd:schema>
10.11.2 定义 handler
public class CustomHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("user", new CustomParser()); } }
http\://gcl.com/schema/users=com.gcl.configuration.metadata.handler.CustomHandler
10.11.3 定义 parser
public class CustomParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return User.class; } @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { setAttribute("name", element, builder); setAttribute("age", element, builder); } private void setAttribute(String attributeName, Element element, BeanDefinitionBuilder builder) { String attribute = element.getAttribute(attributeName); if (StringUtils.hasText(attribute)) { builder.addPropertyValue(attributeName, attribute); } } }
10.11.4 注册 xml 扩展
http\://gcl.com/schema/users.xsd=META-INF/com/gcl/configuration/metadata/users.xsd
10.11.5 测试
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:users="http://gcl.com/schema/users" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://gcl.com/schema/users http://gcl.com/schema/users.xsd" > <users:user id="1" name="foo" age="18"/> </beans>
public class CustomXmlParserDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions("classpath:META-INF/users.xml"); System.out.println(factory.getBean(User.class)); } } // 运行结果: User{name='foo', age=18}
10.12 基于 Properties 资源装载外部化配置
-
注解驱动
-
@org.springframework.context.annotation.PropertySource
-
@org.springframework.context.annotation.PropertySources
-
-
API 编程
-
org.springframework.core.env.PropertySource
-
org.springframework.core.env.PropertySources
-
Java 8 支持重复注解功能,也就是支持一个类上重复使用相同的注解,因此可以使用多个 @PropertySource 替换 @PropertySources 注解
10.13 基于 YAML 资源装载外部化配置
API 编程
-
org.springframework.beans.factory.config.YamlProcessor
-
org.springframework.beans.factory.config.YamlMapFactoryBean
-
org.springframework.beans.factory.config.YamlPropertiesFactoryBean
-
10.14 面试题
10.14.1 Spring 内建 XML Schema 常见有哪些
命名空间 | 所属模块 | Schema 资源 URL |
---|---|---|
beans | spring-beans | http://www.springframework.org/schema/beans/spring-beans.xsd |
context | spring-context | http://www.springframework.org/schema/context/spring-context.xsd |
aop | spring-aop | http://www.springframework.org/schema/sop/spring-aop.xsd |
tx | spring-tx | http://www.springframework.org/schema/tx/spring-tx.xsd |
util | spring-beans | http://www.springframework.org/schema/util/spring-util.xsd |
tool | spring-beans | http://www.springframework.org/schema/tool/spring-tool.xsd |
10.14.2 Spring 配置元信息具体有哪些
-
Bean 配置元信息:通过媒介(如 XML、Properties等),解析 BeanDefinition
-
IoC 容器配置元信息:通过媒介(如:XML、Properties等),控制 IoC 容器行为,比如注解驱动、AOP等
-
外部化配置:通过资源抽象(如:Properties、YAML等),控制 PropertySource
-
Spring Profile:通过外部化配置,提供条件分支流程
10.14.3 Extensible XML authoring 的缺点
-
高复杂度:开发人员要熟悉 XML Schema,spring.handlers、spring.schemas 以及 Spring API
-
嵌套元素支持较弱:通常需要使用方法递归或者其嵌套解析的方式处理嵌套元素
-
XML 处理性能较差:Spring XML 基于 DOM Level 3 API 实现,该 API 便于理解,然而性能较差
-
XML 框架移植性较差:很难适配高性能和便利性的 XML 框架,如 JAXB
11. Spring 资源管理
11.1 引入动机
-
Java 标准资源管理强大,然而扩展负载,资源存储方式并不统一
-
Spring 要自立门户
-
Spring 3C原则:抄、超、潮
11.2 Java 标准资源管理
Java 标准资源定位
职责 | 说明 |
---|---|
面向资源 | 文件系统、artifact(jar、war、ear文件)以及远程资源(HTTP、FTP等) |
API整合 | java.lang.ClassLoader#getResource、java.io.File 或 java.net.URL |
资源定位 | java.net.URL 或 java.net.URI |
面向流式存储 | java.net.URLConnection |
协议扩展 | java.net.URLStreamHandler 或 java.net.URLStreamHandlerFactory |
Java URL 协议扩展
-
基于 java.net.URLStreamHandlerFactory
-
基于 java.net.URLStreamHandler
基于 java.net.URLStreamHandler 扩展协议
-
jdk 1.8 内建实现
协议 实现类 file sun.net.www.protocol.file.Handler ftp sun.net.www.protocol.ftp.Handler http sun.net.www.protocol.http.Handler https sun.net.www.protocol.https.Handler jar sun.net.www.protocol.jar.Handler mailto sun.net.www.protocol.mailto.Handler netdoc sun.net.www.protocol.netdoc.Handler -
扩展协议:实现类名必须为”Handler“
实现类命名规则 说明 默认 sun.net.www.protocol.{protocl}.Handler 自定义 通过 Java Properties java.protocol.handler.pkgs 指定实现类包名,实现类名必须为"Handler"。如果存在多包名指定,通过分隔符"|"
11.3 Spring 资源接口
类型 | 接口 |
---|---|
输入流 | org.springframework.core.io.InputStreamSource |
只读资源 | org.springframework.core.io.Resource |
可写资源 | org.springframework.core.io.WritableResource |
编码资源 | org.springframework.core.io.support.EncodedResource |
上下文资源 | org.springframework.core.io.ContextResource |
上下文资源是给 Servlet 来使用的,应用较为狭窄
11.4 Spring 内建 Resource 实现
资源来源 | 资源协议 | 实现类 |
---|---|---|
Bean 定义 | 无 | org.springframework.beans.factory.support.BeanDefinitionResource |
数组 | 无 | org.springframework.core.io.ByteArrayResource |
类路径 | class path:/ | org.springframework.core.io.ClassPathResource |
文件系统 | file:/ | org.springframework.core.io.FileSystemResource |
URL | URL 支持的协议 | org.springframework.core.io.UrlResource |
ServletContext | 无 | org.springframework.web.context.support.ServletContextResource |
重要的是类路径、文件系统、URL 三种,这三种都实现 Resource 接口,也就是说 Spring 将从类路径、文件系统以及 URL 中获取数据抽象成了 Resource 接口,用一个接口的不同实现就可以读取不同的资源
11.5 Spring Resource 接口扩展
-
可写资源
-
org.springframework.core.io.WritableResource
-
org.springframework.core.io.FileSystemResource
-
org.springframework.core.io.FileUrlResource(@Since 5.0.2)
-
org.springframework.core.io.PathResource(@Since 4.0 & @Deprecated)
-
-
-
编码资源
-
org.springframework.core.io.support.EncodedResource
-
11.6 Spring 资源加载器
Resource 加载器
-
org.springframework.core.io.ResourceLoader
-
org.springframework.core.io.DefaultResourceLoader
-
org.springframework.core.io.FileSystemResourceLoader
-
org.springframework.core.io.ClassRelativeResourceLoader
-
org.springframework.context.support.AbstractApplicationContext
-
-
11.7 Spring 通配路径资源加载器
-
通配路径
-
org.springframework.core.io.support.ResourcePatternResolver
-
org.springframework.core.io.support.PathMatchingResourcePatternResolver
-
-
-
路径匹配器
-
org.springframework.util.PathMatcher
-
Ant 模式匹配实现——org.springframework.util.AntPathMatcher
-
-
11.8 Spring 通配路径资源扩展
-
实现 org.springframework.util.PathMatcher
-
重置 PathMathcer——PathMatchingResourcePatternResolver#setPathMatcher
11.9 依赖注入 Spring Resource
基于 @Value 实现:
// 注入单个 Resource @Value("classpath:/...") private Resource resource; // 注入多个 Resource @Value("classpath*:/*.properties") private Resource[] resources;
11.10 依赖注入 ResourceLoader
-
方法一:实现 ResourceLoaderAware 回调
-
方法二:@Autowired 注入 ResourceLoader
-
方法三:注入 ApplicationContext 作为 ResourceLoader
public class InjectingResourceLoaderDemo implements ResourceLoaderAware { private ResourceLoader aware; @Resource private ResourceLoader loader; @Resource private AbstractApplicationContext abstractContext; @Resource private ApplicationContext context; @PostConstruct public void init() { System.out.println("aware == loader? " + (aware == loader)); System.out.println("aware == abstractContext? " + (aware == abstractContext)); System.out.println("aware == context? " + (aware == context)); } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(InjectingResourceLoaderDemo.class); context.refresh(); context.close(); } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.aware = resourceLoader; } } // 运行结果: aware == loader? true aware == abstractContext? true aware == context? true
11.11 面试题
11.11.1 Spring 配置资源中有哪些常见类型
-
XML 资源
-
Properties 资源
-
YAML 资源
11.11.2 请列举不同类型 Spring 配置资源
-
XML 资源
-
普通 Bean Definition XML 配置资源——*.xml
-
Spring Schema 资源——*.xsd
-
-
Properties 资源
-
普通 Properties 格式资源——*.properties
-
Spring Handler 实现类映射文件——META-INF/spring.hanlders
-
Spring Schema 资源映射文件+META-INF/spring.schemas
-
-
YAML 资源
-
普通 YAML 配置资源——*.yaml 或 *.yml
-
11.11.3 Java 标准资源管理扩展的步骤
-
简易实现
-
实现 URLStreamHandler 并放置在 sun.net.protocol.${protocl}.Handlers 包下
-
-
自定义实现
-
实现 URLStreamHandler
-
添加 -Djava.protocol.handler.pkgs 启动参数,指向 URLStreamHandler 实现类的包下
-
-
高级实现
-
实现 URLStreamHandlerFactory 并传递到 URL 之中
-
12. Spring 国际化(i18n)
12.1 Spring 国际化使用场景
-
普通国际化文案
-
Bean Validation 校验国际化文案
-
Web 站点页面渲染
-
Web MVC 错误消息提示
12.2 Spring 国际化接口
-
核心接口
-
org.springframework.context.MessageSource
-
-
主要概念
-
文案模板编码(code)
-
文案模板参数(args)
-
区域(Locale)
-
12.3 层次性 MessageSource
-
Spring 层次性接口回顾
-
org.springframework.beans.factory.HierarchicalBeanFactory
-
org.springframework.context.ApplicationContext
-
org.springframework.beans.factory.config.BeanDefinition
-
-
Spring 层次性国际化接口
-
org.springframework.context.HierarchicalMessageSource
-
12.4 Java 国际化标准实现
核心接口
-
抽象实现——java.util.ResourceBundle
-
Properties 资源实现——java.util.PropertyResourceBundle
-
例举实现——java.util.ListResourceBundle
ResourceBundle 核心实现
-
Key-Value 设计
-
层次性设计
-
缓存设计
-
字符编码控制——java.util.ResourceBundle.Control(@Since 1.6)
-
Contrl SPI 扩展——java.util.spi.ResourceBundleControlProvider(@Since 1.8)
12.5 Java 文本格式
-
核心接口
-
java.text.MessageFormat(非线程安全,每次使用 new 一个实例来使用)
-
-
基本用法
-
设置消息格式模式——new MessageFormat(...)
-
格式化——format(new Object[]{...})
-
-
消息格式模式
-
格式元素:{ArgumentIndex(,FormatType,(FormatStyle))}
-
FormatType:消息格式类型,可选项,每种类型在 number、date、time 和 choice 类型选其一
-
FormatStyle:消息格式风格,可选项,包括:short、medium、long、full、integer、currency、percent
-
-
高级用法
-
重置消格式模式
-
重置 java.util.Locale
-
重置 java.text.Format
-
12.6 MessageSource 开箱即用实现
-
基于 ResourceBundle + MessageFormat 组合 MessageSource 实现
-
org.springframework.context.support.ResourceBundleMessageSource
-
-
可重载 Properties + MessageFormat 组合 MessageSource 实现
-
org.springframework.context.support.ReloadableResourceBundleMessageSource
-
12.7 MessageSource 内建实现
MessageReource 内建 Bean 可能来源
-
预注册 Bean 名称为 "messageSource",类型为:MessageSource Bean
-
默认内建实现——DelegatingMessageSource(层次性查找 MessageSource 对象)
12.8 课外资料
Spring Boot 为什么要新建 MessageSource Bean?
-
AbstractApplciationContext 的实现决定了 MessageSource 内建实现
-
Spring Boot 通过外部化配置简化 MessageSource Bean 构建
-
Spring Boot 基于 Bean Validation 校验非常普遍
12.9 面试题
12.9.1 Spring 国际化接口有哪些?
-
核心接口——MessageSource
-
层次性接口——org.springframework.context.HierarchicalMessageSource
12.9.2 Spring 有哪些 MessageSource 内建实现
-
org.springframework.context.support.ResourceBundleMessageSource
-
org.springframework.context.support.ReloadableResourceBundleMessageSource
-
org.springframework.context.support.StaticMessageSource
-
org.springframework.context.support.DelegatingMessageSource
12.9.3 如何实现配置自动更新 MessageSource
主要技术:
-
Java NIO 2:java.nio.file.WatchService
-
Java Concurrency:java.util.concurrent.ExecutorService
-
Spring:org.springframework.context.support.AbstractMessageSource
13. Spring 校验
13.1 Spring 校验使用场景
-
Spring 常规校验(Validation)
-
Spring 数据绑定(DataBinder)
-
Spring Web 参数绑定(WebDataBinder)
-
Spring WebMVC/WebFlux 处理方法参数校验
13.2 Validator 接口设计
接口职责
-
Spring 内部校验器接口,通过编程的方式校验目标对象
核心方法
-
supports(Class):校验目标类能否校验
-
validate(Object,Errors):校验目标对象,并将校验失败的内容输出至 Errors 对象
配套组件
-
错误收集器:org.springfamework.validation.Errors
-
Validator 工具类:org.springframework.validation.ValidationUtils
13.3 Errors 接口设计
接口职责
-
数据绑定和校验错误收集接口,与 Java Bean 和其属性有强关联性
核心方法
-
reject 方法(重载):收集错误文案
-
rejectValue 方法(重载):收集对象字段中的错误文案
配套组件
-
Java Bean 错误描述:org.springfamework.validation.ObjectError
-
Java Bean 属性错误描述:org.springframework.validation.FieldError
13.4 Errors 文案来源
-
选择 Errors 实现(如:org.springframework.validation.BeanPropertyBindingResult)
-
调用 reject 或 rejectValue 方法
-
获取 Errors 对象中 ObjectError 或 FieldError
-
将 ObjectError 或 FieldError 中的code 和args,管理啊 MessageSource 实现(如:ResourceBundleMessageSource)
13.5 自定义 Validator
实现 org.springframework.validation.Validator 接口
-
实现 supports 方法
-
实现 validate 方法
-
通过 Errors 对象收集错误
-
ObjectError:对象(Bean)错误
-
FieldError:对象(Bean)属性(Property)错误
-
-
通过 ObjectError 和 FieldError 关联 MessageSource 实现获取最终文案
-
13.6 Validator 的救赎
Bean Validation 与 Validator 适配
-
核心组件——org.springframework.validation.beanvalidation.LoacalValidationFactoryBean
-
依赖 Bean Validation——JSR-303 or JSR-349 provider
-
Bean 方法参数校验——org.springframework.validation.beanvalidation.MethodValidationProcessor
13.7 面试题
13.7.1 Spring 校验接口是什么?
org.springframework.validation.Validator
13.7.2 Spring 有哪些校验核心组件
-
检验器:org.springframework.validation.Validator
-
错误收集器:org.springframework.validation.Errors
-
Java Bean 错误描述:org.springframework.validation.ObjectError
-
Java Bean 属性错误描述:org.springframework.validation.FieldError
-
Bean Validation 适配:org.springframework.validation.beaanvalidation.LocalValidatorFactoryBean
13.7.3 请通过示例演示 Spring Bean 的校验
后续章节详细介绍
14. Spring 数据绑定
14.1 Spring 数据绑定使用场景
-
Spring BeanDefinition 到 Bean 实例创建
-
Spring 数据绑定(DataBinder)
-
Spring Web 参数绑定(WebDataBinder)
14.2 Spring 数据绑定组件
-
标准组件
-
org.springframework.validation.DataBinder
-
-
Web 组件
-
org.springframework.web.bind.WebDataBinder
-
org.springframework.web.bind.ServletRequestDataBinder
-
org.springframework.web.bind.WebRequestDataBinder
-
org.springframework.web.bind.WebExchangeDataBinder
-
DataBinder 核心属性
属性 | 说明 |
---|---|
target | 关联目标 Bean |
objectName | 目标 Bean 名称 |
bindingResult | 属性绑定结果 |
typeConverter | 类型转换器 |
conversionService | 类型转换服务 |
messageCodesResolver | 校验错误文案 Code 处理器 |
validators | 关联的 Bean Validator 实例集合 |
DataBinder 绑定方法
-
bind(PropertyValues):将 PropertyValues Key-Value 内容映射到关联 Bean(target)中的属性上
-
假设 PropertyValues 中包含 "name=小马哥"的键值对,同时 Bean 对象 User 中存在 name 属性,当 bing 方法执行时,User 对象中的 name 属性值将被绑定为 "小马哥"
-
14.3 Spring 数据绑定元数据
DataBinder 元数据——PropertyValues
特征 | 说明 |
---|---|
数据来源 | BeanDefinition,主要来源 XML 资源配置 BeanDefinition |
数据结构 | 由一个或多个 PropertyValue 组成 |
成员结构 | PropertyValue 包含属性名称,以及属性值(包括原始值、类型转换后的值) |
常见实现 | MutablePropertyValues |
Web 扩展实现 | ServletConfigPropertyValues、ServletRequestParameterPropertyValues |
相关生命周期 | InstantiationAwareBeanPostProcessor#postProcessProperties |
14.4 Spring 数据绑定控制参数
数据绑定特殊场景分析
-
当 PropertyValues 中包含名称 x 的 PropertyValue,目标对象 B 不存在 x 属性,当 bind 方法执行时,会发生什么?
-
忽略未知属性
-
-
当 PropertyValues 中包含名称 x 的 PropertyValue,目标对象 B 中存在 x 属性,当 bind 方法执行时,如何避免 B 属性 x 不被绑定
-
当 PropertyValues 中包含名称 x.y 的 PropertyValue,目标对象 B 中存在 x 属性(嵌套 y 属性),当 bind 方法执行时,会发生什么?
-
支持嵌套属性,嵌套对象被创建并被填充
-
DataBinder 绑定控制参数
参数名称 | 说明 |
---|---|
ignoreUnkonwnFields | 是否忽略未知字段,默认值:true |
ignoreInvalidFields | 是否忽略非法字段,默认值:false |
autoGrowNestedPaths | 是否自动增加嵌套路径,默认值:true |
allowedFields | 绑定字段白名单 |
disallowedFields | 绑定字段黑名单 |
requiredFields | 必须绑定字段 |
14.5 Spring 底层 Java Bean 替换实现
JavaBeans 核心实现——java.beans.BeanInfo
-
属性(Property)
-
java.beans.PropertyEditor
-
-
方法(Method)
-
事件(Event)
-
表达式(Expression)
Spring 替代实现——org.springframework.beans.BeanWrapper
-
属性(Property)
-
java.beans.PropertyEditor
-
-
嵌套属性路径(nested path)
14.6 BeanWrapper 的使用场景
-
Spring 底层 JavaBeans 基础设施的中心化接口
-
通常不会直接使用,间接用于 BeanFactory 和 DataBinder
-
提供标准 javaBeans 分析和操作,能够单独或批量存储 Java Bean 的属性(propertues)
-
支持嵌套型路径(nested path)
-
实现类 org.springframework.beans.BeanWrapperImpl
14.7 课外资料
标准 javaBeans 是如何操作属性的?
API | 说明 |
---|---|
java.beans.Introspector | Java Bean 内省 API |
java.beans.BeanInfo | Java Bean 元信息 API |
java.beans.BeanDescriptor | Java Bean 信息描述符 |
java.beans.PropertyDescriptor | Java Bean 属性描述符 |
java.beans.MethodDescriptor | Java Bean 方法描述符 |
java.beans.EventSetDescriptor | Java Bean 事件集合描述符 |
14.8 DataBinder 数据校验
DataBinder 与 BeanWrapper
-
bind 方法生成 BeanPropertyBindingResult
-
BeanPropertyBindingResult 关联 BeanWrapper
14.9 面试题
14.9.1 Spring 数据绑定的 API 是什么?
org.springframework.DataBinder
14.9.2 BeanWrapper 和 JavaBeans 之间关系是?
BeanWrapper 是 Spring 底层 JavaBeans 基础设施的中心化接口
14.9.3 DataBinder 是怎么完成属性类型转换的?
下章揭晓
15. Spring 类型转换
15.1 Spring 类型转换的实现
-
基于 JavaBeans 接口的类型转换实现
-
基于 java.beans.PropertyEditor 接口扩展
-
-
Spring 3.0+ 通用类型转换实现
15.2 使用场景
场景 | 基于 JavaBeans 接口的类型转换实现 | Spring 3.0+ 通用类型转换实现 |
---|---|---|
数据绑定 | YES | YES |
BeanWrapper | YES | YES |
Bean 属性类型转换 | YES | YES |
外部化属性类型转换 | NO | YES |
15.3 基于 JavaBeans 接口的类型转换
-
核心职责
-
将 String 类型的内容转化为目标类型的对象
-
-
扩展原理
-
Spring 框架将文本内容传递到 PropertyEditor 实现的 setAsText(String) 方法
-
PropertyEditor#setAsText(String) 方法实现将 String 类型给转化为目标类型的对象
-
将目标类型的对象传入 PropertyEditor#setValue(Object) 方法
-
PropertyEditor#setValue(Object) 方法实现需要临时存储传入对象
-
Spring 框架将通过 PropertyEditor#getValue() 获取类型转换后的对象
-
15.4 Spring 内建 PropertyEditor 扩展
内建扩展(org.springframework.beans.propertyeditors包下)
转换场景 | 实现类 |
---|---|
String -> Byte 数组 | org.springframework.beans.propertyeditors.ByteArrayPropertyEditor |
String -> Char | org.springframework.beans.propertyeditors.CharacterEditor |
String -> Char 数组 | org.springframework.beans.propertyeditors.CharArrayPropertyEditor |
String -> Charset | org.springframework.beans.propertyeditors.CharsetEditor |
String -> Class | org.springframework.beans.propertyeditors.ClassEditor |
String -> Currency | org.springframework.beans.propertyeditors.CurrencyEditor |
... | ... |
15.5 自定义 PropertyEditor 扩展
扩展模式
-
扩展 java.beans.PropertyEditorSupport 类
实现 org.springframework.beans.PropertyEditorRegistrar
-
实现 registerCustomEditors(org.springframework.beans.PropertyEditorRegistrar) 方法
-
将 PropertyEditorRegistrar 实现注册为 Spring Bean
向 org.springframework.beans.PropertyEditorRegistrar 注册自定义 PropertyEditor 实现
-
通用类型实现 registerCustomEditor(Class<?>, PropertyEditor)
-
Java Bean 属性类型实现:registerCustomEditor(Class<?>, String, PropertyEditor)
15.6 Spring PropertyEditor 的设计缺陷
-
违反职责单一原则:java.beans.PropertyEditor 接口职责太多,除了类型转换,还包括 Java Beans 事件和 Java GUI 交互
-
java.beans.PropertyEditor 实现类型局限:来源类型只能为 java.lang.String 类型
-
java.beans.PropertyEditor 实现缺少类型安全:除了实现类命名可以表达语义,实现类无法感知目标类型转换
15.7 Spring 3 通用类型转换接口
类型转换接口——org.springframework.core.convert.Converter<S, T>
-
泛型参数 S:来源类型,参数 T:目标类型
-
核心方法:T convert(S)
通用类型转换接口_org.springframework.core.convert.converter.GenericConverter
-
和新方法:convert(Object, TypeDescriptor, TypeDescriptor)
-
配对类型:org.springframework.core.convert.converter.GenericConverter.ConvertiblePair
-
类型描述:org.springfamework.core.convert.TypeDescriptor
15.8 Spring 内建类型转换器
转换场景 | 实现类所在包名(package) |
---|---|
日期/时间相关 | org.springframework.format.datetime |
Java 8 日期/时间相关 | org.springframework.format.datetime.standard |
通用实现 | org.springframework.core.convert.support |
15.9 Convertor 接口的局限性
局限一:缺少 Source Type 和 Target Type 前置判断
-
应对:增加 org.springframework.core.convert.converter.ConditionalConverter 实现
局限二:仅能转换单一的 Source Type 和 Target Type
-
应对:使用 org.springframework.core.convert.converter.。GenericConverter 代替
15.10 GenericConverter 接口
org.springframework.core.convert.converter.GenericConverter
核心要素 | 说明 |
---|---|
使用场景 | 用于“复合”类型转换场景,比如 Collection、Map、数组等 |
转换范围 | Set<ConvertiblePair> getConvertibleTypes() |
配对类型 | org.springframework.core.convert.converter.GenericConverter.ConvertiblePair |
转换方法 | convert(Object, TypeDescriptor, TypeDescriptor) |
类型描述 | org.springframework.core.convert.TypeDescriptor |
15.11 优化 GenericConverter 接口
GenericConverter 局限性
-
缺少 SourceType 和 TargetType 前值判断
-
单一类型转换实现复杂
GenericConverter 优化接口——ConditionalGenericConverter
-
复合类型转换:org.springframework.core.convert.converter.GenericConverter
-
类型条件判断:org.springframework.core.convert.converter.ConditionalConverter
15.12 扩展 Spring 类型转换器
实现转换器接口
-
org.springframework.core.converter.Converter
-
org.springframework.core.converter.ConverterFactory
-
org.springframework.core.converter.GenericConverter
注册转换器实现
-
通过 ConversionServiceFactoryBean Spring Bean
-
通过 org.springframework.core.converter.ConvertsionService API
15.13 统一类型转换服务
org.springframework.core.converter.ConversionService
实现类型 | 说明 |
---|---|
GenericConversionService | 通用 ConversionService 模板实现,不内置转换器实现 |
DefaultConversionService | 基础 ConversionService 实现,内置常用转化器实现 |
FormattingConversionService | 通用 Formatter + GenericConversionService 实现,不内置转化器和 Formatter 实现 |
DefaultFormattingConversionService | DefaultConversionService + 格式化实现(如:JSR-354 Money & Currency,JSR-310 Date-Time) |
15.14 ConversionService 作为依赖
类型转换器底层接口——org.springframework.beans.TypeConverter
-
起始版本:Spring 2.0
-
核心方法——convertIfNecessary
-
抽象实现——org.springframework.beans.TypeConverterSupport
-
简单实现——org.springframework.beans.SimpleTypeConverter
类型转换器底层抽象实现——org.springframework.beans.TypeConverterSupport
-
实现接口——org.springframework.beans.TypeConverter
-
扩展实现——org.springframework.beans.PropertyEditorRegistrySupport
-
委派实现——org.springframework.beans.TypeConverterDelegate
类型转换器底层委派实现——org.springframework.beans.TypeConverterDelegate
-
构造来源——org.springframework.beans.AbstractNestablePropertyAccessor 实现
-
org.springframework.beans.BeanWrapperImpl
-
-
依赖——java.beans.PropertyEditor
-
默认内建实现——PropertyEditorRegistrySupport#registerDefaultEditor
-
-
可选依赖——org.springframework.core.convert.ConversionService
AbstractApplicationContext -> "conversionService" ConversionService Bean -> ConfigurableBeanFactory$setConversionService(ConversionService) -> AbstractAutowireCapableBeanFactory.instantiateBean -> BeanDefinition -> BeanWrapper -> 属性转换(数据来源:PropertyValues)-> setPropertyValues(PropertyValue) -> TypeConverter#convertIfNecessary -> TypeConverterDelegate#convertIfNecessary -> PropertyEditor or ConversionService
15.15 面试题
15.15.1 Spring 类型转换实现有哪些
-
基于 JavaBeans PropertyEditor 接口实现
-
Spring 3.0+ 通用类型转换实现
15.15.2 Spring 类型转换器接口有哪些
-
类型转换接口——org.springframework.core.converter.Converter
-
通用类型转换接口——org.springframework.core.converter.GenericConverter
-
类型条件接口——org.springframework.core.converter.ConditionalConverter
-
综合类型转换接口——org.springframework.core.converter.ConditionalGenericConverter
15.15.3 TypeDescriptor 是如何处理泛型的?
下章揭晓
16. Spring 泛型处理
16.1 Java 泛型基础
-
泛型类型
-
泛型类型是在类型上参数化的泛型类或接口
-
-
泛型使用场景
-
编译时强类型检查
-
避免类型强转
-
实现通用算法
-
-
泛型类型擦写
-
反省被引入到 Java 中,以便在编译时提供更严格的类型检查并支持泛型编程。类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。为了实现泛型,编译器将类型擦除应用于:
-
将泛型类型中的所有类型参数替换为其边界,如果类型参数是无边界的,则将其替换为"Object"。因此,生成的字节码只包含普通类、接口和方法
-
必要时插入类型转换以保持类型安全
-
生成桥方法以保留扩展泛型中的多态性
-
-
16.2 Java 5 类型接口
派生类或接口 | 说明 |
---|---|
java.lang.Calss | Java 类 API,如 java.lang.String |
java.lang.reflect.GenericArrayType | 泛型数组类型 |
java.lang.reflect.ParameterizedType | 泛型参数类型 |
java.lang.reflect.TypeVariable | 泛型类型变量,如 Collection<E> 中的 E |
java.lang.reflect.WildcardType | 泛型通配类型 |
类型 | API |
---|---|
泛型信息(Generics Info) | java.lang.Class#getGenericInfo() |
泛型参数(Parameters) | java.lang.reflect.ParameterizedTpe |
泛型父类(Super Classes) | Java.lang.Class#getGenericSuperClass() |
泛型接口(interfaces) | Java.lang.Class#getGenericInterface() |
泛型声明(Generics Declaration) | java.lang.reflectGenericDeclaration |
16.3 Spring 泛型类型辅助类
核心 API——org.springframework.core.GenericTypeResolver
-
版本支持:[2.5.2,)
-
处理类型相关(Type)相关方法
-
resolveReturnType
-
resolveType
-
-
处理泛型参数类型(ParameterizedType)相关方法
-
resolveReturnTypeArgument
-
resolveTypeArgument
-
resolveTypeArguments
-
-
处理泛型类型变量(TypeVariable)相关方法
-
getTypeVariableMap
-
16.4 Spring 泛型集合类型辅助类
核心 API——org.springframework.core.GenericCollectionTypeResolver
-
版本支持:[2.0, 4.3]
-
替换实现:org.springframework.core.ResolvableType
-
处理 Collection 相关
-
getCollection*Type
-
-
处理 Map 相关
-
getMapKey*Type
-
getMapValue*Type
-
16.5 Spring方法参数封装——MrthodParameter
核心 API——org.springframework.core.MethodParameter
-
起始版本:[2.0,)
-
元信息
-
关联的方法——Method
-
关联的构造器——Constructor
-
构造器或方法参数索引——parameterIndex
-
构造器或方法参数类型——parameterType
-
构造器或方法参数泛型类型——genericParameterType
-
构造器或方法参数参数名称——parameterName
-
所在类——containingClass
-
16.6 Spring 4.2 泛型优化实现——ResolvableType
核心 API——org.springframework.core.ResolvableType
-
起始版本:[4.0,)
-
扮演角色:GenericTypeResolver 和 GenericCollectionTypeResolver 替代者
-
工厂方法:for* 方法
-
转换方法:as* 方法
-
处理方法:resolve* 方法
16.7 ResolvableType 的局限性
16.8 面试题
16.8.1 Java 泛型擦除发生在编译时还是运行时
运行时
16.8.2 请介绍 Java 5 Type 类型的派生类或接口
java.lang.Calss——Java 类 API,如 java.lang.String
java.lang.reflect.GenericArrayType——泛型数组类型
java.lang.reflect.ParameterizedType——泛型参数类型
java.lang.reflect.TypeVariable——泛型类型变量,如 Collection<E>
中的 E
java.lang.reflect.WildcardType——泛型通配类型
16.8.3 请说明 ResolvableType 的设计优势
-
简化 Java 5 Type API 开发,屏蔽复杂 API 的运用,如 ParameterizedType
-
不变性设计
-
Fluent API 设计(Builder 模式),链式(流式)编程
17. Spring 事件
17.1 Java 事件/监听器编程模型
-
设计模式——观察者模式扩展
-
可观察对象(消息发送者)——java.util.Observable
-
观察者——java.util.Observer
-
-
标准化接口
-
事件对象——java.util.EventObject
-
事件监听器——java.util.EventListener
-
public class ObserverDemo { public static void main(String[] args) { EventObservable observable = new EventObservable(); EventObserver observer = new EventObserver(); observable.addObserver(observer); observable.notifyObservers("Event"); } static class EventObservable extends Observable { @Override public void notifyObservers(Object arg) { setChanged(); super.notifyObservers(new EventObject(arg)); clearChanged(); } } static class EventObserver implements Observer, EventListener { @Override public void update(Observable o, Object arg) { EventObject event = (EventObject) arg; System.out.println("receive msg: " + event); } } }
17.2 面向接口的事件
一般事件系统中事件需要继承 EventObject 类,监听器接口需要继承 EventListener 接口
17.3 面向注解的事件
17.4 Spring 标准事件——ApplicationEvent
Spring 应用上下文 ApplicationEvent 扩展——ApplicationContextEvent
-
Spring 应用上下文(ApplicationContext)作为事件源
-
具体实现:
-
org.springframework.context.event.ContextClosedEvent
-
org.springframework.context.event.ContextRefreshedEvent
-
org.springframework.context.event.ContextStartedEvent
-
org.springframework.context.event.ContextStoppedEvent
-
继承关系:ContextRefreshedEvent 继承 ApplicationContextEvent 继承 ApplicationEvent 继承 ObjectEvent
其中 ApplicationEvent 新增 timestamp 属性记录时间
ApplicationContextEvent 则将 ApplicationContext 作为事件源,方便 Listener 中和 ApplicationContext 做交互
public abstract class ApplicationContextEvent extends ApplicationEvent { /** * Create a new ContextStartedEvent. * @param source the {@code ApplicationContext} that the event is raised for * (must not be {@code null}) */ public ApplicationContextEvent(ApplicationContext source) { super(source); } /** * Get the {@code ApplicationContext} that the event was raised for. */ public final ApplicationContext getApplicationContext() { return (ApplicationContext) getSource(); } }
17.5 基于接口的 Spring 事件监听器
Java 标准事件监听器 java.util.EventListener 扩展
-
扩展接口——org.springframework.context.ApplicationListener
-
设计特点:单一类型事件处理
-
处理方法:onApplicationEvent(ApplicationEvent)
-
事件类型:org.springframework.context.ApplicationEvent
public class ApplicationListenerDemo { public static void main(String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.addApplicationListener(e -> { if (e instanceof ContextRefreshedEvent) { System.out.println("refresh"); } else if (e instanceof ContextClosedEvent) { System.out.println("close"); } }); context.refresh(); context.close(); } }
17.6 基于注解的 Spring 事件监听器
Spring 注解 @org.springframework.context.event.EventListener
特性 | 说明 |
---|---|
设计特点 | 支持多 ApplicationEvent 类型,无需接口约束 |
注解目标 | 方法 |
是否支持异步执行 | 支持 |
是否支持泛型类型事件 | 支持 |
是否支持顺序控制 | 支持,配合 @Order 注解控制 |
@EnableAsync public class AnnotationApplicationListenerDemo { @EventListener @Order(2) public void listener(ContextRefreshedEvent event) { println("refresh"); } @EventListener @Order(1) public void listener1(ContextRefreshedEvent event) { println("refresh1"); } @EventListener @Async public void listener(ContextStartedEvent event) { println("start"); } @EventListener public void listener(ContextClosedEvent event) { println("close"); } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(AnnotationApplicationListenerDemo.class); context.refresh(); context.start(); context.close(); } private static void println(String msg) { System.out.printf("[%s] handle %s%n", Thread.currentThread().getName(), msg); } } // 运行结果 [main] handle refresh1 [main] handle refresh 2月 19, 2022 9:15:42 下午 org.springframework.aop.interceptor.AsyncExecutionAspectSupport getDefaultExecutor 信息: No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either [SimpleAsyncTaskExecutor-1] handle start [main] handle close
17.7 注册 Spring ApplicationListener
-
ApplicationListener 作为 Spring Bean 注册
-
通过 ConfigurableApplicationContext API 注册
public class SpringBeanApplicationListener { @Bean public ApplicationListener<ContextRefreshedEvent> myListener() { return new MyApplicationListener(); } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(SpringBeanApplicationListener.class); context.refresh(); context.close(); } static class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { System.out.println("my refresh"); } } }
17.8 Spring 事件发布器
-
方法一:通过 ApplicationEventPublisher 发布 Spring 事件
-
获取 ApplicationEventPublisher(依赖注入)
-
-
方法二:通过 ApplicationEventMulticaster 发布 Spring 事件
-
获取 ApplicationEventMuiltcaster
-
依赖注入
-
依赖查找
-
-
public class ApplicationPublisherDemo implements ApplicationEventPublisherAware { @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { applicationEventPublisher.publishEvent(new ApplicationEvent("hello world") { }); applicationEventPublisher.publishEvent("generic object"); } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.addApplicationListener(System.out::println); context.register(ApplicationPublisherDemo.class); context.refresh(); context.start(); context.close(); } }
17.9 Spring 层次性上下文事件传播
当 Spring 应用出现多层次 Spring 应用上下文(ApplicationContext)时,如 Spring WebMVC、Spring Boot 或 Spring Cloud 场景下,由子 ApplicationContext 发起的 Spring 事件可能会传递到其 Parent ApplicationContext(直到 Root)的过程
应该如何避免事件传播呢?可通过定位 Spring 事件源(ApllicationEvent)进行过滤处理,可通过缓存已处理事件来解决
public class HierarchicalEventDemo { public static void main(String[] args) { // create parent context AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(); parent.setId("parent"); // create sub context AnnotationConfigApplicationContext current = new AnnotationConfigApplicationContext(); current.setId("current"); current.setParent(parent); parent.register(MyListener.class); current.register(MyListener.class); parent.refresh(); current.refresh(); current.close(); parent.close(); } } // 运行结果: receive ContextRefreshedEvent event from id [parent] receive ContextRefreshedEvent event from id [current] receive ContextClosedEvent event from id [current] receive ContextClosedEvent event from id [parent]
解决了事件重复被消费的问题
17.10 Spring 内建事件
ApplicationContextEvent 派生事件
-
ContextRefreashEvent:Spring 应用上下文就绪事件——context#refreash
-
ContextStartedEvent:Spring 应用上下文启动事件——context#start
-
ContextStopedEvent:Spring 应用上下文停止事件——context#stop
-
ContextClosedEvent:Spring 应用上下文关闭事件——context#close
17.11 Spring 4.2 Payload 事件
Spring Payload 事件——org.springframework.context.PayloadApplicationEvent
-
使用场景:简化 Spring 事件发送,关注事件源主体
-
发送方法:ApplicationEventPublisher#publishEvent(java.lang.Object)
17.12 自定义 Spring 事件
-
扩展 org.springframework.context.ApplicationEvent
-
实现 org.springframework.context.ApplicationListener
-
注册 org.springframework.context.ApplicationListener
17.13 依赖注入 ApplicationEventPublisher
-
通过 ApplicationEventPublisherAware 回调接口
-
通过 @Autowired ApplicationEventPublisher
17.14 依赖查找 ApplicationEventPublisher
查找条件:
-
Bean 名称:"applicationEventMulticaster"
-
Bean 类型:"org.springframework.context.event.ApplicationEventMulticaster"
17.15 ApplicationEventPublisher 底层实现
底层实现:
-
接口:org.springframework.context.event.ApplicationEventMulticaster
-
抽象类:org.springframework.context.event.AbstractApplicationEventMulticaster
-
实现类:org.springframework.context.event.SimpleApplicationEventMulticaster
ApplicationContext 继承了 ApplicationEventPublisher 接口,在 AbstractApplicationContext 内部持有一个 ApplicationEventMulticaster 对象,因此 ApplicationContext 的发布事件的功能底层是委托给 ApplicationEventMulticaster 对象来实现的
protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); // Decorate event as an ApplicationEvent if necessary ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; } else { applicationEvent = new PayloadApplicationEvent<>(this, event); if (eventType == null) { eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType(); } } // Multicast right now if possible - or lazily once the multicaster is initialized if (this.earlyApplicationEvents != null) { this.earlyApplicationEvents.add(applicationEvent); } else { getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // Publish event via parent context as well... if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } }
17.16 同步和异步 Spring 事件广播
-
事件广播基于 org.springframework.context.event.SimpleApplicationEventMulticaster 类(全局影响)
-
模式切换:setTaskExcutor(java.util.concurrent.Excutor) 方法
-
默认模式:同步
-
异步模式:如 java.util.concurrentThreadPoolExecutor
-
-
设计缺陷:非基于接口契约编程
-
-
基于注解——@org.springframework.context.event.EventListener(局部影响)
-
模式切换
-
默认模式:同步
-
异步模式:标注 @org.springframework.scheduling.annotation.Async
-
-
实现限制:无法直接实现同步/异步动态切换
-
17.17 Spring 4.1 事件异常处理
Spring 3.0 错误处理接口——org.springframework.util.ErrorHandler
-
使用场景
-
Spring 事件(Events)
-
SimpleApplicationEventMulticaster Spring 4.1 开始支持
-
-
Spring 本地调度(Scheduling)
-
org.springframework.scheduling.concurrent.ConcurrentTaskScheduler
-
org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
-
-
17.18 Spring 事件/监听器实现原理
核心类——org.springframework.context.event.SimpleApplicationEventMulticaster
-
设计模式:观察者模式扩展
-
被观察者——org.springframework.context.ApplicationListener
-
API 添加
-
依赖查找
-
-
通知对象——org.springframework.context.ApplicationEvent
-
-
执行模式:同步/异步
-
异常处理:org.springframework.util.ErrorHandler
-
泛型处理:org.springframework.core.ResolvableType
17.19 课外资料
Spring Boot 事件
事件类型 | 发生时机 |
---|---|
ApplicationStartingEvent | 当 Spring Boot 应用启动时 |
ApplicaitonStartedEvent | 当 Spring Boot 应用已启动时 |
ApplicationEnvironmentPreparedEvent | 当 Spring Boot Environment 实例已准备时 |
ApplicationPreparedEvent | 当 Spring Boot 应用预备时 |
ApplicationReadyEvent | 当 Spring Boot 应用完全可用时 |
ApplicationFailedEvent | 当 Spring Boot 应用启动失败时 |
Spring Cloud 事件
事件类型 | 发生时机 |
---|---|
EnvironmentChangeEvent | 当 Environment 示例配置属性发生变化时 |
HeartbeatEvent | 当 DiscoveryClient 客户端发送心跳时 |
InstancePreRegisteredEvent | 当服务实例注册前 |
InstanceRegisteredEvent | 当服务实例注册后 |
RefreshEvent | 当 RefreshEndpoint 被调用时 |
RefreshScopeRefreshedEvent | 当 Refresh Scope Bean 刷新后 |
17.20 面试题
17.20.1 Spring 事件核心接口/组件
-
Spring 事件——org.springframework.context.ApplicationEvent
-
Spring 事件监听器——org.springframework.context.ApplicationListener
-
Spring 事件发布器——org.springframework.context.ApplicationEventPublisher
-
Spring 事件广播器——org.springframework.context.event.ApplicationEventMulticaster
17.20.2 Spring 同步和异步事件处理的使用场景
-
Spring 同步事件——绝大多数 Spring 使用场景,如 ContextRefreshedEvent
-
Spring 异步事件——主要 @EventListener 与 @Async 配合,实现异步处理,不阻塞主线程,比如长时间的数据计算任务等,不要轻易调整 SimpleApplicationEventMulticaster 中关联的 taskExecutor 对象,除非使用者非常了解 Spring 的事件机制,否则容易出现异常行为
17.20.3 @EventListener 的工作原理
18. Spring 注解
18.1 Spring 注解驱动编程发展历程
-
注解驱动启蒙时代:Spring Framework 1.x
-
注解驱动过度时代:Spring Framework 2.x
-
注解驱动黄静时代:Spring Framework 3.x
-
注解驱动完善时代:Spring Framework 4.x
-
注解驱动当下时代:Spring Framework 5.x
18.2 Spring 核心注解场景分类
Spring 模式注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@Repository | 数据仓储模式注解 | 2.0 |
@Component | 通用组件模式注解 | 2.5 |
@Service | 服务模式注解 | 2.5 |
@Controller | Web 控制器模式注解 | 2.5 |
@Configuration | 配置类模式注解 | 3.0 |
装配注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@ImportResource | 替换 XML 元素 <import> | 2.5 |
@Import | 导入 Configuration 类 | 2.5 |
@ComponentScan | 扫描指定 package 下标注 Spring 模式注解的类 | 3.1 |
依赖注入注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@Autowired | Bean 依赖注入,支持多种依赖查找方式 | 2.5 |
@Qulifier | 细粒度的 @Autowired 依赖查找 | 2.5 |
18.3 Spring 注解编程模型
编程模型:
-
元注解(Meta-Annotation)
-
Spring 模式注解(Stereotype Annotation)
-
Spring 组合注解(Composed Annotations)
-
Spring 注解属性别名和覆盖(Attribute Alias and Overrides)
18.4 Spring 元注解
元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。 Java 5 定义了4个注解,分别是 @Documented、@Target、@Retention 和 @Inherited。 Java 8 又增加了 @Repeatable 和 @Native 两个注解。
18.5 Spring 模式注解
理解 @Component "派生性":原标注 @Component 的注解在 XML 元素 <context:component-scan>
或注解 @ComponentScan 扫描中 "派生"了 @Component 的特性,并且从 Spring Framework 4.0 开始支持多层次性"派生性"。
举例说明:
-
@Repository
-
@Service
-
@Controller
-
@Configuration
-
@SpringBootConfiguration(SpringBoot)
@Component 派生性原理
-
核心组件——orgspringframework.context.annotation.ClassPathBeanDefinitionScanner
-
orgspringframework.context.annotation.ClassPathScanningCandidateComponentProvider
-
-
资源处理——org.springframework.core.io.support.ResourcePatternResolver
-
资源-类元信息
-
org.springframework.core.type.classreading.MetadataReaderFactory
-
-
类元信息——orgspringframework.core.type.ClassMetadata
-
ASM 实现——org.springframework.core.type.classreading.ClassMetadataReadingVisitor
-
反射实现——org.springframework.core.type.StandardAnnotationMetadata
-
-
注解元信息——orgspringframework.core.type.AnnotationMetadata
-
ASM 实现——org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor
-
反射实现——org.springframework.core.type.StandardAnnotationMetadata
-
18.6 Spring 组合注解
Spring 组合注解(Component Annotations) 中的元标注允许是 Spring 模式注解(Stereotype Annotation)与其他 Spring 功能性注解的任意组合。
18.7 Spring 注解属性别名
显性别名、隐形别名、传递性别名
18.8 Spring 注解属性覆盖
18.9 Spring @Enable 模块驱动
@Enable 模块驱动:@Enable 模块驱动是以 @Enable 为前缀的注解驱动编程模型。所谓"模块"是指具备相同领域的功能组件集合,组合所形成一个独立的单元。比如 Web MVC 模块、AspectJ 代理模块、Caching(缓存)模块、JMX(Java 管理扩展)模块、Async(异步处理)模块等。
举例说明:
-
@EnableWebMvc
-
@EnableTransactionManagement
-
@EnableCaching
-
@EnableMBeanExport
-
@EnableAsync
@Enable 模块驱动编程模式
-
驱动注解:@EnableXXX
-
导入注解:@Import 具体实现
-
具体实现:
-
基于 Configuration Class
-
基于 ImportSelector 接口实现
-
基于 ImportBeanDefinitionRegistrar 接口实现
-
18.10 Spring 条件注解
基于配置条件注解——@org.springframewok.context.annotation.Profile
-
关联对象——org.springframework.core.Environment 中的 Profiles
-
实现变化:从 Spring 4.0 开始,@Profile 基于 @Confitional 实现
基于变成条件注解——@org.springframework.context.annotation.Conditional
-
关联对象——org.springframewok.context.annotation.Condition 具体实现
@Conditional 实现原理:
-
上下文对象——org.springframewok.context.annotation.ConditionalContext
-
条件判断——org.springframewok.context.annotation.ConditionEvaluator
-
配置阶段——org.springframewok.context.annotation.ConfigurationCondition.ConfigurationPhase
-
判断入口——org.springframewok.context.annotation.ConfigurationClassPostProcessor
-
org.springframewok.context.annotation.ConfigurationClassParser
-
18.11 课外资料
Spring Boot 注解
注解 | 场景说明 | 起始版本 |
---|---|---|
@SpringBootConfiguration | Spring Boot 配置类 | 1.4.0 |
@SpringBootApplication | Spring Boot 应用引导注解 | 1.2.0 |
@EnableAutoConfigutaion | Sppring Boot 激活自动转配 | 1.0.0 |
Spring Cloud 注解
注解 | 场景说明 | 起始版本 |
---|---|---|
@SpringCloudApplication | Spring Cloud 应用引导注解 | 1.0.0 |
@EnableDiscoveryClient | Spring Cloud 激活服务发现客户端注解 | 1.0.0 |
@EnableCircuitBreaker | Spring Cloud 激活熔断注解 | 1.0.0 |
18.12 面试题
18.12.1 Spring 模式注解有哪些
-
@org.springframework.stereotype.Component
-
@org.springframework.stereotype.Repository
-
@org.springframework.stereotype.Service
-
@org.springframework.stereotype.Controller
-
@org.springframework.stereotype.Configuration
18.12.2 @EventListener 的工作原理
18.12.3 @PropertySource 的工作原理
下章揭晓
19. Spring Environment 抽象
19.1 理解 Spring Environment 抽象
-
统一的 Spring 配置属性管理
Spring Framework 3.1 开始引入 Environment 抽象,它统一 Spring 配置属性的存储,包括占位符处理和类型转换,不仅完整地替换 PropertyPlaceholderConfigurer,而且还支持更丰富的配置属性源(PropertySource)
-
条件化 Spring Bean 装配管理
通过 Environment Profiles 信息,帮助 Spring 容器提供条件话地装配 Bean
19.2 Spring Environment 接口使用场景
-
用于属性占位符处理
-
用于转换 Spring 配置属性类型
-
用于存储 Spring 配置属性源(PropertySource)
-
用于 Profiles 状态维护
19.3 Environment 占位符处理
Spring 3.1 前占位符处理
-
组件:org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
-
接口:org.springframework.util.StringValueResolver
Spring 3.1 后占位符处理
-
组件:org.springframework.context.support.PropertySourcesPlaceholderConfigurer
-
接口:org.springframework.beans.factory.config.EmbeddedValueResolver
19.4 理解条件配置 Spring Profiles
Spring 3.1 条件配置
-
API:org.springframework.core.env.ConfigurableEnvironment
-
修改:addActiveProfile(String)、setActiveProfile(String ...) 和 setDefaultProfile(String ...)
-
获取:getActiveProfiles() 和 getDefaultProfiles()
-
匹配:$acceptsProfiles(String ...) 和 acceptsProfiles(Profiles)
-
-
注解:@org.springframework.context.annotation.Profile
可以通过 -Dspring.profiles.active 来指定 activeProfile
19.5 Spring 4 重构 @Profile
基于 Spring 4 org.springframework.context.annotation.Condition 接口实现
-
org.springframework.context.annotation.ProfileCondition
19.6 依赖注入 Environment
直接依赖注入
-
通过 EnvironmentAware 接口回调
-
通过 @Autowired 注入 Environment
间接依赖注入
-
通过 ApplicationContextAware 接口回调
-
通过 @Autowired 注入 ApplicationContext
19.7 依赖查找 Environment
直接依赖查找
-
通过 org.springframework.context.ConfigurableApplicationContext#ENVIRONMENT_BEAN_NAME
间接依赖查找
-
通过 org.springframework.context.ConfigurableApplicationContext#getEnvironment
19.8 依赖注入 @Value
通过注入 @Value
-
实现——org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
19.9 Spring 类型转换在 Environment 中的运用
Environment 底层实现
-
底层实现——org.springframework.core.env.PropertySourcesPropertyResolver
-
核心方法——convertValueIfNecessary(Object, Class)
-
-
底层服务——org.springframework.core.convert.ConversionService
-
默认实现——org.springframework.core.convert.support.DefaultConversionService
-
19.10 Spring 类型转换在 @Value 中的运用
@Value底层实现
-
底层实现——org.springframework.beans.factory.annotation.AutoworedAnnotationBeanPostProcessor
-
org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
-
-
底层服务——org.springframework.beans.TypeConverter
-
默认实现——org.springframework.beans.TypeConverterDelegate
-
java.beans.PropertyEditor
-
org.springframework.core.convert.ConversionService
-
-
19.11 Spring 配置属性源 PropertySource
API
-
单配置属性源——org.springframework.core.env.PropertySource
-
多配置属性源——org.springframework.core.env.PropertySources
注解
-
单配置属性源——@org.springframework.context.annotation.PropertySource
-
多配置属性源——@org.springframework.context.annotation.PropertySources
关联
-
存储对象——org.springframework.core.env.MutablePropertySources
-
关联方法——org.springframework.core.env.ConfigurableEnvironment#getPropertySources()
19.12 Spring 内建的配置属性源
内建 PropertySource
PropertySource 类型 | 说明 |
---|---|
org.springframework.core.env.CommandLinePropertySource | 命令行配置属性源 |
org.springframework.jndi.JndiPropertySource | JNDI 配置属性源 |
org.springframework.core.env.PropertySource | Properties 配置属性源 |
org.springframework.web.context.support.ServletConfigPropertySource | Servlet配置属性源 |
org.springframework.web.context.support.ServletContextPropertySource | ServletContext 配置属性源 |
org.springframework.core.env.SystemEnvironmentPropertySource | 环境变量配置属性源 |
…… |
19.13 基于注解扩展 Spring 配置属性源
@org.springframework.context.annotation.PropertySource 实现原理
-
入口——org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
-
org.springframework.context.annotation.ConfigurationClassParser#processPropertySource
-
-
4.3 新增语义
-
适配属性字符编码——encoding
-
org.springframework.core.io.support.PropertySourceFactory
-
-
适配对象——org.springframework.core.env.CompositePropertySource
19.14 基于 API 扩展 Spring 配置属性源
-
Spring 应用上下文启动前装配 PropertySource
-
Spring 应用上下文启动后装配 PropertySource
19.15 课外资料
Spring 4.1 测试配置属性源——@TestPropertySource(优先级高于@PropertySource)
19.16 面试题
19.16.1 简单介绍 Spring Environment 接口
-
核心接口——org.springframework.core.env.Environment
-
父接口——org.springframework.core.env.PropertyResolver
-
可配置接口——org.springframework.core.env.ConfigurabelRnvironment
-
职责:
-
guanli Spring 配置属性源
-
管理 Profiles
-
19.16.2 如何控制 PropertySource 优先级
可以通过 api addFirst、addLast 等进行控制
19.16.3 Environment 完整的生命周期是怎样的?
下章揭晓
20. Spring 应用上下文生命周期
20.1 Spring 应用上下文启动准备阶段
AbstractApplicationContext#prepareRefresh 方法
-
启动时间——startupDate
-
状态标识——closed(false)、active(true)
-
初始化 PropertySource——initPropertySources()(扩展,初始化 Environment 并且初始化 Environment 关联的 PropertySource)
-
检验 Environment 中必须的属性
-
初始化事件监听器集合
-
初始化早期 Spring 事件集合
20.2 BeanFactory 创建阶段
AbstractApplicationContext#obtainFreshBeanFactory 方法
-
刷新 Spring 应用上下文底层 BeanFactory——refreshBeanFactory
-
销毁或关闭 BeanFactory,如果已经存在的话
-
创建 BeanFactory——createBeanFactory
-
设置 BeanFactory ID
-
设置"是否允许 BeanDefinition 重复定义"——customizeBeanFactory(DefaultListableBeanFactory)
-
设置"是否允许循环引用(依赖)"——customizeBeanFactory(DefaultListableBeanFactory)
-
加载 BeanDefinition——loadBeanDefinitions(DefaultListableBeanFactory)
-
关联新建 BeanFactory 到 Spring 应用上下文
-
-
返回 Spring 应用上下文底层 BeanFactory——getBeanFactory
20.3 BeanFactory 准备阶段
AbstractApplicationContext#prepareBeanFactory(ConfigurableListableBeanFactory)
-
关联 ClassLoader
-
设置 Bean 表达式处理器
-
添加 PropertyEditorRegister 实现——ResourceEditorRegistrar
-
添加 Aware 回调接口 BeanPostProcessor 实现——ApplicationContextAwareProcessor
-
忽略 Aware 回调接口作为依赖注入接口
-
注册 ResolvableDependency 对象——BeanFactory、ResourceLoader、ApplicationEventPublisher 以及 ApplicationContext
-
注册 ApplicationListenerDetector 对象
-
注册 LoadTimeWeaverAwareProcessor 对象
-
注册单例对象——Environment、Java System Properites 以及 OS 环境变量
20.4 BeanFactory 后置处理阶段
AbstractApplicationContext#postProcessBeanFactory(ConfigurableListableBeanFactory)
-
子类覆盖该方法
AbstractApplicationContext#invokeBeanFactoryPostProcessor(ConfigurableListableBeanFactory)
-
调用 BeanFactoryPostProcessor 或 BeanDefinitionRegistry 后置处理方法
-
注册 LoadTimeWeaverAwareProcessor 对象
20.5 BeanFactory 注册 BeanPostProcessor 阶段
AbstractApplicationContext#registerBeanPostProcessors(ConfigurableListableBeanFactory)
-
注册 PriorityOrdered 类型的 BeanPostProcessor Beans
-
注册 Ordered 类型的 BeanPostProcessor Beans
-
注册普通 BeanPostProcessor Beans
-
注册 MergedBeanDefinitionPostProcessor Beans
-
注册 ApplicationListenerDetector 对象
20.6 初始化内建 Bean:MessageSource
AbstractApplicationContext#initMessageSource
20.7 初始化内建 Bean:Spring 事件广播器
AbstractApplicationContext#initApplicationEventMulticaster
20.8 Spring 应用上下文刷新阶段
AbstractApplicationContext#onRefresh(空实现,需要子类实现)
-
子类覆盖该方法
20.9 Spring 事件监听器注册阶段
AbstractApplicationContext#registerListeners
-
添加当前引用上下文所关联的 ApplicationListener 对象
-
添加 BeanFactory 所注册的 ApplicaitonListener Beans
-
广播早期 Spring 事件
20.10 BeanFactory 初始化完成阶段
AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory)
-
BeanFactory 关联 ConversionService Bean,如果存在
-
添加 StringValueResolver 对象
-
依赖查找 LoadTimeWeaverAware Bean
-
BeanFactory 临时 ClassLoader 置为 null
-
BeanFactory 冻结配置
-
BeanFactory 初始化非延迟单例 Beans
20.11 Spring 应用上下文刷新完成阶段
AbstractApplicationContext#finishRefreash
-
清除 ResourceLoader 缓存——clearResourceCaches
-
初始化 LifecycleProcessor 对象——initLifecycleProcessor
-
调用 LifecycleProcessor#onRefeash 方法
-
发布 Spring 应用上下文已刷新事件——ContextRefreashedEvent
-
向 MBeanServer 托管 Live Beans
20.12 Spring 应用上下文启动完成阶段
20.13 Spring 应用上下文启动阶段(start 方法)
AbstractApplicationContext#start
-
启动 LifecycleProcessor
-
依赖查找 Lifecycle Beans
-
启动 Lifecycle Beans
-
-
发布 Spring 应用上下文已启动事件——ContextStartedEvent
20.14 Spring 应用上下文停止阶段
AbstractApplicationContext#stop
-
停止 LifecycleProcessor
-
依赖查找 Lifecycle Beans
-
停止 Lifecycle Beans
-
-
发布 Spring 应用上下文已启动事件——ContextStoppedEvent
20.15 Spring 应用上下文关闭阶段
AbstractApplicationContext#close
-
状态标识:active(false)、closed(true)
-
Live Beans JMX 撤销托管
-
LiveBeansView.unregisterApplicationContext(configurableApplicationContext)
-
-
发布 Spring 应用上下文已关闭事件——ContextClosedEvent
-
关闭 LifecycleProcessor
-
依赖查找 Lifecycle Beans
-
停止 Lifecycle Beans
-
-
销毁 Spring Beans
-
关闭 BeanFactory
-
回调 onClose
-
注册 Shutdown Hook 线程(如果曾注册)
20.16 面试题
20.16.1 Spring 应用上下文生命周期有哪些阶段
-
刷新阶段——ConfigurableApplicationContext#refresh
-
启动阶段——ConfigurableApplicationContext#start
-
停止阶段——ConfigurableApplicationContext#stop
-
关闭阶段——ConfigurableApplicationContext#close
20.16.2 Environment 完整的生命周期是怎样的?
20.16.3 Spring 应用上下文生命周期执行动作
21 加餐
21.1 为什么说 ObjectFactory 提供的是延迟依赖查找
-
原因
-
ObjectFactory(或ObjectProvider)可关联某一类型 Bean
-
ObejctFactory 和 ObjectProvider 对象在被依赖注入和依赖查询时并未试试查找关联类型的 Bean
-
当 ObjectFactory(或 ObjectProvider)调用 getObject 方法时,目标 Bean 才被依赖查找
-
-
总结
-
ObjectFactory(或 ObjectProvider)相当于某一类型 Bean 依赖查找对象
-
21.2 依赖查找(注入)的 Bean 会被缓存吗?
-
单例 Bean(Singleton)——会缓存
-
缓存位置:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#singletonObjects
-
-
原型 Bean(Prototype)——不会缓存
-
当以来查询或依赖注入时,根据 BeanDefinition 每次创建
-
-
其他 Scope Bean
-
request:每个 ServletRequest 内部缓存,生命周期维持在每次 HTTP 请求
-
session:每个 HttpSession 内部缓存,生命周期维持在每个用户 HTTP 会话
-
application:当前 Servlet 应用内部缓存
-
21.3 @Bean 的处理流程是怎样的?
-
解析范围——Configuration Class 中的 @Bean 方法
-
方法类型——静态 @Bean 方法和实例 @Bean 方法
21.4 BeanFactory 如何处理循环依赖?
-
预备知识
-
循环依赖开关(方法)——AbstractAutowireCapableBeanFactory#setAllowCirclarReferences
-
单例工厂(属性)——DefaultSingletonBeanRegistry#singletonFactories
-
获取早期未处理 Bean (方法)——AbstractAutowireCapableBeanFactory#getEarlyBeanReference
-
早期未处理 Bean (属性)——DefaultSingletonBeanRegistry#earlySingletonObjects
-
-
案例分析
三个 Map:singletonObject、singletonFactories、earlySingletonObjects
21.5 MyBatis 与 Spring Feamework 是如何集成的?
AbstractAutowiredCapableBeanFactory:populateBean
DefaultListableBeanFacotry:doResolveDependency(解析占位符)