ApplicationContext
ConfigurationApplicationContext扩展于ApplicationContext,新增了两个主要的方法,refresh()和close(),让ApplicationxContext具有启动,刷新和关闭应用上下文的能力。
在获取ApplicationContext实例后,就可以像BeanFactory一样调用getBean(beanName)返回Bean了。
ApplicationContext的初始化和BeanFactory有一个重大区别:
- BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例化目标Bean;
- ApplicationContext在初始化应用上下文时就实例化所有的单实例的Bean,因此ApplicationContext的初始化时间比BeanFactory稍长一些。
注解支持 AnnotationConfigApplicationContext
Spring支持基于类注解的配置方式,主要功能来源于Spring名为javaCOnfig的子项目。JavaConfig已升级为Spring核心框架的一部分,一个标注@Configuration注解的POJO即可提供Spring所需的Bea配置信息。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.smart.Car;
@Configuration
public class Beans {
@Bean(name = "car")
public Car buildCar() {
Car car = new Car();
car.setBrand("红旗CA72");
car.setMaxSpeed(200);
return car;
}
}
和基于XMl文件的配置方式相比,类注解的配置方式可以很容易地让开发者控制Bean的初始化过程,比基于XML文件的配置方式更灵活。
WebApplicationContext
WebApplicationContext是专门为Web应用准备的,它允许从Web根目录的路径中装载配置文件并完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用。整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Spring应用上下文。
Spring为此专门提供了一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc) ,可以从ServletContext中获取WebApplicationContext的实例。
Bean的作用域
- 非Web应用环境下,Bean只有singleton和protorype两种作用域。
- WebApplicationContext为Bean新添了三个作用域:request session 和 global session(application)
/**
* Scope identifier for request scope: "request".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_REQUEST = "request";
/**
* Scope identifier for session scope: "session".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_SESSION = "session";
/**
* Scope identifier for the global web application scope: "application".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_APPLICATION = "application";
Web应用比一般的应用拥有更多特性,因此WebApplicationContext扩展了ApplicationContext。WebApplicationContext定义了一个常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文启动时,WebApplicationContext实例即以此为键放置在
ServletContext属性列表中,可以通过以下语句从Web容器中获取WebApplicationContext
WebApplicationContext wac = (WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
ConfigurableWebApplicationContext扩展了WebApplicationContext,允许通过配置的方式实例化WebApplicationContext,同时定义了两个重要的方法。
- #setServletContext(ServletContext servletContext):为Spring设置Web应用上下文,以便二者整合。
- #setConfigLocations(String[] configLocations):设置Spring配置文件地址,一般情况下,配置文件地址是相对于Web根目录的地址,如/WEB-INF/smart-dao.xml,/WEB-INF/smart-service.xml等,
用户也可以使用带资源类型前缀的地址,如classpath:com/smart/beans.xml
WebApplicationContext初始化
WebApplicationContext初始化方式和BeanFactory、ApplicationContext有所区别,因为WebApplicationContext需要ServletContext实例,它必须在拥有Web容器的前提下才能完成启动工作。可以在web.xml中配置自启动的Servlet或定义Web容器监听器(ServletContextListener),借助二者中的任何一个,就可以完成SpringWeb应用上下文的工作。
Spring分别提供了用于启动WebApplicationContext的Servlet和Web容器监听器
- org.springframework.web.context.ContextLoaderListener
- org.springframework.web.context.ContextLoaderServlet(Spring3.0移除)
web.xml配置ContextLoaderListener
<!-- 配置spring核心监听器,默认会以 /WEB-INF/applicationContext.xml作为配置文件 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- contextConfigLocation参数用来指定Spring的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
ContextLoaderServlet
在不支持容器监听器的低版本web容器中,可采用ContextLoaderServlet完成相同的工作
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
<servlet>
<servlet-name>springContextLoaderServlet</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<loader-on-startup>1</loader-on-startup>
</servlet>
由于WebApplicationContext 需要使用日志功能,可将log4j的配置文件放置在类路径 WEB-INF/classes下
父子容器
通过HierarchicalBeanFactory接口,Spring的IOC容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的bean,但父容器不能访问子容器中的bean。在容器内,bean的IDb必须是唯一的,但子容器可以拥有和父容器id相同的Bean。
父子容器层级体系增强了Spring容器架构的扩展性和灵活性,以为第三方可以通过编程的方式为一个已经存在的容器添加一个或多个特殊用途的子容器,以提供一些额外的功能。
Spring使用父子容器实现了很多功能,比如在SpringMVC中,展现层Bean位于一个子容器中,而业务层和持久层bean位于父容器中,这样展现层就可以引用业务层和持久层的Bean,而业务层和持久层Bean则看不到展现层bean。
Bean的生命周期
Web容器中的Servlet拥有明确的生命周期,Spring容器中的Bean也拥有相似的生命周期。Bean的生命周期由多个特定的生命阶段组成,每个阶段都开出了一扇门(实现方法),允许外界借此对Bean进行控制。
Spring可以从两方面定义Bean的生命周期:第一个层面是Bean的作用范围;第二个层面是实例化Bean时所经历的一系列阶段。下面对BeanFactory和ApplicationContext中Bean的生命周期进行分析。
BeanFactory中Bean的生命周期
- 生命周期图解
- BeanPostProcessor在Spring框架中占据着重要的地位,为容器提供对Bean进行后续加工处理的切入点,Spring容器所提供的各种“神奇功能”(如AOP,动态代理等)都通过BeanPostProcessor实施。
- Bean的完整生命周期从Spring容器着手实例化Bean,直到最终销毁Bean。其中经过了许多关键点,每个关键点都涉及特定的方法调用,可以将这些方法划分为四类。
- Bean自身的方法:如调用Bean构造函数实例化Bean、调用Setter设置Bean的属性值及通过
的init-method和destroy-method所指定的方法。 - Bean级生命周期接口方法:如:BeanNameAware、BeanFactoryAware、InitializingBean和DisposableBean,这些接口方法由Bean类直接实现。
- 容器级生命周期接口方法:在图中带*号的步骤是由InstantiationAwareBeanPostProcessor和BeanPostProcessor这两个接口实现的,一般称它们为“后处理器”。后处理器一般不由Bean本身实现,它们独立于Bean,实现类以容器附件装置的形式注册到Spring中,并通过接口反射为Spring容器扫描识别。当Spring容器创建任何Bean的时候,这些后处理器都会发生作用,所以这些处理器的影响是全局性的。当然,用户可以通过合理地编写后处理器,让其仅对感兴趣的bean进行加工处理。
- 工厂后处理接口方法:包括AspectKWearingEnabler,CustomAutowireConfigure、ConfigurationClassPostProcessor等方法。工厂后处理器也是容器级别的,在应用上下文装配文件后立即调用。
Bean级生命周期接口和容器级生命周期接口是个性和共性辩证统一思想的体现。前者解决Bean个性化处理的问题,后者解决容器中某些Bean共性化处理的问题。
InstantiationAwareBeanPostProcessor其实是BeanPostProcessor接口的子接口,Spring为其提供了一个适配器类InstantiationAwareBeanPostProcessorAdapter,一般情况下,可以方便地扩展该适配器覆盖感兴趣的方法定义其实现类。
- 关于Bean生命周期接口的讨论
通过实现Spring的Bean生命周期接口对Bean进行额外控制,虽然让Bean具有了更细致的生命周期阶段,但也带来了一个问题:Bean和Spring框架紧密地绑定在一起,这个Spring一直推崇的“不对应用程序类做任何限制”的理念是相悖的。因此,如果用户希望将业务类完全POJO化,则可以只实现自己的业务接口,不需要和某个特定框架(包括Spring框架)的接口关联。可以通过的init-method和destroy-method属性配置方式为Bean指定初始化和销毁的方法,采用这种方式对Bean生命周期的控制效果和通过实现InitializingBean和DisposableBean接口所达到的效果是完全相同的。采用前者的方式可以使Bean不需要和特定的Spring框架接口所绑定,达到了框架解耦的目的。
此外,Spring还拥有一个Bean后置处理器InitDestroyAnnotationBeanPostProcessor,它负责对标注了@PostConstruct@PreDestroy的Bean进行处理,在Bean初始化后及销毁前执行相应的逻辑。喜欢注解的同学,可以通过InitDestroyAnnotationBeanPostProcessor达到和以上两种方式相同的效果(如果在ApplicationContext中,则已经默认 装配了该处理器)。
对于BeanFactoryAware和BeanNameAware接口,前者让Bean感知容器(BeanFactory实例),而后者让Bean获得配置文件中对应的配置名称。一般情况下,用户不需要关心这两个接口。如果Bean希望获取容器中的其他Bean,则可以通过属性注入的方式引用这些Bean;如果Bean希望在运行期获知在配置文件中的Bean名称,则可以简单地将名称作为属性注入。
综上,除非编写一个基于Spring之上的扩展插件或子项目之类的东西,否则用户完全可以抛开以上4个Bean生命周期的接口类,使用更好的方案替代之。
但BeanPostProcessor接口却不一样,它不要求Bean去继承它,完全可以像插件一样注册到Spring容器中,为容器提供额外功能。Spring容器充分利用了BeanPostProcessor对Bean进行加工处理,当学习Spring的AOP功能时,会对此进行分析,了解BeanPostProcessor对Bean的影响,对于深入了解Spring核心功能的工作机理有很大帮助。很多Spring扩展插件或Spring子项目都是使用这些后处理器完成让人激动的功能。
ApplicationContext中Bean的生命周期
1、生命周期图
2、生命周期的流程
Bean在应用上下文中的生命周期和在BeanFactory中的生命周期类似,不同的是,如果Bean实现了org.springframework.context.ApplicationContextAware接口,则会增加一个调用该接口方法setApplicationContext(ApplicationContext applicationContext) 的步骤,如上图。
此外还增加了BeanFactoryPostProcessor对配置信息进行加工处理。Spring框架还提供了多个工厂后处理器,如CustomEditorConfigurer、PropertyPlaceholderConfigurer。
ApplicationContext和BeanFactory另一个最大的不同之处在于:前者会利用Java反射机制自动识别出配置文件中定义的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,并将它们自动注册到应用上下文中;而后者需要在代码中通过手工调用addBeanPostProcessor()方法进行注册。这也是为什么在应用开发中普遍使用ApplicationContext而很少使用BeanFactory的原因之一。
在ApplicationContext中,只需在配置文件中通过
3、使用工厂后处理器实例
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) throws BeansException {
BeanDefinition bd = bf.getBeanDefinition("car");
bd.getPropertyValues().addPropertyValue("brand", "奇瑞QQ");
System.out.println("调用MyBeanFactoryPostProcessor.postProcessBeanFactory()!");
}
}
xml配置
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean id="car" class="com.smart.Car"
p:brand="红旗CA72"
p:maxSpeed="200"/>
<!--注册Bean后处理器-->
<bean id="myBeanPostProcessor" class="com.smart.context.MyBeanPostProcessor"/>
<!--工厂后处理器-->
<bean id="myBeanFactoryPostProcessor" class="com.smart.context.MyBeanFactoryPostProcessor"/>
</beans>
BeanPostProcessor和BeanFactoryPostProcessor会自动被ApplicationContext识别并注册到容器中。