先回到《初窥Spring之依赖注入》这篇文章,里面说了,在配置文件中有以下配置:
<bean id="job" class="com.jellythink.Job"> <!-- 将juniorengineer通过构造函数的方式注入到Job对象中 --> <constructor-arg ref="juniorengineer" /> </bean>
在代码中,我们通过 Job job = context.getBean(Job.class);
就可以获得对应的Job类的实例。那么,现在问题来了;仅仅通过一个 getBean
调用就可以获得对应的实例,这是怎么做到的?同时,我们只是get一个Bean,而我们又是从哪里get这个Bean的呢?这篇文章就先来大致说说我们是从哪里获取这个实例对象的。
在基于Spring的应用中,我们的应用对象生存于Spring容器中。Spring容器负责创建对象,配置它们并管理它们的整个生命周期,从生存到死亡。首先最重要的是了解容纳对象的容器。理解容器将有助于理解对象是如何被管理的。
Spring容器并不是只有一个。Spring自带了多个容器实现,可以归为以下两种不同的类型:
org.springframework. beans. factory.eanFactory
接口定义),是最简单的容器,提供基本的DI 支持; org.springframework.context.ApplicationContext
接口定义)基于BeanFactory构建,并提供应用框架级别的服务,例如 从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听者。 由于bean工厂对大多数应用来说往往太低级了,所以我这里只总结应用上下文的使用。
Spring自带了多种类型的应用上下文,以下几种是比较常用的:
AnnotationConfigApplicationContext
:从一个或多个基于Java的配置类中加载Spring应用上下文; AnnotationConfigWebApplicationContext
:从一个或多个基于Java的配置类中加载Spring Web应用上下文; ClassPathXmlApplicationContext
:从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件 作为类资源; FileSystemXmlapplicationcontext
:从文件系统下的一个或多个XML配置文件中加载上下文定义; XmlWebApplicationContext
:从Web应用下的一个或多个XML配置文件中加载上下文定义。 其中 AnnotationConfigWebApplicationContext
和 XmlWebApplicationContext
我会在后面的博文会进行详细的总结,这里先说说剩下三种的使用。
无论是从文件系统中装载应用上下文还是从类路径下装载应用上下 文,将bean加载到bean工厂的过程都是相似的。
// 从类路径中加载应用上下文 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("job.xml"); // 从文件系统中加载应用上下文 FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("file:/Users/Jelly/job.xml");
这里有篇文章,重点的总结了 Spring中ClassPathXmlApplication和FileSystemXmlApplicationContext使用区别 。
使用 AnnotationConfigApplicationContext
来配置应用上下文则比较麻烦一些,因为要单独编写一个配置类。
对于以下的XML配置来说:
<!-- 创建SeniorEngineer对象 --> <bean id="seniorengineer" class="com.jellythink.SeniorEngineer" /> <!-- 创建JuniorEngineer对象 --> <bean id="juniorengineer" class="com.jellythink.JuniorEngineer" /> <bean id="job" class="com.jellythink.Job"> <!-- 将juniorengineer通过构造函数的方式注入到Job对象中 --> <constructor-arg ref="juniorengineer" /> </bean>
则需要编写这样的一个配置类:
@Configuration public class AppContextDemo { @Bean public SeniorEngineer seniorengineer() { SeniorEngineer seniorengineer = new SeniorEngineer(); return seniorengineer; } @Bean public JuniorEngineer juniorengineer() { JuniorEngineer juniorengineer = new JuniorEngineer(); return juniorengineer; } @Bean public Job job() { JuniorEngineer juniorengineer = new JuniorEngineer(); Job job = new Job(juniorengineer); return job; } }
正如您通过以上代码所看到的那样,现在可以以编程的方式将bean 定义为基于Java的配置的一部分。AppContextDemo类现在就像XML一样表示配置类。这是通过利用@Configuration注释实现的。@Configuration注释位于类的顶端。它告知Spring容器这个类是一个拥有bean定义和依赖项的配置类。@Bean注释用于定义bean。上述注释位于实例化bean并设置依赖项的方法上方。方法名称与bean id或默认名称相同。该方法的返回类型是向Spring应用程序上下文注册的bean。您可使用bean的setter方法来设置依赖项,容器将调用它们来连接相关项。基于Java的配置也被视为基于注释的配置。通过以下代码来使用AnnotationConfigApplicationContext注册配置类:
ApplicationContext context = new AnnotationConfigApplicationContext(AppContextDemo.class);
在传统的Java应用中,bean的生命周期很简单。使用Java关键字new进行bean实例化,然后该bean就可以使用了。一旦该bean不再被使用,则由Java自动进行垃圾回收。
相比之下,Spring容器中的bean的生命周期就显得相对复杂多了。正确理解Spring bean的生命周期非常重要,因为将来我们可能要利用Spring提供的扩展点来自定义bean的创建过程。下面先通过一张图来说明bean的整个生命周期。
从上图中可以看到,在bean准备就绪之前,bean工厂执行了若干启动步骤。下面就对上面的每一步都进行简单的说明:
BeanNameAware
接口,Spring将bean的ID传递给setBean-Name()方法; BeanFactoryAware
接口,Spring将调用 setBeanFactory()
方法,将BeanFactory容器实例传入; ApplicationContextAware
接口,Spring将调用 setApplicationContext()
方法,将bean所在的应用上下文的 引用传入进来; BeanPostProcessor
接口,Spring将调用它们的post-ProcessBeforeInitialization()方法; InitializingBean
接口,Spring将调用它们的after-PropertiesSet()方法。类似地,如果bean使用init- method声明了初始化方法,该方法也会被调用; BeanPostProcessor
接口,Spring将调用它们的post-ProcessAfterInitialization()方法; DisposableBean
接口,Spring将调用它的 destroy()
接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。 上面就是一个bean在Spring容器中的整个生命周期。每一阶段都可以针对Spring如何管理bean进行个性化定制,这个就可以根据我们的需要来搞了。
Spring容器这个概念贯穿着整个Spring,我们必须要掌握的知识点,这篇文章也只是总结了一点皮毛,但是对于入门Spring容器还是有很大帮助的,希望大家喜欢。
无聊而有趣的一天,虚度而又充实的一个周末。
果冻想-一个原创技术文章分享网站。
2017年6月4日 于呼和浩特。