面试还不知道BeanFactory和ApplicationContext的区别?

前言

接口 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/beans
   http://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

  1. preHandle:执行controller之前执行
  2. postHandle:执行完controller,return modelAndView之前执行,主要操作modelAndView的值
  3. 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)

  1. 应用启动的时候占用资源很少;对资源要求较高的应用,比较有优势;

不延迟实例化的优点: (ApplicationContext)

  1. 所有的Bean在启动的时候都加载,系统运行的速度快;
  2. 在启动的时候所有的Bean都加载了,我们就能在系统启动的时候,尽早的发现系统中的配置问题
  3. 建议web应用,在启动的时候就把所有的Bean都加载了。(把费时的操作放到系统启动中完成)

SpringBoot中获取ApplicationContext的三种方式:

一、 直接使用Autowired注入
在这里插入图片描述

二、利用 spring4.3 的新特性
在这里插入图片描述

三、实现spring提供的接口 ApplicationContextAware
在这里插入图片描述


总结

ApplicationContext 包含 BeanFactory 的所有特性,通常推荐使用前者。但是也有一些限制情形,比如移动应用内存消耗比较严苛,在那些情景中,使用更轻量级的 BeanFactory 是更合理的。然而,在大多数企业级的应用中,ApplicationContext 是你的首选。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值