1 Spring整合Java 核心API
Java语言的变化
2004年:jdk5提供的枚举类、泛型、注解、拆箱和装箱
2006年:6提供的@Override接口
2011年:钻石语法、多个catch、try
2014年:lambda语法、可重复注解、类型注解
2017年:模块化、接口私有方法
2018年:局部变量类型推断
7提供的钻石语法、Spring支持5.0+,代表实现DefaultListableBeanFactory
try-with-resource语法、Spring支持5.9、ResourceBundle
8提供的Lambda语法
JDK核心API的部分
5之前的版本:反射、JavaBeans、动态代理
5的版本:J.U.C、Formater、Java管理扩展(JMX)、Instrumentation、XML处理
6的版本:JDBC4.0、JAXB2.0、可插拔的注解处理API
7的版本:NIO2、Fork/Join框架、invokedynamic字节码
8的版本:Stream API、CompletableFuture、Annotation on Java Types、Date and Time API、可重复Annotations、JavaScirpt运行时。
9的版本:Reactive Streams Flow API、varable Handlers、Method Handles、spi-wait Hints、stack-walking
注解的实现两种方式:第一就是ASM、第二就是Java的反射。都是在Java运行时进行实现
@Indexed实现可插拔的注解处理API
2 Spring对JavaEE API整合
3 Spring实现的五种编程模型
- 面向对象编程(契约接口)
契约接口
- Aware接口,它是一种模式
子接口有ApplicationContextAware,通过set接口把对应的前缀的类传递过来
beanFactoryAware也是一样的接口 - BeanPostProcessor
设计模式
观察者模式:ApplicationListener接口中onApplicatinEvent通过事件的方式,实现广播
组合模式:Composite…类
模版模式:如JdbcTemplate,
大量的Abstract*
- 面向切面编程(动态代理和字节码提升)
- 动态代理的实现JdkDynamicAopProxy、AopProxy接口
- 面向元编程(配置元信息、注解、)
注解:模式注解(@Component、@Service、@Respository)
配置:Enviroment抽象、PropertySources、BeanDefinition
泛型:GenericTypeResolve、ResovleType(简化泛型处理) - 函数驱动:(lambda和Reactive)
ApplicatinEvent
FunctionalIntegerface
Reactive(Mono和Flux)、Spring WebFulx - 模块驱动
Maven Artifacts、
Spring的@Enable*
4 Spring的核心价值(比较重要)
- API抽象设计:AOP抽象、事务抽象、Environment抽象、生命周期
- 编程模型(看上面):OOP、面向切面、面向元编程、面向模块
- 设计思想:OOP、IOC/ID、DDD(领域驱动设计)、TDD(测试驱动)、EDP(事件驱动)、FP(函数驱动)
- 设计模式:专属模式+GOF23
专属模式包含前缀模式(Enable模式+Configurable模式),后缀模式(处理器模式(Processor+Resolve+Handler)、意识模式(Aware)、配置器模式(Configuror)、选择器模式(importSelector))
5 Spring面试题目
- SpringFramework的核心模块?
答:Spring Core基础API模块、如资源管理、泛型处理
Spring bean:Spring Bean相关,如依赖查找、依赖注入
Spring Aop:Spring AOP处理,如动态代理、AOP字节码提升
Spring Context:事件驱动(ApplicationEvent)、注解驱动、模块驱动
Spring expression:Spring表达式语言模块 - Spring Framework的优势和不足是什么?
6 Spring IoC(inversion of control)
知识点:IoC发展、IoC主要实现策略、IoC容器的职责、IoC容器的实现、传统IoC容器实现、轻量级IoC容器、依赖查找VS.依赖注入
构造器注入 VS. Setter注入
6.1 IoC主要实现策略(依赖查找和依赖注入)
- 使用service locator pattern
**2. 依赖注入:构造器注入、属性注入、setter方法注入、 - 上下文查找contextualized lookup,**
- 使用模版方法模式
- 使用策略模式
6.2 IoC容器的职责
依赖处理:依赖查找和依赖注入。依赖怎么来的?怎么把它进行返回给客户端
生命周期:容器、托管的资源(bean或者其他资源比如事件)
配置:容器、外部化配置(属性配置等)、托管的资源(bean或者其他资源比如线程池)
6.3 IoC容器的实现
JavaSE:Java beans、Java ServiceLader SPI、JNDI(Java命名目录接口)
Java EE:EJB、Servlet
开源:Apache Avalon(依赖查找)、PicoContainer()、Google Guice(依赖注入和依赖查找)、SpringFramework
6.4 传统的IoC容器的实现
Java Bean作为IoC容器特性:依赖查找、生命周期管理、配置元信息、事件、自定义、资源管理、持久化
依赖查找
🐴【代码实例6-4-1】
BeanInfo beanInfo = Introspector.getBeanInfo(Person.class); // 获取Java bean的信息
Stream.of(beanInfo.getPropertyDescriptors()).forEach(
propertyDescriptor -> {
System.out.println(propertyDescriptor);
}
);
// 输出
java.beans.PropertyDescriptor[name=age; values={expert=false; visualUpdate=false; hidden=false; enumerationValues=[Ljava.lang.Object;@29774679; required=false}; propertyType=class java.lang.Integer; readMethod=public java.lang.Integer myspring.Person.getAge(); writeMethod=public void myspring.Person.setAge(java.lang.Integer)]
java.beans.PropertyDescriptor[name=class; values={expert=false; visualUpdate=false; hidden=false; enumerationValues=[Ljava.lang.Object;@3ffc5af1; required=false}; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()]
java.beans.PropertyDescriptor[name=name; values={expert=false; visualUpdate=false; hidden=false; enumerationValues=[Ljava.lang.Object;@5e5792a0; required=false}; propertyType=class java.lang.String; readMethod=public java.lang.String myspring.Person.getName(); writeMethod=public void myspring.Person.setName(java.lang.String)]
🐴【代码实例6-4-2】
// 使用PropertyEditor实现属性类型转换
Person p = new Person();
Stream.of(beanInfo.getPropertyDescriptors()).forEach(
propertyDescriptor->{
// System.out.println(propertyDescriptor);
//PropertyDescriptor 运训添加属性编辑器 PropertyEditor
Class<?> propertyType = propertyDescriptor.getPropertyType();
String propertyName = propertyDescriptor.getName();
if("age".equals(propertyName)){
//把string -> Integer Integer.valueOf("")
propertyDescriptor.setPropertyEditorClass(StringToIntegerProEditor.class);
propertyDescriptor.createPropertyEditor(p);
}
}
);
static class StringToIntegerProEditor extends PropertyEditorSupport{
public void setAsText(String text) throws java.lang.IllegalArgumentException{
Integer value = Integer.valueOf(text);
setValue(value);
}
}
6.5 轻量级IoC容器
6.6 依赖查找 VS. 依赖注入
6.7 构造器注入 VS. Setter注入
SpringFrameWork对构造器注入于setter注入的论点:
- 提倡构造器注入,因为可以用一个不可变得对象传入构造器中,确保依赖不是null,其次构造器注入能够保证client调用是初始化了的对象。
- setter注入是一种可选的注入。@Autowire
setter注入的优点:支持JavaBean支持IDEs、JavaBean的属性是self-documenting。许多已经存在的JavaBeans能够在面向JavaBean的IoC容器中使用,不需要修改。
缺点:set的顺序不能确定,构造器的顺序是确定的。
org。springframework.beans.factory.InitializingBean接口保证了初始化调用任何一个初始化方法
6.8 面试题
- 什么是IoC
控制反转:好莱坞原则、依赖注入和依赖查找实现
Servlet也是一种IoC,可以通过外部依赖或者JNDI方式获取外部资源,包括DataSource或者EJB相关的组件。反向观察者模式也是一种IoC
- 依赖查找和依赖注入
依赖查找:它的大体思路是容器中的受控对象通过容器的 API 来查找自己所依赖的资源和协作对象。这种方式虽然降低了对象间的依赖,但是同时也使用到了容器的 API,造成了我们无法在容器外使用和测试对象。依赖查找是一种更加传统的 IOC 实现方式。
依赖注入:自动注入基于构造器、setter方法注入,非自动注入基于属性注入。
- Spring作为IoC容器有什么优势?
典型的IoC管理,依赖查找和依赖注入、AOP抽象、事务抽象、事件机制、SPI扩展、强大的第三方整合、容易测试性(Spring-test、spring-mock)、更好的面相对象
6.9 Spring IoC的依赖查找
实时查找和延时查找的区别:延时查找通过ObjectFactory获取bean,让applicatincontext加载完成,防止有些bean是null导致NPE问题。
Object Bean和FactoryBean的区别?
FactoryBean的使用案例:
FactoryBean只是一个接口,要创建工厂bean,你只需要把创建的bean类实现FactoryBean接口,该类将创建实际所需要的bean。简单起见,您可以扩展AbstractFactoryBean类。
根据Bean名称查找:实时查找和延时查找
根据Bean类型查找:单个Bean对象、集合Bean对象
根据Bean名称 + 类型查找
根据注解查找:单个Bean对象和集合Bean对象
🐴【代码实例6-9-1】
public static void main(String[] args) {
// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
// 按照类型查找
lookupByType(beanFactory);
// 按照类型查找结合对象
lookupCollectionByType(beanFactory);
// 通过注解查找对象
lookupByAnnotationType(beanFactory);
// 实时查找
lookupInRealTime(beanFactory);
// 延时查找
lookupInLazy(beanFactory);
}
private static void lookupByAnnotationType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class);
System.out.println("查找标注 @Super 所有的 User 集合对象:" + users);
}
}
private static void lookupCollectionByType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有的 User 集合对象:" + users);
}
}
private static void lookupByType(BeanFactory beanFactory) {
User user = beanFactory.getBean(User.class);
System.out.println("实时查找:" + user);
}
private static void lookupInLazy(BeanFactory beanFactory) {
ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");
User user = objectFactory.getObject();
System.out.println("延迟查找:" + user);
}
private static void lookupInRealTime(BeanFactory beanFactory) {
User user = (User) beanFactory.getBean("user");
System.out.println("实时查找:" + user);
}
6.10 Spring IoC依赖注入
util:list手动配置
自动配置:auto-wiring
根据Bean名称注根据Bean类型注入:单个Bean对象、集合Bean对象
注入容器内建Bean对象
注入非Bean对象
注入类型:实时注入和延迟注入
🐴【代码实例6-10-1】
// UserRepository
public class UserRepository {
private Collection<User> users; // 自定义 Bean
private BeanFactory beanFactory; // 內建非 Bean 对象(依赖)
private ObjectFactory<ApplicationContext> objectFactory;
public Collection<User> getUsers() {
return users;
}
public void setUsers(Collection<User> users) {
this.users = users;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public BeanFactory getBeanFactory() {
return beanFactory;
}
public ObjectFactory<ApplicationContext> getObjectFactory() {
return objectFactory;
}
public void setObjectFactory(ObjectFactory<ApplicationContext> objectFactory) {
this.objectFactory = objectFactory;
}
}
// 主类
public static void main(String[] args) {
// 配置 XML 配置文件
// 启动 Spring 应用上下文
// BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
// 依赖来源一:自定义 Bean
UserRepository userRepository = applicationContext.getBean("userRepository", UserRepository.class);
// System.out.println(userRepository.getUsers());
// 依赖来源二:依赖注入(內建依赖)
System.out.println(userRepository.getBeanFactory());
ObjectFactory userFactory = userRepository.getObjectFactory();
System.out.println(userFactory.getObject() == applicationContext);
// 依赖查找(错误)
// System.out.println(beanFactory.getBean(BeanFactory.class));
// 依赖来源三:容器內建 Bean
Environment environment = applicationContext.getBean(Environment.class);
System.out.println("获取 Environment 类型的 Bean:" + environment);
}
private static void whoIsIoCContainer(UserRepository userRepository, ApplicationContext applicationContext) {
// ConfigurableApplicationContext <- ApplicationContext <- BeanFactory
// ConfigurableApplicationContext#getBeanFactory()
// 这个表达式为什么不会成立 System.out.println(userRepository.getBeanFactory() == beanFactory); 因为ApplicationContext的实现类AbstractRefreshableApplicationContext中是通过组合的方式DefaultListableBeanFactory beanFactory,所以这里是False
System.out.println(userRepository.getBeanFactory() == applicationContext); // True
// ApplicationContext is BeanFactory
}
6.11 Spring IoC依赖来源
自定义的Bean、容器内建Bean对象、容器内建依赖(比如Envirment)代码见上面
6.12 Spring IoC配置元信息
bean定义配置:基于XML文件、基于Properties文件、基于Java注解、基于Java API
IoC容器配置:基于XML文件、基于Java注解、基于Java API
外部化属性配置:基于Java注解
6.13 Spring IoC容器
BeanFactory和ApplicationContext谁才是IoC容器?
ApplicationContext是BeanFactory的sub-interface,提供了Spring的AOP特征更好的集成,
消息资源处理、事件发布、应用层级别特殊的contexts
ApplicationContext是BeanFactory的子集,组合了 BeanFacotry
6.14 Spring应用上下文
ApplicationContext除了IoC容器角色,还提供了
AOP、配置元信息(Environments)、资源管理(Resources)、事件、国际化、注解、Envirnmnet抽象(配置走profile、外部化配置)
6.15 使用Spring IoC:选BeanFactory还是ApplicationContext
使用BeanFactory
使用ApplicationContext
6.16 Spring IoC容器的生命周期
启动
application.refresh()—> 加锁startupShudownMonitor–》prepareRefresh
—〉refreshBeanFactory(对beanFactoryMonitory加把锁)
initMessageSource应用程序国际化
initApplicationEventMulticaster事件广播
运行
停止
application.close()—>doclose()
销毁bean、销毁BeanFactory
6.17 面试题目
- 什么是Spring IoC容器?
- BeanFactory和FactroyBean区别?
BeanFactory是IoC底层容器,FactoryBean是创建Bean的一直方式,帮助实现复杂的Bean初始化过程
但是在某些具体的情况下,实例化 Bean 的操作会很复杂,按照其要求需要配置大量的属性,此时 Bean 的配置灵活性就受到了限制,此时就需要使用到 FactoryBean 了,该接口可以按照用户的需求来构造 Bean 对象,而不再遵守 Bean 生命周期的流程
- Spring IoC容器启动时候做了哪些准备?
IoC配置元信息读取和解析、IoC容器生命周期、Spring事件发布、国际化等
7 Spring Bean 基础
定义SPring Bean、BeanDefinition元信息、命名Spring bean、实例化Spring Bean、初始化Spring Bean、延迟初始化Spring Bean、销毁Spring Bean、垃圾回收Spring Bean
7.1 定义 Spring Bean
BeanDefinition 是Spring Framework 中定义 Bean 的配置元信息接口,包括:
- Bean 的类名
- Bean 行为配置元素,如作用域、自动绑定的模式、生命周期回调等
- 其他 Bean 引用,依赖关系
- 配置设置,比如 Bean 属性 (Properties
7.2 BeanDefinition 元信息
BeanDefinition 的构建
- 通过Builder模式,使用BeanDefinitionBuilder
- 通过AbstractBeanDefinition,直接使用GenericBeanDefinition extends AbstractBeanDefinition
public static void main(String[] args) {
// 1.通过 BeanDefinitionBuilder 构建
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
// 通过属性设置
beanDefinitionBuilder
.addPropertyValue("id", 1)
.addPropertyValue("name", "小马哥");
// 获取 BeanDefinition 实例
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// BeanDefinition 并非 Bean 终态,可以自定义修改
// 2. 通过 AbstractBeanDefinition 以及派生类
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
// 设置 Bean 类型
genericBeanDefinition.setBeanClass(User.class);
// 通过 MutablePropertyValues 批量操作属性
MutablePropertyValues propertyValues = new MutablePropertyValues();
// propertyValues.addPropertyValue("id", 1);
// propertyValues.addPropertyValue("name", "小马哥");
propertyValues
.add("id", 1)
.add("name", "小马哥");
// 通过 set MutablePropertyValues 批量操作属性
genericBeanDefinition.setPropertyValues(propertyValues);
}
7.3 命名 Spring Bean
Bean 名称生成器(BeanNameGenerator)
DefaultBeanNameGenerator:默认通用 BeanNameGenerator 实现
AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator 实现
7.4 Spring bean 别名化
Bean 别名(Alias)的价值
- 复用现有的 BeanDefinition
- 更加具有场景化的命名方法,通过<alias name=“原先 bean 的名称” alias=“新的别名”>
- 配置文件
<?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">
<!-- 导入第三方 Spring XML 配置文件 -->
<import resource="classpath:/META-INF/dependency-lookup-context.xml" />
<!-- 将 Spring 容器中 "user" Bean 关联/建立别名 - "xiaomage-user" -->
<alias name="user" alias="xiaomage-user" />
</beans>
- Java 代码
public class BeanAliasDemo {
public static void main(String[] args) {
// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-definitions-context.xml");
// 通过别名 xiaomage-user 获取曾用名 user 的 bean
User user = beanFactory.getBean("user", User.class);
User xiaomageUser = beanFactory.getBean("xiaomage-user", User.class);
System.out.println("xiaomage-user 是否与 user Bean 相同:" + (user == xiaomageUser));
}
}
7.5 注册 Spring Bean,把 BeanDefinition 注册到IoC容器中
BeanDefinition 注册
- XML 配置元信息
- Java 注解配置元信息@Bean、@Component、@Import
- Java API 配置元信息
命名方式:BeanDefinitionRegistry#registerBeanDefiniiton(String, BeanDefinition)
非命名方式:BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition, BeanDefinitionRegistry)
配置类方式:AnnotatedBeanDefinitionReader#registrer(Class…)
@Import(AnnotationBeanDefinitionDemo.Config.class)
public class AnnotationBeanDefinitionDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类)
applicationContext.register(AnnotationBeanDefinitionDemo.class);
// 通过 BeanDefinition 注册 API 实现
// 1.命名 Bean 的注册方式
registerUserBeanDefinition(applicationContext, "mercyblitz-user");
// 2. 非命名 Bean 的注册方法
registerUserBeanDefinition(applicationContext);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 按照类型依赖查找
System.out.println("Config 类型的所有 Beans" + applicationContext.getBeansOfType(Config.class));
System.out.println("User 类型的所有 Beans" + applicationContext.getBeansOfType(User.class));
// 显示地关闭 Spring 应用上下文
applicationContext.close();
}
// Java API 方式配置元信息
public static void registerUserBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder beanDefinitionBuilder = genericBeanDefinition(User.class);
beanDefinitionBuilder
.addPropertyValue("id", 1L)
.addPropertyValue("name", "小马哥");
// 判断如果 beanName 参数存在时
if (StringUtils.hasText(beanName)) {
// 注册 BeanDefinition
registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
} else {
// 非命名 Bean 注册方法
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(), registry);
}
}
public static void registerUserBeanDefinition(BeanDefinitionRegistry registry) {
registerUserBeanDefinition(registry, null);
}
// 2. 通过 @Component 方式
@Component // 定义当前类作为 Spring Bean(组件)
public static class Config {
// 1. 通过 @Bean 方式定义
/**
* 通过 Java 注解的方式,定义了一个 Bean
*/
@Bean(name = {"user", "xiaomage-user"})
public User user() {
User user = new User();
user.setId(1L);
user.setName("小马哥");
return user;
}
}
}
7.6 Bean 的实例化方式
常规方式:
通过构造器(配置元信息:XML、Java 注解和Java API)
通过静态工厂方法(配置元信息:XML 和 Java API)在 POJO 中定义创建对象的方法
通过 Bean 工厂方法
通过FactoryBean(配置元信息:XML、Java 注解 和 Java API)
特殊方式:
通过ServiceLoaderFactoryBean(配置元信息: XML、Java注解 和 Java API)这种是Java 的SPI机制 这里直接用ServiceLoader去load配置??
通过AutowriteCapableBeanFacotry#createBean(java.lang.Class, int boolean)
通过BeanDefinitionRegistry#y#registerBeanDefinition(String, BeanDefinition)
<?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 直接调用 POJO 中的 createUser 返回一个 new 出来的 bean-->
<bean id="user-by-static-method" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User"
factory-method="createUser"/>
<!-- 实例(Bean)方法实例化 Bean 通过 UserFactory 接口 提供的 createUser -->
<bean id="user-by-instance-method" factory-bean="userFactory" factory-method="createUser"/>
<!-- FactoryBean实例化 Bean -->
<bean id="user-by-factory-bean" class="org.geekbang.thinking.in.spring.bean.factory.UserFactoryBean" />
<bean id="userFactory" class="org.geekbang.thinking.in.spring.bean.factory.DefaultUserFactory"/>
</beans>
FactoryBean实例化 Bean
public class UserFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return User.createUser();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
7.7 Bean 的初始化方式
初始化的顺序也是如下:
- @PostConstruct 标注方法
- 实现InitializingBean 接口的 afterPropertiesSet方法
- 自定义初始化方法:
XML配置<bean init-method=“初始化方法">
Java 注解: @Bean(initMethod=“初始化方法”)
Java API:AbstractBeanDefinition#setInitMethodName(String )
依赖或者初始化倒转的问题是?
public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {
// 1. 基于 @PostConstruct 注解
@PostConstruct
public void init() {
System.out.println("@PostConstruct : UserFactory 初始化中...");
}
public void initUserFactory() {
System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中...");
}
}
@Configuration // Configuration Class
public class BeanInitializationDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类)
applicationContext.register(BeanInitializationDemo.class);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
System.out.println("Spring 应用上下文已启动...");
// 依赖查找 UserFactory
UserFactory userFactory = applicationContext.getBean(UserFactory.class);
System.out.println(userFactory);
System.out.println("Spring 应用上下文准备关闭...");
// 关闭 Spring 应用上下文
applicationContext.close();
System.out.println("Spring 应用上下文已关闭...");
}
@Bean(initMethod = "initUserFactory", destroyMethod = "doDestroy")
@Lazy(value = false)
public UserFactory userFactory() {
return new DefaultUserFactory();
}
}
7.8 Bean 的延迟初始化
Bean延迟初始化方式Lazy Initialization
- xml 配置:
- Java 注解: @Lazy(true)
延迟加载和非延迟加载区别是 延迟加载在 Spring 应用上下文启动完成后,需要调用时候被初始化
而非延迟加载是 Spring 应用上下文启动之前被加载
AbstractApplicationContext#finishBeanFactroyInitialization(beanFactory) 这个逻辑是初始化非延迟加载的bean
然后进入出发beanFactory.preInstantiateSingletons() 进行初始化
7.9 销毁 Spring Bean
Bean 的销毁顺序如下:
- @PreDestory 标注方法
- 实现DisposableBean 接口的 destroy() 方法
- 自定义销毁方法:
XML配置<bean destory=“销毁方法">
Java 注解: @Bean(destroyMethod=“销毁方法”)
Java API:AbstractBeanDefinition#setDestorytMethodName(String )
applicationContext.close()#doclose()#destroyBeans 触发 bean 的销毁方法
public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {
// 1. 基于 @PostConstruct 注解
@PostConstruct
public void init() {
System.out.println("@PostConstruct : UserFactory 初始化中...");
}
public void initUserFactory() {
System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中...");
}
@PreDestroy
public void preDestroy() {
System.out.println("@PreDestroy : UserFactory 销毁中...");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean#destroy() : UserFactory 销毁中...");
}
public void doDestroy() {
System.out.println("自定义销毁方法 doDestroy() : UserFactory 销毁中...");
}
@Override
public void finalize() throws Throwable {
System.out.println("当前 DefaultUserFactory 对象正在被垃圾回收...");
}
}
// 主类
@Configuration // Configuration Class
public class BeanInitializationDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类)
applicationContext.register(BeanInitializationDemo.class);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
System.out.println("Spring 应用上下文已启动...");
// 依赖查找 UserFactory
UserFactory userFactory = applicationContext.getBean(UserFactory.class);
System.out.println(userFactory);
System.out.println("Spring 应用上下文准备关闭...");
// 关闭 Spring 应用上下文
applicationContext.close();
System.out.println("Spring 应用上下文已关闭...");
}
@Bean(initMethod = "initUserFactory", destroyMethod = "doDestroy")
@Lazy(value = false)
public UserFactory userFactory() {
return new DefaultUserFactory();
}
}
7.10 回收 Spring Bean
Bean 垃圾回收 GC
- 关闭 Spring 容器
- 执行 GC
- Spring Bean 覆盖的 finalize() 方法被回调
7.11 Spring Bean 面试题目
- 如何注册一个 Spring Bean?
通过BeanDefinition 和 外部单体bean注册实例 - 什么是 Spring BeanDefintion?
定义Spring Bean 和 Spring BeanDefition
BeanDefinition 继承了 AttributeAccessor, BeanMetadataElement
AttributeAccessorSupport 提供通用的属性操作封装, BeanMetadataAttributeAccessor 提供Source 相关读写接口
BeanDefition 是关于 Bean 的元信息定义的接口,存储 - Spring 容器是怎么样管理注册 Bean
IoC配置元信息读取和解析、依赖查找和注入、Bean 生命周期
Spring IoC依赖查找
依赖查找的今世前生、单一类型依赖查找、集合类型依赖查找、层次性依赖查找、延迟依赖查找、安全依赖查找
内建可查找的依赖、依赖查找种的经典异常
依赖查找的今世前生
单一类型依赖查找
- JNDI:javax.naming.Context#lookup(javax.naming.Name)
- JavaBeans:BeanContext
集合类型依赖查找 - java.beans.beanContext.BeanContext
层次
单一类型依赖查找
根据bean名称查找:getBean(String )\Spring2.5 getBean(String, Object)
根据Bean类型查找:
- Bean实时查找 getBean(Class)
- Spring4.1 覆盖默认参数:getBean(Class, Object)
- Spring5.1 Bean 延迟加载
- getBeanProvider(Class)
- getBeanProvider(ResolvableType)
- 根据 Bean 名称 + 类型查找:getBean(String, Class)
集合类型依赖查找
集合类型依赖查找接口——ListableBeanFactory
- 根据Bean 类型查找
- getBeanNamesForType(Class)
- Spring 4.2 getBeanNamesForType(ResolvableType)
- 获取同类型 Bean 实例列表
- getBeansOfType(Class) 以及重载方法
- 通过注解类型查找
- Spring 3.0 获取标注类型 Bean 名称列表
- getBeanNamesForAnnotation(Class<? extends Annotation>)
- Spring 3.0 获取标注类型 Bean 实例列表
- getBeanWithAnnotation(Class<? extends Annotation>)
- Spring 3.0 获取指定名称 + 标注类型 Bean 实例列表
推荐使用Bean的名称判断Bean是否存在,重要的方式是判断BeanDefinition 是否存在
避免提早初始化bean,产生不确定因素。
层次性依赖查找-HierarchicalBeanFactory
- 双亲 BeanFactory:getParentBeanFactory()
- 层次性查找
- 根据 Bean 名称查找:基于containsLocalBean 方法实现
- 根据 Bean 类型查找实例列表:单一类型,BeanFactoryUtils#beanOfType;集合类型BeanFactoryUtils#beansOfTypeIncludingAncestors
- 根据 Java 注解查找名称列表:BeanFactoryUtils#
延迟查找
ObjectFactory 和 ObjectProvider
安全依赖查找
Spring IoC 内建可查找的依赖
依赖查找中的经典异常
面试题目
- ObjectFactory 与 BeanFactory 的区别?
- BeanFactory.getBean 操作是否线程安全?线程安全,操作过程中会增加互斥锁。