Spring容器里面 Bean的创建与Bean的生命周期
在工作或者学习中我们经常会使用spring的依赖注入来创建对象,并且将对象交给IOC容器去管理。那么spring究竟是如何为我们创建bean,以及是如何管理bean的呢?
bean的创建
我们可以看AbstractApplicationContext类中的refresh方法,spring在容器创建完成之后会调用refresh方法来初始化、刷新容器里面的bean:
这里我们把这个方法抠出来看看
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 为刷新过程准备上下文环境
prepareRefresh();
// 告诉子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备bean工厂以在此上下文中使用
prepareBeanFactory(beanFactory);
try {
// 为bean工厂添加后置处理器
postProcessBeanFactory(beanFactory);
// 执行bean工厂的后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 为beanfactory里面的bean注册后置处理器
registerBeanPostProcessors(beanFactory);
// 初始化此上下文的消息源
initMessageSource();
// 初始化上下文事件广播
initApplicationEventMulticaster();
// 在特定的上下文子类中初始化其他特殊bean
onRefresh();
// 检查bean上面的监听器并且注册
registerListeners();
// 实例化所有(非懒加载)的单例
finishBeanFactoryInitialization(beanFactory);
// 最后,发布相应的事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 销毁已经创建的单例bean,避免浪费空间
destroyBeans();
// 重置'有效'标志
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();
}
}
}
在上面的代码中下面这个方法就是其中创建bean的关键方法
我们继续往下跟,这里会调用getBean方法
然后又会调用doGetBean 方法,在doGetBean方法里面我们找到这一段代码,就是创建bean实例的代码
这里面又会有一堆判断,然后调用doCreateBean,后面又会调用createBeanInstance 方法,这里createBeanInstance 方法就是真正的创建实例的方法,OK我们进入这个方法之后会看到他在返回的时候调用了instantiateBean这个方法
我们继续跟进去看
/**
* Instantiate the given bean using its default constructor.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @return a BeanWrapper for the new instance
*/
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
}
else {
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
上面代码中的instantiate方法就是创建bean的方法,并且点所生成的bean包装成了一个通用的BeanWrapper 类型, 我们看一下这个方法
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
//如果没有覆盖,请不要使用CGLIB覆盖类。
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(
(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
}
else {
constructorToUse = clazz.getDeclaredConstructor();
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// 必须使用CGLIB代理生成实例
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
在这里我们可以看到,spring会判断类里面是否有重写的方法,如果有的话就会使用Cgilb 去生成对象,否则的话就直接用反射生成对象。
bean的生命周期
既然spring容器帮我们创建了容器,那么他也就能为我们销毁容器,那么就一定会有一个对应的声明周期。
在spring容器中bean的而生命周期为 创建–> 初始化–>销毁。同时spring也为我们提供了bean生命周期 的监控,一共有三种方式:使用@bean注解里面的属性 initMethod 和 destroyMethod ,让bean实现InitializingBean 和 DisposableBean接口;使用JSR250规范里面的注解 @PostConstruct: 在Bean创建完成,且属于赋值完成后进行初始化,属于JDK规范的注解,@PreDestroy: 在bean将被移除之前进行通知, 在容器销毁之前进行清理工作。
1、使用@bean注解里面的属性 initMethod 和 destroyMethod
我们在bean里面添加两个方法,初始化和销毁
我
我们在配置类里面为该bean加上如**解
@Configuration
public class PdfConfigration {
@Bean(initMethod = "init",destroyMethod = "destroy")
public ParseToPdf parseExcel(){
return new ParseExcelToPdf();
}
2、让bean实现InitializingBean 和 DisposableBean接口
实现接口之后会要求重写两个方法,其中afterPropertiesSet 为初始化方法,也就是在属性赋值之后执行这个方法。
3、使用@PostConstruct 和 @PreDestroy
这样通过以上三种方式我们都可以监控bean的生命周期,并且做一些我们想要做的事情。