Spring初始化过程,相信很多人都知道,而初始化过程中,我们能够做些什么,现在就来做个总结。
项目地址:https://github.com/esiyuan/spring_sdy.git
导出项目sdy01,如图:
图上可以看到,非常简单,代码中没有用Xml的配置方式,而是使用JavaConfig的配置方式,非常的简单。我们可以ConfigSty01入手,这个就是配置文件,相当于xml配置方式的xml文件,其中@Configuration注解,这是标注此类具有xml配置相同的功能,而@ComponentScan提供能进行扫描的功能,spring还提供了很多的注解,进行不同的功能,可以通过@PropertySource注解加载properties文件内容进入上下文;通过@ImportResource可以实现导入xml配置,以及另外的javaconfig配置,进行模块化装配等等。
我们看看这个配置类的详情:
@Configuration
@ComponentScan(basePackages = "com.esy.sd.tdy01.impl")
public class ConfigSty01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ConfigSty01.class);
Producible producter = ac.getBean(Producible.class);
producter.produce();
ac.close();
}
}
从代码中可以看到,使用javaConfig进行配置的话,spring装载上下文,使用的是AnnotationConfigApplicationContext,作用类似ClassPathXmlApplicationContext从类路径进行加载。
我们在看下Producter类,可以看到实现了非常多的接口,而这就是这篇文章的重点,spirng初始化过程会进行哪些动作,而我们又可以做些什么,就是需要实现这些接口才有具有。
@Component
@Log4j
public class Producter implements Producible, InitializingBean, ApplicationContextAware
,BeanClassLoaderAware, BeanNameAware, DisposableBean{
@Override
public void destroy() throws Exception {
log.info("destroy()");
}
@PreDestroy
public void preDestory() {
log.info("preDestory()");
}
@Override
public void produce() {
log.info("produce()");
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("afterPropertiesSet()");
}
@Override
public void setBeanName(String name) {
log.info("setBeanName()" + name);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
log.info("setBeanClassLoader()" + classLoader);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
log.info("setApplicationContext()" + applicationContext);
}
@PostConstruct
public void postContruct() {
log.info("postContruct()");
}
}
我们运行ConfigSty01的main方法,会发现打印如下.
2017-02-18 16:56:11,161 INFO annotation.AnnotationConfigApplicationContext (AbstractApplicationContext.java:577) - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@56606032: startup date [Sat Feb 18 16:56:11 CST 2017]; root of context hierarchy
2017-02-18 16:56:11,351 INFO impl.Producter (Producter.java:41) - setBeanName()producter
2017-02-18 16:56:11,351 INFO impl.Producter (Producter.java:45) - setBeanClassLoader()sun.misc.Launcher$AppClassLoader@5200089
2017-02-18 16:56:11,351 INFO impl.Producter (Producter.java:50) - setApplicationContext()org.springframework.context.annotation.AnnotationConfigApplicationContext@56606032: startup date [Sat Feb 18 16:56:11 CST 2017]; root of context hierarchy
2017-02-18 16:56:11,351 INFO impl.Producter (Producter.java:54) - postContruct()
2017-02-18 16:56:11,351 INFO impl.Producter (Producter.java:37) - afterPropertiesSet()
2017-02-18 16:56:11,376 INFO impl.Producter (Producter.java:33) - produce()
2017-02-18 16:56:11,376 INFO annotation.AnnotationConfigApplicationContext (AbstractApplicationContext.java:957) - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@56606032: startup date [Sat Feb 18 16:56:11 CST 2017]; root of context hierarchy
2017-02-18 16:56:11,377 INFO impl.Producter (Producter.java:29) - preDestory()
2017-02-18 16:56:11,377 INFO impl.Producter (Producter.java:25) - destroy()
由此可以看出,spring容器进行javabean管理的时候,并不是只是简单的实例化,还提供和容器相关的功能,例如:BeanNameAware、BeanClassLoaderAware、ApplicationContextAware接口,当我们的类实现了这些接口后,同时也就和spring进行了强耦合(如今很少会有不用spring的项目,基于spring的开发估计都成了规范,还会有谁关心是否spring耦合了呢,重点是项目能够运行)。通过BeanNameAware我们可以获取spring容器中此类实例的名字,通过BeanClassLoaderAware我们可以获取加载此类的类加载器,这两个好像不怎么常见,而ApplicationContextAware这个就非常有用了,实现了它,那么容器实例化类后,会将spring的上下文传递给此实例,通过spring上下文,我们就可以获取到spring容器中的其他bean实例,对于有些场景,需要在项目中利用名称获取其他bean的地方,就非常有用了。
再来,就是spring生命周期相关的接口了,实现了InitializingBean接口,spring容器会在bean实例化后进行afterPropertiesSet方法的调用,相同的功能,我们也能够通过@PostConstruct注解实现,可能对于大家最常用的应该是配置文件中init属性指定方法,而实现了DisposableBean接口,容器会销毁前进行destroy方法的调用,同样的也可以通过@PreDestroy注解的方式以及配置文件中 destroy-method属性进行指定,这些都是spring提供的生命周期管理的方式,具体的选择,就需要看大家的使用习惯了。
最后总结下,bean实例化的流程。