欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

BeanFactory和ApplicationContext的区别

程序员文章站 2022-05-24 12:31:42
...

疑问:

我看到过很多问 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));  
    }  
}  

 

相关标签: java面试