Bean的初始化和销毁
在整个生命周期过程中,我们可以自定义Bean的初始化和销毁钩子函数,当Bean的生命周期到达相应的阶段的时候,Spring会调用我们自定义的Bean的初始化和销毁方法。自定义Bean初始化和销毁方法有多种方式,下面逐一介绍。
@Bean
上一节中介绍了可以在配置类中通过@Bean
注解来注册Bean,我们也可以通过它来指定Bean的初始化和方法。
为了演示,我们新建一个Spring Boot项目,然后创建一个User
类:
publicclassUser{publicUser(){System.out.println("调用无参构造器创建User");}publicvoidinit(){System.out.println("初始化User");}publicvoiddestory(){System.out.println("销毁User");}}
然后在配置类里注册该组件,并指定初始化和销毁方法:
@ConfigurationpublicclassWebConfig{@Bean(initMethod="init",destroyMethod="destory")publicUseruser(){returnnewUser();}}
其中initMethod = "init"
和destroyMethod = "destory"
与User类里的init
,destory
方法相对应。
在Spring Boot入口类中测试:
//返回IOC容器,使用注解配置,传入配置类AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(WebConfig.class);Useruser=context.getBean(User.class);//关闭IOC容器context.close();
启动项目,观察控制台输出:
从上面的输出我们看出在容器启动之前,先调用对象的无参构造器创建对象,然后调用初始化方法,在容器关闭的时候调用销毁方法。
上面的情况是对于单例而言的,如果组件是多例模式又是什么情况呢?我们把上面的组件注册配置改为多例,然后再次启动项目,观察控制台输出:
在多例模式下,IOC容器启动的时候并不会去创建对象,而是在每次获取的时候才会去调用方法创建对象,创建完对象后再调用初始化方法。
但在容器关闭后,Spring并没有调用相应的销毁方法,这是因为在多例模式下,容器不会管理这个组件(只负责在你需要的时候创建这个组件),所以容器在关闭的时候并不会调用相应的销毁方法。
InitializingBean&DisposableBean
除了上面这种方式指定初始化和销毁方法外,Spring还为我们提供了和初始化,销毁相对应的接口:
InitializingBean
接口包含一个afterPropertiesSet
方法,我们可以通过实现该接口,然后在这个方法中编写初始化逻辑。
DisposableBean
接口包含一个destory
方法,我们可以通过实现该接口,然后再这个方法中编写销毁逻辑。
新建一个类,名称为Bird
,然后实现这两个接口:
publicclassBirdimplementsInitializingBean,DisposableBean{publicBird(){System.out.println("调用无参构造器创建Bird");}@Overridepublicvoiddestroy(){System.out.println("销毁Bird");}@OverridepublicvoidafterPropertiesSet(){System.out.println("初始化Bird");}}
在配置类中注册这个组件:
@BeanpublicBirdbird(){returnnewBird();}
测试一波:
AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(WebConfig.class);System.out.println("容器创建完毕");context.close();
启动项目,观察控制台输出:
@PostConstruct & @PreDestroy
除了上面两种指定初始化和销毁方法的方式外,我们还可以使用@PostConstruct
和@PreDestroy
注解修饰方法来指定相应的初始化和销毁方法。
新建一个类,名称为Fish:
publicclassFish{publicFish(){System.out.println("调用无参构造器创建Fish");}@PostConstructpublicvoidinit(){System.out.println("初始化Fish");}@PreDestroypublicvoiddestory(){System.out.println("销毁Fish");}}
在配置类中这个组件:
@BeanpublicFishfish(){returnnewFish();}
测试一波:
AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(WebConfig.class);System.out.println("容器创建完毕");context.close();
启动项目,观察控制台输出:
效果和上面两种方式一致。
这两个注解并非Spring提供,而是JSR250规范提供。
BeanPostProcessor
Spring提供了一个BeanPostProcessor
接口,俗称Bean后置通知处理器,它提供了两个方法postProcessBeforeInitialization
和postProcessAfterInitialization
。其中postProcessBeforeInitialization
在组件的初始化方法调用之前执行,postProcessAfterInitialization
在组件的初始化方法调用之后执行。它们都包含两个入参:
bean:当前组件对象;
beanName:当前组件在容器中的名称。
两个方法都返回一个Object类型,我们可以直接返回当前组件对象,或者包装后返回。
我们来定义一个BeanPostProcessor
接口的实现类MyBeanPostProcessor
:
publicclassMyBeanPostProcessorimplementsBeanPostProcessor{@OverridepublicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)throwsBeansException{System.out.println(beanName+"初始化之前调用");returnbean;}@OverridepublicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{System.out.println(beanName+"初始化之后调用");returnbean;}}
在配置类中注册该组件:
@ConfigurationpublicclassWebConfig{@Bean(initMethod="init",destroyMethod="destory")publicUseruser(){returnnewUser();}}0
再次启动项目,观察控制台输出:
总结
主要把握创建过程和销毁过程这两个大的方面:
创建过程:首先实例化Bean,并设置Bean的属性,根据其实现的Aware接口(主要是BeanFactoryAware接口,BeanFactoryAware,ApplicationContextAware)设置依赖信息,接下来调用BeanPostProcess的postProcessBeforeInitialization方法,完成initial前的自定义逻辑;afterPropertiesSet方法做一些属性被设定后的自定义的事情;调用Bean自身定义的init方法,去做一些初始化相关的工作;然后再调用postProcessAfterInitialization去做一些bean初始化之后的自定义工作。这四个方法的调用有点类似AOP。 此时,Bean初始化完成,可以使用这个Bean了。
销毁过程:如果实现了DisposableBean的destroy方法,则调用它,如果实现了自定义的销毁方法,则调用之。