BeanFactory和ApplicationContext的区别
疑问:
我看到过很多问 BeanFactory 和 ApplicationContext 不同点的问题,
考虑到这,我应该使用前者还是后者从 Spring 容器中获取 beans 呢?
spring基本功:BeanFactory和ApplicationContext的区别
接口 BeanFactory 和 ApplicationContext 都是用来从容器中获取 Spring beans 的,但是,他们二者有很大不同
(1)什么是 Spring Bean
这是一个非常简单而又很复杂的问题,通常来说,Spring beans 就是被 Spring 容器所管理的 Java 对象,来看一个简单的例子
public class myTest {
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 提供元数据
(2)什么是 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/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id = "helloWorld" class = "com.test.myTest">
<property name = "message" value = "Hello World!"/>
</bean>
</beans>
现在,它已经被 Spring 容器管理了,接下来的问题是:我们怎样获取它?
BeanFactory 和 ApplicationContext 的不同点
这是一个用来访问 Spring 容器的 root 接口,要访问 Spring 容器,我们将使用 Spring 依赖注入功能,使用 BeanFactory 接口和它的子接口特性:
-
Bean 的实例化/串联
通常情况,BeanFactory 的实现是使用懒加载的方式,这意味着 beans 只有在我们通过 getBean() 方法直接调用它们时才进行实例化实现 。
BeanFactory 最常用的 API 是 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 的所有功能以及更多功能!它的主要功能是支持大型的业务应用的创建特性:
-
Bean instantiation/wiring
-
Bean 的实例化/串联
-
自动的 BeanPostProcessor 注册
-
自动的 BeanFactoryPostProcessor 注册
-
方便的 MessageSource 访问(i18n)
-
ApplicationEvent 的发布
与 BeanFactory 懒加载的方式不同,它是预加载,所以,每一个 bean 都在 ApplicationContext 启动之后实例化
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
总结
ApplicationContext 包含 BeanFactory 的所有特性,通常推荐使用前者。但是也有一些限制情形,比如移动应用内存消耗比较严苛,在那些情景中,使用更轻量级的 BeanFactory 是更合理的。然而,在大多数企业级的应用中,ApplicationContext 是你的首选。
拓展
转:http://youyu4.iteye.com/blog/2346183
BeanFactory:
是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;
ApplicationContext:
应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;
1) 国际化(MessageSource)
2) 访问资源,如URL和文件(ResourceLoader)
3) 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
4) 消息发送、响应机制(ApplicationEventPublisher)
5) AOP(拦截器)
两者装载bean的区别
BeanFactory:BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;
ApplicationContext:ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;
我们该用BeanFactory还是ApplicationContent
延迟实例化的优点:(BeanFactory)
应用启动的时候占用资源很少;对资源要求较高的应用,比较有优势;
不延迟实例化的优点: (ApplicationContext)
1. 所有的Bean在启动的时候都加载,系统运行的速度快;
2. 在启动的时候所有的Bean都加载了,我们就能在系统启动的时候,尽早的发现系统中的配置问题
3. 建议web应用,在启动的时候就把所有的Bean都加载了。(把费时的操作放到系统启动中完成)
spring国际化例子(MessageSource)
1. 在xml中配置messageSource
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<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>
2. “org/rjstudio/spring/properties/messages”,是指org.rjstudio.spring.proerties包下的以messages为主要名称的properties文件
文件如下:
messages_en_US.properties
messages_zh_CN.properties
messages_zh_HK.properties
3. 取值的时候是通过ApplicationContext.getMessage(),拿到对应语言的内容
public class MessageTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("messages.xml");
Object[] arg = new Object[] { "Erica", Calendar.getInstance().getTime() };
String msg = ctx.getMessage("userinfo", arg,Locale.CHINA);
System.out.println("Message is ===> " + msg);
}
}
spring访问资源(ResourceLoader)
这是spring对资源文件(如:properties)进行存取操作的功能
ApplicationContext acxt =new ClassPathXmlApplicationContext("/applicationContext.xml");
1.通过虚拟路径来存取。当资源位于CLASSPATH路径下时,可以采用这种方式来存取。
Resource resource = acxt.getResource("classpath:messages_en_CN.properties");
2.通过绝对路径存取资源文件。
Resource resource = acxt.getResource("file:F:/testwork/MySpring/src/messages_en_CN.properties");
3.相对路径读取资源文件。
Resource resource = acxt.getResource("/messages_en_CN.properties");
Resource常用的方法:
getFilename() : 获得文件名称
contentLength() : 获得文件大小
createRelative(path) : 在资源的相对地址上创建新文件
exists() : 是否存在
getFile() : 获得Java提供的File 对象
getInputStream() : 获得文件的流
spring载入多个上下文
不同项目使用不同分模块策略,spring配置文件分为
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
两种方法配置
1.可以在applicationContext.xml文件中引用
<beans></beans>标记之间引入其他applicationContext.xml
<beans>
<import resource="applicationContext-cache.xml"/>
</beans>
2.或者在web.xml文件中引用
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
WEB-INF/classes/applicationContext-security.xml
,WEB-INF/classes/applicationContext-dao.xml
,WEB-INF/classes/applicationContext-Service.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
spring事件机制(订阅发布模式 == 观察者模式)
ApplicationContext事件机制是观察者设计模式的 实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。 如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。
两个重要成员
ApplicationEvent:容器事件,必须由ApplicationContext发布;
ApplicationListener:监听器,可由容器中的任何监听器Bean担任。
1. 定义容器事件
package com.cxg.test.springPlatfrom;
import org.springframework.context.ApplicationEvent;
/**
* Title: email之事件类
* EmailEvent类继承了ApplicationEvent类,除此之外,它就是一个普通的Java类
* Description: dataPlatfrom
* @author: xg.chen
* @date:2016年8月24日
*/
public class EmailEvent extends ApplicationEvent{
private static final long serialVersionUID = 1L;
//属性
private String address;
private String text;
//构造方法
public EmailEvent(Object source) {
super(source);
}
public EmailEvent(Object source, String address, String text) {
super(source);
this.address = address;
this.text = text;
}
//getter和setter设置
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
2. 定义监听器
package com.cxg.test.springPlatfrom;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
/**
* Title: email之监听类
* 容器事件的监听器类必须实现ApplicationListener接口,实现该接口就必须实现
* Description: dataPlatfrom
* @author: xg.chen
* @date:2016年8月24日
*/
public class EmailNotifier implements ApplicationListener<ApplicationEvent>{
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof EmailEvent){
EmailEvent emailEvent = (EmailEvent) event;
System.out.println("email's address:"+emailEvent.getAddress());
System.out.println("email's text:"+emailEvent.getText());
} else {
System.out.println("the Spring's event:"+event);
}
}
}
3.将监听器注入到spring容器
<!-- 配置事件监听 -->
<bean class="com.cxg.test.springPlatfrom.EmailNotifier" />
4. 测试
package com.cxg.test.springPlatfrom;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Title: Spring的ApplicationContexet单元成测试
* Description: dataPlatfrom
* @author: xg.chen
* @date:2016年8月24日
*/
public class SpringTest {
public static void main(String arg[]){
//读取Spring容器的配置文件
@SuppressWarnings("resource")
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
//创建一个事件对象
EmailEvent emailEvent = new EmailEvent("hello Spring!", "[email protected]", "This is SpringApplicatoinContext test!");
//主动触发事件监视机制
applicationContext.publishEvent(emailEvent);
}
}
spring的AOP(常用的是拦截器)
一般拦截器都是实现HandlerInterceptor,其中有三个方法preHandle、postHandle、afterCompletion
1. preHandle:执行controller之前执行
2. postHandle:执行完controller,return modelAndView之前执行,主要操作modelAndView的值
3. afterCompletion:controller返回后执行
实现步骤:
1. 注册拦截器,并且确定拦截器拦截哪些URL
<!-- Check Session -->
<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>
<!-- SpringMVC.end} -->
2. 定义拦截器实现类
package com.cherrypicks.appsdollar.cms.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.cherrypicks.appsdollar.common.constant.Constants;
import com.cherrypicks.appsdollar.common.exception.InvalidUserSessionException;
import com.cherrypicks.appsdollar.service.cms.CmsUserSessionService;
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;
}
public static void main(final String[] args) {
final String i = "a";
System.out.println(StringUtils.isNotBlank(i));
}
}
下一篇: PHP获取页面最后修改时间的实现方法。