4. Spring Bean 基础
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 生命周期等(后续详解)