前言
接口 BeanFactory 和 ApplicationContext 都是用来从容器中获取 Spring beans
的,但是,他们二者有很大不同。
什么是 Spring Bean
之前一篇文章: Spring Bean生命周期 中有详细说明。
通常来说,Spring beans 就是被 Spring 容器所管理的 Java 对象。
package com.fhp;
public class HelloWorld { private String message; public void setMessage(String message){ this.message = message; } public void getMessage(){ System.out.println("My Message : " + message); }
}
在基于 XML 的配置中, beans.xml 为 Spring 容器管理 bean 提供元数据。
什么是 Spring 容器
Spring 容器负责实例化,配置和装配 Spring beans,下面来看如何为 IoC 容器配置我们的 HelloWorld POJO。
<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation = "http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd"><bean id = "helloWorld" class = "com.fhp.HelloWorld"><property name = "message" value = "Hello World!"/></bean>
</beans>
现在,它已经被 Spring 容器管理了,接下来的问题是:我们怎样获取它?
ApplicationContext(应用上下文):继承BeanFactory接口,简单来说就是Spring中的容器,是Spring的一个更高级的容器。可以用来获取容器中的各种bean组件,注册监听事件,加载资源文件等功能。
BeanFactory:是Spring里面最底层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;
BeanFactory 接口:
这是一个用来访问 Spring 容器的 root 接口,要访问 Spring 容器,我们将使用 Spring 依赖注入功能,使用 BeanFactory 接口和它的子接口。
通常情况,BeanFactory 的实现是使用懒加载的方式,这意味着 beans 只有在我们通过 getBean() 方法直接调用它们时才进行实例化。
实现 BeanFactory 最常用的 API 是 XMLBeanFactory,如下图:
package fhp;
import org.springframework.core.io.ClassPathResource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
public class HelloWorldApp { public static void main(String[] args) { XmlBeanFactory factory = new XmlBeanFactory (new ClassPathResource("beans.xml")); HelloWorld obj = (HelloWorld) factory.getBean("helloWorld"); obj.getMessage(); }
}
ApplicationContext 接口:
ApplicationContext 是 Spring 应用程序中的中央接口,用于向应用程序提供配置信息。它继承了 BeanFactory 接口,所以 ApplicationContext 包含 BeanFactory 的所有功能以及更多功能!它的主要功能是支持大型的业务应用的创建。
与 BeanFactory 懒加载的方式不同,它是预加载,所以,每一个 bean 都在 ApplicationContext 启动之后实例化。
package fhp;
import org.springframework.core.io.ClassPathResource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
public class HelloWorldApp{ public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml"); HelloWorld obj = (HelloWorld) context.getBean("helloWorld"); obj.getMessage(); }
}
特性:
- Bean instantiation/wiring
- Bean 的实例化/串联
- 自动的 BeanPostProcessor 注册
- 自动的 BeanFactoryPostProcessor 注册
- 方便的 MessageSource 访问(i18n)
- ApplicationEvent 的发布
1. 国际化(MessageSource)
<!- 资源国际化测试 ->
< beans>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames"> <list> <value>org/rjstudio/spring/properties/messages</value> </list> </property>
</bean>
< /beans>
org/rjstudio/spring/properties/messages 是指org.rjstudio.spring.proerties包下的以messages为主要名称的properties文件:
- messages_en_US.properties
- messages_zh_CN.properties
- messages_zh_HK.properties
2. 访问资源,如URL和文件(ResourceLoader)
对资源文件(如:properties)进行存取操作的功能
ApplicationContext acxt =new ClassPathXmlApplicationContext("/applicationContext.xml");
通过虚拟路径来存取(classpath路径:存放.class等编译后文件的路径)
Resource resource = acxt.getResource(“classpath:messages_en_CN.properties”);
通过绝对路径存取资源文件
Resource resource = acxt.getResource(“file:F:/testwork/MySpring/src/messages_en_CN.properties”);
相对路径读取资源文件
Resource resource = acxt.getResource("/messages_en_CN.properties");
3. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
applicationContext.xml(主文件,包括JDBC配置,hibernate.cfg.xml,与所有的Service与DAO基类)
applicationContext-cache.xml(cache策略,包括hibernate的配置)
applicationContext-jmx.xml(JMX,调试hibernate的cache性能)
applicationContext-security.xml(acegi安全)
applicationContext-transaction.xml(事务)
moduleName-Service.xml
moduleName-dao.xml
< beans>
< import resource=“applicationContext-cache.xml”/>
< /beans>
4. 消息发送、响应机制(ApplicationEventPublisher)
ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。 如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。
两个重要成员
ApplicationEvent类:容器事件,必须由ApplicationContext发布;
ApplicationListener接口:监听器,可由容器中的任何监听器Bean担任。
例:
1.1. 定义容器事件 EmailEvent extends ApplicationEvent
1.2. 定义监听器 EmailNotifierListener implements ApplicationListener(容器事件的监听器类必须实现ApplicationListener接口)
1.3. 将监听器注入到spring容器
1.4. 测试:
public class SpringTest {
public static void main(String arg[]){ //读取Spring容器的配置文件 @SuppressWarnings("resource") ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml"); //创建一个事件对象 EmailEvent emailEvent = new EmailEvent("hello Spring!", "cxg@126.com", "This is SpringApplicatoinContext test!"); //主动触发事件监视机制 applicationContext.publishEvent(emailEvent);
}
}
5. AOP(拦截器)
一般拦截器都是实现HandlerInterceptor,其中有三个方法preHandle、postHandle、afterCompletion
- preHandle:执行controller之前执行
- postHandle:执行完controller,return modelAndView之前执行,主要操作modelAndView的值
- afterCompletion:controller返回后执行
例:
1.1 注册拦截器,并且确定拦截器拦截哪些URL
< bean id=“validateSystemUserSessionInterceptor” class=“com.cherrypicks.appsdollar.cms.interceptor.ValidateSystemUserSessionInterceptor” />
<!-- Interceptors -->
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <mvc:exclude-mapping path="/login" /> <mvc:exclude-mapping path="/logout" /> <!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 --> <ref bean="validateSystemUserSessionInterceptor" /> </mvc:interceptor>
</mvc:interceptors>
1.2. 定义拦截器实现类
public class ValidateSystemUserSessionInterceptor extends HandlerInterceptorAdapter {
private final Log logger = LogFactory.getLog(this.getClass()); @Autowired
private CmsUserSessionService userSessionService; @Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception { logger.debug("ValidateUserSessionInterceptor.preHandle run...."); final String userIdStr = request.getParameter(Constants.USERID); final String sessionId = request.getParameter(Constants.SESSIONID); if (!StringUtils.isNotBlank(userIdStr) || !StringUtils.isNotBlank(sessionId)) { throw new InvalidUserSessionException( "Invalid user session. userId[" + userIdStr + "], sessionId[" + sessionId + "]"); } final Long userId = Long.parseLong(userIdStr); // validate userId and sessionId if (!userSessionService.validateUserSession(userId, sessionId)) { throw new InvalidUserSessionException( "Invalid user session. userId[" + userId + "], sessionId[" + sessionId + "]"); } return true;
}
1.3. 测试
public static void main(final String[] args) {
final String i = "a"; System.out.println(StringUtils.isNotBlank(i));
}
小结:
ApplicationContext在启动的时候就把所有的Bean全部实例化。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;
延迟实例化的优点:(BeanFactory)
- 应用启动的时候占用资源很少;对资源要求较高的应用,比较有优势;
不延迟实例化的优点: (ApplicationContext)
- 所有的Bean在启动的时候都加载,系统运行的速度快;
- 在启动的时候所有的Bean都加载了,我们就能在系统启动的时候,尽早的发现系统中的配置问题
- 建议web应用,在启动的时候就把所有的Bean都加载了。(把费时的操作放到系统启动中完成)
SpringBoot中获取ApplicationContext的三种方式:
一、 直接使用Autowired注入
二、利用 spring4.3 的新特性
三、实现spring提供的接口 ApplicationContextAware
总结
ApplicationContext 包含 BeanFactory 的所有特性,通常推荐使用前者。但是也有一些限制情形,比如移动应用内存消耗比较严苛,在那些情景中,使用更轻量级的 BeanFactory 是更合理的。然而,在大多数企业级的应用中,ApplicationContext 是你的首选。