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配置信息初始化容器。