Spring:BeanFactory和ApplicationContext

    Spring通过一个配置文件描述Bean及Bean之间的依赖关系,利用Java的反射功能实例化Bean并建立Bean之间的依赖关系。
Spring的Ioc容器在完成这些底层工作的基础上,还提供了Bean实例缓存、生命周期管理、Bean实例代理、事件发布、资源装在等高级服务。
    Bean工厂(com.springframework.beans.factory.BeanFactory)是Spring框架最核心的接口,它提供了高级Ioc的配置机制。
BeanFactory使管理不同类型的Java对象成为可能,应用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory基础之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。
我们一般称BeanFactory为Ioc容器,而称ApplicationContext为应用上下文,也称为Spring容器。
    对于二者的用途,我们可以进行简单的划分:
        BeanFactory是Spring框架的基础设施,面向Spring本身;
        ApplicationContext面向使用Spring框架的开发者,几乎所有应用场合都可以直接使用ApplicationContext而非底层的BeanFactory;

BanFanFactory介绍:
    BeanFactory是一个类工厂,但和传统的类工厂不同,传统的类工厂仅负责构造一个或几个类的实例;而BeanFactory是类的通用工厂,它可以创建并管理各种累的对象。
这些可被创建和管理的对象本身没有什么特别之处,仅是一个POJO,Spring必须提供一个默认的无参构造方法、不依赖于某一个特定的容器等,但Spring中所说的Bean比JavaBean更宽泛一些,所有可以被Spring容器实例化并管理的Java类都可以成为Bean、
    1.BeanFactory的类体系结构:
        Spring为BeanFactory提供了多种实现,最常用的是XmlBeanFactory,但在Spring3.2中已被废弃,建议使用XmlBeanDefinitionReader、DefaultListableBeanFactory替代。
        BeanFactory的类集成体系设计优雅,看成经典、通过集成体系,我们可以很容易的了解到BeanFactory具有哪些功能。如图所示:

    BeanFactory接口位于类结构树的顶端,它最主要的方法就是getBean(Stirng beanName),该方法从容器中返回特定名称的Bean。
BeanFactory的功能通过其他接口得到不断扩展。
    ① ListableBeanFactory:该接口定义了访问容器中Bean基本信息的若干方法,如:查看Bean的个数、获取某一类型Bean的配置名、查看容器中是否包括某一Bean等。
    ② HierarchicalBeanFactory:父子级联Ioc容器的接口,子容器可以通过接口方法访问父容器。
    ③ ConfigurableBeanFactory:这是一个重要的接口,增强了Ioc容器的可定制性。它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法、
    ④ AutowireCapableBeanFactory:定义了将容器中的Bean按某种规则进行自动装配的方法。(按名字匹配、按类型匹配)
    ⑤ SingletonBeanRegistry:定义了允许在运行期想容器注册单实例Bean的方法、
    ⑥ BeanDefinitionRegistry:Spring配置文件中没一个<bean>节点元素在Spring容易李都通过一个BeanDefinition对象表示,它描述了Bean的配置信息、而BeanDefinitionRegistry接口提供了想容器公共注册BeanDefinition对象的方法。
2.初始化BeanFactory
    下面使用Spring配置文件WieCar提供配置信息,然后通过BeanFactory装在配置文件,启动SpringIoc容器。 

    beans.xml

    <?xml version="1.0" encoding="UTF-8" ?>
	<beans xmlns="http://www.springframework.org/schema/beans"
		   xmlns:xsi="http:www.w4.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="car1" class="com.smart.Car"
			p:brand="红旗CA72"
			p:color="黑色"
			p:maxSpeed="200"/>
	</beans>

    下面通过XmlBeanDefinitionReader、DefaultListableBeanFactory实现类启动SpringIoc容器
    BeanFactoryTest.java

package com.smart.beanfactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import com.smart.Car;
import org.testng.annotations.*;
import static org.testng.Assert.*;

public class BeanFactoryTest{
	@Test
	public void getBean()throws Throwable{
		ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
		Resource res = resolver.getResource("classpath:com/smart/beanfactory/beans.xml");
		System.out.pringln(res.getURL());
		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
		reader.loadBeanDefinitions(res);
		System.out.pringln("init BeanFactory");
		Car car = factory.getBean("car",Car.class);
		System.out.pringln("car bean is ready for use");
		car.introduce();
	}
}

    XmlBeanDefinitionReader通过Resource装在Spring配置信息并启动IOC容器,然后就可以通过BeanFactory#getBean(beanName) 方法从Ioc容器中获取Bean、
通过BeanFactory启动Ioc容器时,并不会初始化配置文件中定义的Bean,初始化动作发生在第一个调用时。
对于单实例(singleton)的Bean磊说,BeanFactory会缓存Bean实例,所以第二次使用getBean()获取Bean时,将直接从Ioc容器的缓存中获取Bean实例。
    Spring在DefaultSingletonBeanRegistry类中提供了一个用于缓存单实例Bean的缓存器,它是一个用HashMap实现的缓存器,单实例的ean以beanName为键保存在这个HashMap中、
    在初始化BeanFactory时,必须为其提供一种日志矿机,我们使用Log4J,即在类路径下提供Log4J配置文件,这样启动Spring容器才不会报错。

ApplicationContext介绍:
    如果说BeanFactory是Spring的心脏,那么ApplicationContext就是完整的身躯了。
ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能在BeanFactory中,很多功能需要以变成的方式实现,而在ApplicationContext中则可以通过配置的方式实现。
    1.ApplicationContext类体系结构
    ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中装在配置文件。
    下面了解一下ApplicationContext的类继承体系。

    ① ApplicationEventPublisher:让容器拥有发布应用上下文时间的功能,包括容器启动事件、关闭事件等。
实现了ApplicationListener事件监听接口的Bean可以接受到容器事件,并对事件进行响应处理。
在ApplicationContext抽象实现类AbstractAppplicationContext中存在一个ApplicationEventMulticaster,它负责保存所有的监听器,以便在容器产生上下文事件时通知这些事件监听者。
    ② MessageSource:为应用提供il8n国际化消息访问的功能。
    ③ ResourcePatternResolver:所有ApplicationContext实现类都实现了类似于PathMatchingResourcePatternResolver的功能,可以通过带前缀的Ant封过的资源文件路径装在Spring的配置文件。
    ④ LifeCycle:该接口同时被ApplicationContext实现及具体Bean实现,ApplicationContext会将start/stop的信息传递给容器中所有实现了该接口的Bean,以达到管理和控制JMX、任务调度等目的。
    ConfigurableApplicationContext扩展于ApplicationContext,它新增了两个主要的方法:refresh()和close(),让ApplicationContext具有启动、刷新和关闭应用上下文的能力。
在应用上下文关闭的情况下调用refresh()即可启动应用上下文,在已经启动的状态下调用refresh()则可以清楚缓存并重新装在配置信息,而调用close()则可以关闭应用上下文。
这些接口方法为容器的控制管理带来了便利,单作为开发者,我们并不需要过多关注这些方法。
    和BeanFactory初始化相似,ApplicationContext的初始化也很简单、如果配置文件防止在类路径下,则可以优先考虑使用ClassPathXmlApplicationContext实现类。

ApplicationContext ctx = new ClassPathXmlApplicationContext("com/smart/context/beans.xml");

    对于ClassPathXmlApplicationContext来说,"com/smart/context/beans.xml"等同于"classpath:com/smart/context/beans.xml"。
    如果配置文件防止在文件系统的路径下,则可以优先考虑使用FilySystemXmlApplicationContext实现类。
    ApplicationContext ctx = new FileSystemXmlApplicationContext("com/smart/context/beans.xml");
    对于FileSystemXmlApplicationContext来说,"com/smart/context/beans.xml"等同于"file:com/smart/context/beans.xml"。
    还可以指定一组配置文件,Spring会自动将多个配置文件在内存中整合成一个配置文件,如:

ApplicationContext ctx = new CliassPathXmlApplicationContext(new String[]{"conf/beans1.xml","conf/beans2.xml"});

    当然,FileSystemXmlApplicationContext和ClassPathXmlApplicationContext都可以显式使用带资源类型前缀的路径,它们的区别在于如果不显式指定资源类型前缀,则分别将路径解析为文件系统路径和类路径、
    在获取ApplicationContext实例后,就可以像BeanFactory一样调用getBean(beanName)返回Bean了、
ApplicationContext的初始化和BeanFactory有一个重大的区别:BeanFactory在初始化容器时,并未实例化Bean,知道第一次访问某个Bean时才实例化目标Bean;而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean。
因此ApplicationContext的初始化时间会比BeanFactory稍长一些,不过稍后的地用用则没有"第一次惩罚"的问题。
    Spring支持基于类注解的配置方式,主要功能来自Spring的一个名为JavaConfig的子项目、JavaConfig现已升级为Spring核心框架的一部分。一个标注@Configuration注解的POJO即可提供Spring所需要的Bean配置信息,如:

package com.smart.context;
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文件的配置方式更加灵活。
    Spring基于注解类的配置提供了专门的ApplicationContext实现类:AnnotationConfigApplicationContext。来看一个使用AnnotationConfigApplicationContext启动S任凭容器的示例。

通过带@Configuration的配置类启动容器

package com.smart.context;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.smart.Car;
import static org.textng.Assert.*;
import org.testng.annotations.*;
public class AnnotationApplicationContextTest{
	@Test
	public void getBean(){
		ApplicationContext ctx = new AnnotationConfigApplicationContext(Beans.class);
		Car car = ctx.getBean("car",Car.class);
		assertNotNull(car);
	}
}	

AnnotationConfigApplicationContext将加载Beans.class中的Bean定义并调用Beans.class中的方法实例化Bean,启动容器并装配Bean。
    Spring4.0支持使用Groovy DSL来进行Bean定义配置。其基于XML文件的配置类似,只不过基于Groovy脚本语言,可以实现复杂、灵活的Bean配置逻辑,看一个例子。

package com.smart.context;
import com.smart.Car;
beans{
	car(Car){
		brand = "红旗CA72"
		maxSpeed= "200"
		color = "red"
	}
}

    基于Groovy的配置方式可以很容易的让开发者配置复杂Bean的初始化过程,比基于XML文件、注解的配置方式更加灵活。
    Spring为基于Groovy的配置提供了专门的ApplicationContext实现类:GenericGroovyApplicationContext。
    来看一个入伙使用GenericGroovyApplicationContext启动Spring容器的示例:
通过GenericGroovyApplicationContext启动容器

package com.smart.context;
import com.smart.Car;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericGroovyApplicationContext;
import org.textng.annotations.*;
import static org.testng.Assert.*;
public class GroovyApplicationContextTest{
	@Test
	pulibc void getBean(){
		ApplicationContext ctx = new GenericGroovyApplicationContext("classpath:com/smart/context/groovy-beans.groovy");
		Car car = (Car)ctx.getBean("car");
		assertNotNull(car);
		assertEquals(car.getColor(),"red");
	}
}

2.WebApplicationContext类体系结构
    WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化工作。
从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性防止到ServletContext中,以便Web应用环境可以访问Spring应用上下文。
Spring专门为此提供了一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc)方法,可以从ServletContext中获取WebApplicationContext实例。
    在非Web应用的环境下,Bean只有singleton和prototype两种作用域。
WebApplicationContext为Bean添加了三个新的作用域:requesst、session和global session、
下面来看一下WebApplicationContext的类集成体系。

    由于Web应用比一般的应用拥有更多的特性,因此WebApplicationContext扩展了ApplicationContext。
WebApplicationContext定义了一个常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文启动时,
WebApplicationContext实例即以此为键放置在ServletContext的属性列表中,可以通过一下语句从Web容器中获取WebApplicationContext;
WebApplicationContext wac = (WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    这正是前面提到的WebApplicationContextUtils工具类getWebApplicationContext(ServletContext sc)方法的内部实现方式。
    这样,Spring的Web应用上下文和Web容器的上下文应用就可以实现互访,二者实现了融合。

    ConfigurableWebApplicationContext扩展了WebApplicationContext,它允许通过配置的方式实例化WebApplicationContext,同时定义了两个重要的方法。
    ① setServletContext(ServletContext servletContext):为Spring设置Web应用上下文以便二者整合。
    ② setConfigLocations(String[] configLocations):设置Spring配置文件地址,一般情况下,配置文件地址是相对于Web根目录的地址,如/WEB-INF/smart-dao.xml等。
但用户也可以使用带资源类型前缀的地址,如classpath:com/smart/beans.xml等。

3.WebApplicationContext初始化
    WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所区别,因为WebApplicationContext需要ServletContext实例,
ServletContext实例,也就是说,它必须在拥有Web容器的前提下才能完成启动工作。
有过Web开发经验的人都知道,可以在web.xml中配置自启动的Servlet或定义Web容器监听器(ServletContextListener),借助二者中的任何一个,就可以完成启动SpringWeb应用上下文工作。
    所有版本的Web容器都可以定义自启动的Servlet,单只有Servlet2.3及以上版本的Web容器才支持Web容器监听器。
有些及时支持Servlet2.3的Web服务器,也不能在Servlet初始化之前启动Web监听器,如Weblogic8.1、WebSphere5.x、OracleOCJ9.0。
    Spring分别提供了用于启动WebApplicationContext的Servlet和Web容器监听器;
    ① org.springframework.webcontext.ContextLoaderServlet
    ② org.springframework.web.context.ContextLoaderListener
    二者的内部都实现了启动WebApplicationContext实例的逻辑,只要根据Web容器的具体情况选择二者之一,并在web.xml中完成配置即可。
    使用ContextLoaderListener启动WebApplicationContext的具体配置

<!-- 指定配置文件位置 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/smart-dao.xml,/WEB-INF/smart-service.xml
		</param-value>
	</context-param>
	<!-- 声明Web容器监听器 -->
	<listener>
		<listener-calss>org.springframework.web.context.ContextLoaderListener</listener-calss>
	</listener>

    ContextLoaderListener通过Web容器上下文参数contextConfigLocation获取Spring配置文件的位置。用户可以指定多个配置文件,用逗号、空格或冒号分割。
对于带资源类型前缀的配置文件路径,WebApplicationContext默认这些路径相对于Web的部署根路径。
当然,也可以采用带资源类型前缀的路径配置和上面的配置是等效的如: calsspath*:/smart-*.xml
    如果在不支持容器监听器的低版本Web容器中,则可以采用ContextLoaderServlet完成相同的工作,
    通过自启动的Servlet引导

<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/smart-dao.xml,/WEB-INF/smart-service.xml
		</param-value>
	</context-param>
	<!-- 声明自启动的Servlet -->
	<servlet>
		<servlet-name>springContextLoaderServlet</servlet-name>
		<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
		<!-- 启动顺序 -->
		<load-on-startup>1</load-on-startup>
	</servlet>

    由于WebApplicationContext需要使用日志功能,所以用户可以将Log4J的配置文件放在类路径WEB-ONF/classes下,这时Log4J引擎即可顺利启动。
如果Log4J配置文件放置在其他位置,那么用户必须在web.xml中指定存放路径。
Spring为启动Log4J引擎提供了两个类似于启动WebApplicationContext的实现类:Log4jConfigServlet和Log4jConfigListener,不管采用哪种方式,
都必须保证能够装在Spring配置文件前先装在Log4J配置信息,
    指定Log4J配置文件时启动SpringWeb应用上下文

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/smart-dao.xml</param-value>
</context-param>
<!-- 指定Log4J配置文件位置 -->
<context-param>
	<param-name>log4jConfigLocation</param-name>
	<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<!-- 装在Log4J配置文件的自启动Servlet -->
<servlet>
	<servlet-name>log4jConfigServlet</servlet-name>
	<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
	<!-- 启动顺序 -->
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
	<servlet-name>springContextLoadderServlet</servlet-name>
	<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
	<!-- 启动顺序 -->
	<load-on-startup>2</load-on-startup>
</servlet>

    注意上面讲log4j的启动顺序设置为1,而讲spring的启动顺序设置为2.
这样,前者将先启动,完成装在Log4J配置文件初始化Log4J引擎的工作,紧接着后者在启动。
如果使用Web监听器,则必须将Log4JConfigListener防止在ContextLoaderListener的前面。
采用以上配置方式,Spring将自动使用XmlWebApplicationContext启动Spring容器,即通过XML文件为Spring容器提供Bean的配置信息。
    如果是用注解@Configuration的Java类提供配置信息,则web.xml需要按照以下方式配置。

	<!-- 通过制定context参数,让Spring使用AnnotationConfigWebApplicationContext而非XmlWebApplicationContext启动容器 -->
	<context-param>
		<param-name>contextClass</param-name>
		<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
	</context-param>
	<!-- 制定标注了 @Configuration 注解的配置类,多个可以使用逗号或空格分割 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>com.smart.AppConfig1,com.smart.AppConfig2</param-value>
	</context-param>
	<!-- ContextLoaderListener监听器将根据上面的配置使用AnnotationConfigWebApplicationContext根据contextConfigLocation制定的配置类启动Spring容器 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

    ContextLoaderListener如果发现配置了contextClass上下文参数,就会使用参数所制定的WebaApplicationContext实现类(AnnotationConfigWebApplicationContext)
初始化容器,该实现类会根据contextConfigLocation上下文参数制定的标注@Configuration的配置类所提供的Spring配置信息初始化容器。
    如果使用GroovyDSL配置Bean信息,则web.xml需要按一下方式配置

	<!-- 通过制定context参数,让Spring使用GroovyWebApplicationContext而非XmlWebApplicationContext或AnnotationConfigWebApplicationContext启动容器 -->
	<context-param>
		<param-name>contextClass</param-name>
		<param-value>org.springframework.web.context.support.GroovyWebApplicationContext</param-value>
	</context-param>
	<!-- 制定Groovy的配置文件  -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>Classpath*:conf/spring-mvc.groovy</param-value>
	</context-param>
	<!-- ContextLoaderListener监听器将根据上面的配置使用GroovyWebApplicationContext根据contextConfigLocation制定的配置类启动Spring容器 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

    GroovyWebApplicationContext实现类会根据contextConfigLocation上下文参数制定的conf/spring-mvc.groovy所提供的Spring配置信息初始化容器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值