Spring学习5(2)
基本介绍
Spring通过配置文件来描述Bean和Bean之间的依赖关系,利用java来对Bean实例化并建立Bean之间的依赖关系。
BeanFactory是spring框架最核心的接口,它提供了高级IoC的配置机制,它使管理不同类型的java对象成为可能。
ApplicationContext建立在BeanFactory基础之上,提供了更多面向应用的功能,更易于创建实际应用。
一般称BeanFactory为IoC容器,而ApplicationContext为应用上下文,但有时也叫ApplicationContext为spring容器。
对于二者的区别是BeanFactory是spring框架的基础,面向spring本身;ApplicationContext面向spring框架的开发者,基本开发场景都使用ApplicationContext。
BeanFactory
beanfactory是一个类工厂,但和传统的类工厂不同,它是类的通用工厂,所有可以被spring容器实例化并管理的java类都可以成为spring的bean。
BeanFactory的类体系结构
BeanFactory是不断的继承发展而来从而形成了一个类的结构树,BeanFactory这个接口就位于类结构树的顶端,其主要方法就是getBean(String beanName)来获得特定名称的Bean。它的功能通过其他接口而不断的扩展。
下面这张图是书中给出的结构图:
这其中有如下的接口:
- ListableBeanFactory:定义了访问容器中Bean基本信息的若干方法,如查看Bean的个数,容器中是否包含这某一个特定的Bean等。
- HierarchicalBeanFactory:父子级联IoC容器的接口,子容器可以通过接口访问父容器。
- ConfigurableBeanFactory:增强了IoC容器的可定制性,定义了设置类装载器,属性编辑琦等方法。
- AutoWirecapableBeanFactory:定义了将容器中的Bean按某种规则进行自动装配的方法。
- SingletonBeanRegistry:定义了允许在运行期间向容器注册单实例Bean的方法。
- BeanDefinitionRegistry:Spring配置文件中的每一个
<bean>
节点元素在spring容器中都通过一个BeanDefinition对象表示,它描述了Bean的配置对象信息。而这个接口提供了向容器手工注册BeanDefinition对象的方法。
初始化BeanFactory
使用spring配置文件来提供配置信息,然后通过BeanFactory装配配置文件,最后启动springIoC容器。
这里我们通过上一节Car的例子来说明。首先将Car放到com.smart包下,在resourcecs/com/smart/beanfactory下创建beans.xml文件进心配置,其中代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.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>
接下来就是利用BeanFactory来实现容器的启动,这里注意的是不要用XmlBeanFactory了,这个接口已经被弃用了,我们使用XmlBeanDefinitionReader,DefaultListableBeanFactory实现类启动spring的IoC容器。代码如下:
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.println(res.getURL());
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
Car car = factory.getBean("car1", Car.class);
car.introduce();
}
}
这里XmlBeanDefinitionReader通过Resource装载配置信息而后就可以通过BeanFactory.getBean(beanName)
来获取Bean,并且启动容器,不过此时不会去初始化Bean,只有在调用后才会初始化,对于单实例的Bean,BeanFactory会缓存实例。
在初始化BeanFactory时,必须提供一种日志框架,书中采用Log4J的形式否则容器会报错。
注意这里getBean获取的是"car1"也就是和xml中对应起来。
ApplicationContext介绍
如果说BeanFactory是Spring的心脏那么ApplicationContext就是spring的身躯。很多在BeanFactory中以编程实现的功能在ApplicationContext中都能用配置的方式的实现。
ApplicationContext类体系结构
ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者是靠类路径来加载,后者是利用系统路径来加载。
同样于BeanFactory,ApplicationContext有一个类继承体系,不过在经历了上一小节的学习后,个人认为这个暂时还是先看看就好,下面给出书上的图:
基本初始化
和BeanFactory初始化类似,ApplicationContext的初始化也很简单代码如下:(代码中beans的位置可以自己创建文件夹而后把上一节的beans中的内容分配过去)
package com.smart.context;
import org.testng.annotations.*;
import com.smart.Car;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.testng.Assert.*;
public class ApplicationContextTest{
@Test
public void getBean() throws Throwable{
ApplicationContext ctx1 =
new ClassPathXmlApplicationContext("com/smart/context/beans.xml");
ApplicationContext ctx2 =
new FileSystemXmlApplicationContext("E:/work_java/chapter4/src/main/resources/com/smart/context/beans.xml");
Car car1 = ctx1.getBean("car1", Car.class);
car1.introduce();
Car car2 = ctx2.getBean("car1", Car.class);
car2.introduce();
}
}
注意这里的路径都是自动加上"classpath:"和“file:”前缀的,当然也可以手动加上去。
这里有一个坑点就是你要理解"file:"的含义,它是关于系统的路径,千万不要和书上那样去和类路径写成一样。
除此之外,同样也可以指定一组配置文件,Spring会自动将多个配置文件在内存中整合成一个配置文件,代码如下:
ApplicationContext cxt3 =
new ClassPathXmlApplicationContext(
new String[]{"com/smart/beans1.xml","com/smart/beans2.xml"});
注意这两个配置文件中的Bean id不能相同,例如我在复制过去以后将一份代码中的car1
改成了car2
。
ApplicationContext和BeanFactory的初始化和函数虽然类似但是其有一个重大区别,ApplicationContext会在初始化应用上下文的时候就实例化所有的单实例Bean。
类注解配置
spring目前还支持基于类注解的配置方式,其来源于spring的JavaConfig。利用@Configuration标注就可以提供所需要的配置。
直接在com.smart.context包下(这些个包也自己创建,下面不再提醒)创建Beans.java,其中代码如下:
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 = "car3")
public Car buildCar() {
Car car = new Car();
car.setBrand("红旗CA72");
car.setMaxSpeed(200);
return car;
}
}
对于这种注解类的配置,spring提供了专门的实现类:AnnotationConfigApplicationContext
,其初始化以及测试代码如下:
ApplicationContext cxt4 =
new AnnotationConfigApplicationContext(Beans.class);
Car car3 = cxt4.getBean("car3", Car.class);
assertNotNull(car3);//断言是否为空
car3.introduce();
基于Groovy DSL的配置
Spring4.0支持使用Groovy DSL来进行配置,使用Groovy脚本语言,实现及其灵活的配置逻辑。
首先需要在pom.xml中添加依赖:
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>${groovy.version}</version>
</dependency>
我们在resourcs/com/smart/context创建groovy-beans.groovy,代码如下:
package com.smart.context;
import com.smart.Car;
beans{
car5(Car){//名字(类型)
brand = "红旗CA72"
maxSpeed = "200"
color = "red"
}
}
spring为基于Groovy的配置提供了专门的ApplicationContext实现类:GenericGroovyApplicationContext,其初始化代码如下:
ApplicationContext ctx5 =
new GenericGroovyApplicationContext("com/smart/context/groovy-beans.groovy");
Car car5 = (Car) ctx5.getBean("car5");
WebApplicationContext类体系结构
WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件。
从WebApplicationContext中可以获得ServletContext的引用,将整个web应用上下文对象作为属性放置到ServletContext中,以便Web应用环境访问。
为此spring专门提供了一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc)
方法就可以获取WebApplicationContext实例了,示例代码如下:
WebApplicationContext wac = (WebApplicationContext)servletContext
.getAttribute(
WebApplicationContext
.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
在Web应用环境下,Bean比普通的环境多了三个新的作用域:request,session,global session。
WebApplicationContext初始化
WebApplicationCOntext的初始化方式和上述其它两种不同,因为WebApplicationContext需要ServletContext实例,即是它必须在拥有Web容器的前提下才能完成或启动工作。我们可以在web.xml中配置自启动的Servlet或定义Web容器监听器(ServletContextListener)来完成启动工作。
启动方式
spring提供了org.springframework.web.context.ContextLoaderListerner
和org.springframework.web.context.ContextLoaderServlet
来支持Web容器监听器。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 指定配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/smart-dao.xml, /WEB-INF/smart-service.xml
</param-value>
</context-param>
<!-- 1.声明Web容器监听器 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- 2.申明自启动的Servlet-->
<servlet>
<servlet-name>
springContextLoaderServlet
</servlet-name>
<servlet-class>
org.springframework.web.context.ContextLoaderServlet
</servlet-class>
<!-- 启动顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
</web-app>
可以通过Web容器上下文参数contextConfigLocation获取spring配置文件的位置并且可以指定多个配置文件,可以用逗号,空格或者冒号分开即可表示。
这里的配置文件路径是相对于web的部署根目录路径的,当然也可以使用如"classpath"这种更方便的格式。
日志配置文件
由于WebApplicationContext需要日志共能,所以我们需要把Log4J的配置文件放置到WEB-INF/classes目录下,这样Log4J可以顺利启动。
当然,日志配置文件也可以放到其他目录中,不过需要在web.xml中指定文件位置。spring为启动Log4J也提供了两种实现类,不过都需要保证先装载日志文件再装载spring配置文件。示例代码如下:
<!-- 指定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.util.Log4jConfigServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 2.申明自启动的Servlet-->
<servlet>
<servlet-name>
springContextLoaderServlet
</servlet-name>
<servlet-class>
org.springframework.web.context.ContextLoaderServlet
</servlet-class>
<!-- 启动顺序 -->
<load-on-startup>2</load-on-startup>
</servlet>
注意这里的<load-on-startup>
就代表着装载顺序如果是Listerner来配置就要将Log4J的配置放在ContextLoaderListener之前。
使用@Configuration
配置时的启动
如果采用@Configuration
进行配置,需要通过指定context参数,让Springs使用AnnotationConfigWebApplicationContext
而不是xmlWebApplicationContext
进行配置,那么web.xml的配置代码如下:
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.smart.AppConfig1, com.smart.AppConfig2
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Groovy配置启动
与使用注解的方式类似,只不过在contextClass的配置中将<param-value>
的值改成org.springframework.web.context.support.GroovyWebApplicationContext
。