如何注册一个 Spring Bean?
通过 BeanDefinition 和外部单体对象来注册
BeanDefinition
是 Spring Framework 中定义 Bean 的配置元信息接口,包含:
BeanDefinition
元信息如下:
属性(Property) | 说明 |
---|---|
Class Bean | 全类名,必须是具体类,不能用抽象类或接口 |
Name Bean | 的名称或者 ID |
Scope Bean | 的作用域(如:singleton、prototype 等) |
Constructor arguments Bean | 构造器参数(用于依赖注入) |
Properties Bean | 属性设置(用于依赖注入) |
Autowiring mode Bean | 自动绑定模式(如:通过名称 byName) |
Lazy initialization mode Bean | 延迟初始化模式(延迟和非延迟) |
Initialization method Bean | 初始化回调方法名称 |
Destruction method Bean | 销毁回调方法名称 |
BeanDefinition 构建方式:
BeanDefinitionBuilder
AbstractBeanDefinition
以及派生类// 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);
每个 Bean 拥有一个或多个标识符(identifiers),这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名(Alias)来扩充。
在基于 XML 的配置元信息中,开发人员可用 id 或者 name 属性来规定 Bean 的标识符。通常 Bean 的标识符由字母组成,允许出现特殊字符。如果要想引入 Bean 的别名的话,可在 name 属性使用半角逗号(“,”)或分号(“;”) 来间隔。
Bean 的 id 或 name 属性并非必须制定,如果留空的话,容器会为 Bean 自动生成一个唯一的名称。Bean 的命名尽管没有限制,不过官方建议采用驼峰的方式,更符合 Java 的命名约定。
Spring 提供了两种 Spring Bean 命名生成器:
public interface BeanNameGenerator {
String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}
可能存在这样的场景,不同系统中对于同一 bean 的命名方式不一样。 为了适配,Spring 支持 <alias>
为 bean 添加别名的功能。
<bean id="user" class="io.github.dunwu.spring.core.domain.User"
p:name="张三" p:age="18">
</bean>
<alias name="sysUser" alias="superUser" />
Bean 别名(Alias)的作用:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
BeanNameAware
接口,Spring 将 Bean 的 ID 传递给 setBeanName
方法 BeanFactoryAware
接口,Spring 将调用 setBeanDactory
方法,并把 BeanFactory
容器实例作为参数传入。 ApplicationContextAware
接口,Spring 容器将调用 setApplicationContext
方法,把应用上下文作为参数传入 BeanFactory
类似都是为了获取 Spring 容器,不同的是 Spring 容器在调用 setApplicationContext
方法时会把它自己作为 setApplicationContext
的参数传入,而 Spring 容器在调用 setBeanFactory
前需要使用者自己指定(注入)setBeanFactory
里的参数 BeanFactory
BeanPostProcess
接口,Spring 将调用 postProcessBeforeInitialization
方法 InitializingBean
接口,Spring 将调用 afterPropertiesSet
方法,作用与在配置文件中对 Bean 使用 init-method
声明初始化的作用一样,都是在 Bean 的全部属性设置成功后执行的初始化方法。BeanPostProcess
接口,Spring 将调用 postProcessAfterInitialization
方法 postProcessBeforeInitialization
是在 Bean 初始化前执行的,而 postProcessAfterInitialization
是在 Bean 初始化后执行的DispostbleBean
接口,Spring 将调用它的 destory
方法,作用与在配置文件中对 Bean 使用 destory-method
属性的作用一样,都是在 Bean 实例销毁前执行的方法。注册 Spring Bean 实际上是将 BeanDefinition
注册到 IoC 容器中。
Spring 的传统配置方式。在 <bean>
标签中配置元数据内容。
缺点是当 JavaBean 过多时,产生的配置文件足以让你眼花缭乱。
使用 @Bean
、@Component
、@Import
注解注册 Spring Bean。
BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,Be
anDefinitionRegistry)
AnnotatedBeanDefinitionReader#register(Class...)
[示例]
@Import(AnnotationComponentScan.MyConfiguration.class)
public class AnnotationComponentScan {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
// 1.注册配置类
ctx.register(AnnotationComponentScan.class);
// 2.通过 Java API 注册
registerBeanDefinition(ctx, "zhaoliu", User.class);
// 启动应用上下文
ctx.refresh();
User wangwu = (User) ctx.getBean("wangwu");
System.out.println("wangwu info: " + wangwu);
System.out.println("All beans of User: " + ctx.getBeansOfType(User.class));
//显示关闭 ApplicationContext
ctx.close();
}
public static void registerBeanDefinition(BeanDefinitionRegistry registry, String beanName, Class<?> beanClass) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass);
builder.addPropertyValue("name", "赵六");
builder.addPropertyValue("age", 31);
// 注册 BeanDefinition
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
@Configuration
public static class MyConfiguration {
// 注解方式配置元信息
@Bean(name = { "user", "wangwu" })
public User user() {
return new User("王五", 21);
}
}
}
Spring Bean 实例化方式:
FactoryBean
(配置元信息:XML、Java 注解和 Java API)ServiceLoaderFactoryBean
(配置元信息:XML、Java 注解和 Java API )AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
Spring Bean 初始化和销毁的方式有以下几种:
@PostConstruct
和 @PreDestroy
注解分别指定相应的初始化方法和销毁方法。InitializingBean
接口的 afterPropertiesSet()
方法来编写初始化方法;实现 DisposableBean
接口的 destroy()
方法来编写销毁方法。<bean init-method="init" destroy="destroy" ... />
@Bean(initMethod = "init", destroyMethod = "destroy")
AbstractBeanDefinition#setInitMethodName(String)
和 AbstractBeanDefinition#setDestroyMethodName(String)
分别定义初始化和销毁方法注意:如果同时存在,执行顺序会按照序列执行。
@Bean
的 initMethod 和 destroyMethod(1)定义 Bean 实例
public class Pojo {
public Pojo() {
System.out.println("[Pojo 构造方法]");
}
public void init() {
System.out.println("[Pojo 初始化方法]");
}
public void destroy() {
System.out.println("[Pojo 销毁方法]");
}
}
(2)使用 @Bean
在配置类中注册 Bean
@Configuration
public class AnnotationBeanDemo {
@Bean(initMethod = "init", destroyMethod = "destroy")
public Pojo pojo() {
return new Pojo();
}
}
说明:
@Bean
注解的initMethod
和destroyMethod
属性分别用于指定 Bean 对应的初始化方法和销毁方法。
(3)测试:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationBeanDemo.class);
System.out.println("AnnotationBeanDemo 示例结束");
context.close();
}
输出
[Pojo 构造方法]
[Pojo 初始化方法]
AnnotationBeanDemo 示例结束
[Pojo 销毁方法]
InitializingBean
和 DisposableBean
InitializingBean
接口包含一个 afterPropertiesSet
方法,可以通过实现该接口,然后在这个方法中编写初始化逻辑。DisposableBean
接口包含一个 destory
方法,可以通过实现该接口,然后在这个方法中编写销毁逻辑。(1)定义 Bean 实例
public static class Pojo2 implements InitializingBean, DisposableBean {
public Pojo2() {
System.out.println("[Pojo2 构造方法]");
}
@Override
public void afterPropertiesSet() {
System.out.println("[Pojo2 初始化方法]");
}
@Override
public void destroy() {
System.out.println("[Pojo2 销毁方法]");
}
}
(2)使用 @Bean
在配置类中注册 Bean
@Configuration
public class AnnotationBeanDemo {
@Bean
public Pojo2 pojo2() {
return new Pojo2();
}
}
(3)测试:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationBeanDemo.class);
System.out.println("AnnotationBeanDemo 示例结束");
context.close();
}
输出
[Pojo2 构造方法]
[Pojo2 初始化方法]
AnnotationBeanDemo 示例结束
[Pojo2 销毁方法]
@PostConstruct
和 @PreDestroy
可以使用 @PostConstruct
和 @PreDestroy
注解修饰方法来指定相应的初始化和销毁方法。
BeanPostProcessor
Spring 提供了一个 BeanPostProcessor
接口,提供了两个方法 postProcessBeforeInitialization
和 postProcessAfterInitialization
。其中postProcessBeforeInitialization
在组件的初始化方法调用之前执行,postProcessAfterInitialization
在组件的初始化方法调用之后执行。它们都包含两个入参:
(1)定义 Pojo,如上面的例子
(2)定义 BeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor {
public MyBeanPostProcessor() {
System.out.println("[BeanPostProcessor] construct");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[BeanPostProcessor] postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[BeanPostProcessor] postProcessAfterInitialization");
return bean;
}
}
(3)使用 @Bean
在配置类中注册 Bean
@Bean
public Pojo pojo() {
return new Pojo();
}
@Bean
public MyBeanPostProcessor myBeanPostProcessor() {
return new MyBeanPostProcessor();
}
(3)测试:
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyBeanPostProcessorDemo.class);
System.out.println("BeanPostProcessorDemo 示例结束");
context.close();
}
输出
[BeanPostProcessor] construct
[Pojo 构造方法]
[BeanPostProcessor] postProcessBeforeInitialization
[Pojo 初始化方法]
[BeanPostProcessor] postProcessAfterInitialization
BeanPostProcessorDemo 示例结束
[Pojo 销毁方法]
Spring Bean 垃圾回收步骤: