Spring Bean基础
1.1 定义Spring Bean
AbstractBeanDefinition中有Bean的配置元信息
1.1.1 什么是BeanDefinition?
BeanDefinition是Spring Framework中定义Bean的配置元信息接口,包含:
- Bean的类名
- Bean行为配置元素,如作用域,自动绑定的模式(@Autowired),声明周期回调等
- 其他Bean引用,又称合作者或者依赖
- 配置设置,比如Bean属性(Properties),例如数据库连接池的配置
1.2 通过BeanDefinition构建Bean
- 通过BeanDefinitionBuilder构建
// 1. 通过BeanDefinitionBuilder构建
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder.addPropertyValue("age", 14);
beanDefinitionBuilder.addPropertyValue("name", "xiaomage");
// 获取BeanDefinition实例
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
- 通过AbstractBeanDefinition构建
// 2. 通过AbstractBeanDefinition
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(User.class);
// 通过MutablePropertyValues临时存储properties属性值
MutablePropertyValues propertyValues = new MutablePropertyValues();
// propertyValues.addPropertyValue("age", 14);
// propertyValues.addPropertyValue("name", "xiaomage");
propertyValues
.add("age", 14)
.add("name", "xiaomage");
// setPropertyValues:设置Bean的属性值
genericBeanDefinition.setPropertyValues(propertyValues);
1.3 注册Bean(BeanDefinition 注册)
- XML配置元信息
- <bean id="…" class="…"… />
- Java注解配置元信息(
AnnotationConfigApplicationContext
)- @Bean
- @Component
- @Import
- Java API配置元信息
- 命名方式:
org.springframework.beans.factory.support.BeanDefinitionRegistry#registerBeanDefinition
- 非命名方式:
org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerWithGeneratedName
- 配置类方式:
org.springframework.context.annotation.AnnotatedBeanDefinitionReader#register
- 命名方式:
1.4 实例化Spring bean
-
常规方式
- 通过构造器(配置元信息:XML,JAVA注解和JavaApi)
- 通过静态工厂
- 通过Bean工厂方法
- 通过FactoryBean
-
特殊方式
- 通过ServiceLoaderFactoryBean
- 通过AutowireCapableBeanFactory#createBean
- 通过BeanDefininationReistry#registerBeanDefinition(String,BeanDefinition)
静态工厂:
<!-- 添加factory-method-->
<bean id="user-create-by-static-method" class="edu.ahau.thinking.in.spring.ioc.overview.domain.User" factory-method="createUser"/>
在User类里添加createUser静态方法,跟factory-method中的字符串一样;
public static User createUser(){
User user = new User();
user.setAge(12);
user.setName("zasn");
return user;
}
1.4.1 ServiceLoaderFactoryBean
1.4.1.1 先介绍java的控制反转(ServiceLoader)
- 点进ServiceLoader,查看:发现有前缀限制,
- 因此需要在resource包下建立META-INF/services/xxx文件,文件的名字就是接口名称,文件里面的全限定类名是实现接口的类;
- java代码获取
ServiceLoader<UserFactory> serviceLoader = ServiceLoader.load(UserFactory.class, Thread.currentThread().getContextClassLoader());
对ServiceLoader对象遍历Iterator<UserFactory> iterator = serviceLoader.iterator();
1.4.1.2 通过AutowireCapableBeanFactoryBean获取ServiceLoader
<bean id="serviceLoaderFactoryBean" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
<!-- org.springframework.beans.factory.serviceloader.AbstractServiceLoaderBasedFactoryBean中的serviceType-->
<property name="serviceType" value="edu.ahau.thinking.in.spring.bean.factory.UserFactory"/>
</bean>
- 使用Spring应用上下文获取AutowireCapableBeanFactory
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");
AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
ServiceLoader<UserFactory> serviceLoaderFactoryBean = beanFactory.getBean("serviceLoaderFactoryBean", ServiceLoader.class);
通过AutowireCapableBeanFactory可以实例化Bean,通过调用createBean(Class),入参的class对象和不能是接口,而是一个实体类。
UserFactory bean = beanFactory.createBean(DefaultUserFactory.class);
1.5 Bean的初始化
三种方法:
- @PostConstruct
- 实现InitializingBean接口
- 自定义方法(initMethod)
代码如下:
public class DefaultUserFactory implements UserFactory, InitializingBean {
@PostConstruct
public void init() {
System.out.println("由@PostConstruct,UserFactory初始化中....");
}
@Override
public void initUserFactory(){
System.out.println("由自定义,UserFactory初始化中....");
}
// 实现InitializingBean接口,在设置属性后初始化
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("实现InitializingBean接口,在设置属性后初始化....");
}
}
public class BeanInitializationDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册配置类
applicationContext.register(BeanInitializationDemo.class);
// 启动Spring应用上下文
applicationContext.refresh();
// 依赖查找
applicationContext.getBean(UserFactory.class);
// 关闭Spring应用上下文
applicationContext.close();
}
@Bean(initMethod = "initUserFactory")
public UserFactory userFactory() {
return new DefaultUserFactory();
}
}
输出结果:
由@PostConstruct,UserFactory初始化中....
实现InitializingBean接口,在设置属性后初始化....
由自定义,UserFactory初始化中....
得到初始化的顺序:@PostConstruct > 实现InitializingBean接口 > 自定义自定义方法(优先级从大到小)
@Bean(initMethod = "initUserFactory")
在@Bean注解中需要取到initMethod的映射关系;
查看AbstractBeanDefinition
中的setInitMethodName(String initMethodName)
方法,在这个地方进行debug,需要加上debug的condition条件才能很明显的看到initMethodName.
1.5.1 Bean的延迟初始化
在@Bean
处加上@Lazy
,比较一下即可:
非延迟加载,控制台如下
由@PostConstruct,UserFactory初始化中....
实现InitializingBean接口,在设置属性后初始化....
由自定义,UserFactory初始化中....
Spring应用上下文已启动
延迟加载,控制台如下
Spring应用上下文已启动
由@PostConstruct,UserFactory初始化中....
实现InitializingBean接口,在设置属性后初始化....
由自定义,UserFactory初始化中....
根据控制台打印数据可以看出:延迟加载影响Bean初始化位置。延迟加载,Spring应用上下文启动是在Bean初始化之前;反之,非延迟加载。
查看org.springframework.context.support.AbstractApplicationContext#refresh
源码,应用上下文启动的时候会初始化没有延迟加载的Bean
1.6 Bean的销毁阶段
- @PreDestroy
- 实现DisposableBean接口
- 自定义方法@Bean(destroyMethod = “destroyFactory”)
原理与1.5 Bean
的实例化时类似的,查看close()
方法,
@PreDestroy到最后的实现依然是调用DisposableBean
的destroy()方法;
1.7 SpringBean的GC
正常的注册Bean,调用System.gc()
进行强制GC
public class BeanGarbageCollectionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext beanFactory = new AnnotationConfigApplicationContext();
beanFactory.register(BeanInitializationDemo.class);
beanFactory.refresh();
beanFactory.close();
System.gc();
}
}
在Bean对象中覆写Object中的finalize()
方法
@Override
protected void finalize() throws Throwable{
System.out.println("当前 DefaultUserFactory 对象正在被回收。。。");
}
控制台:
当前 DefaultUserFactory 对象正在被回收。。。
如果在调用System.gc()后,没有显示"对象正在被回收。。。",这是正常的行为,需要在gc之前将线程睡眠大概5s左右;