`
gao_xianglong
  • 浏览: 462352 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

详解SSJ(Spring3.x mvc + Spring3.x Core + JPA2.x)轻量级集成开发—第2章 剖析IOC容器进阶

阅读更多

上一章 详解SSJ(Spring3.x mvc + Spring3.x Core + JPA2.x)轻量级集成开发—第1章 剖析IOC容器

下一章 详解SSJ(Spring3.x mvc + Spring3.x Core + JPA2.x)轻量级集成开发—第3章 剖析依赖装配参数

 

 目录

一、BeanFactory和ApplicationContext详解

二、初始化IOC容器的二种方式
三、IOC容器中Bean的生命周期

四、IOC容器中Bean的作用域

五、Bean之懒加载

六、依赖注入之参数详解

七、整合多个配置文件

 

 前言

上一章节,笔者详细的为大家讲解了关于IOC容器的基本原理以及如何构建Spring应用。你可以把本章内容当做是上一章的进阶,因为笔者在本章会详细为大家讲解关于IOC容器的一些常用配置。当然在此笔者也要感谢那些支持笔者的朋友,因为没有你们的支持就没有笔者坚持写下去的动力,在此笔者深深的谢谢你们。

 

一、BeanFactory和ApplicationContext详解
上一章其实笔者已经对BeanFactory和ApplicationContext做了大致的介绍,并且大家都已经知道IOC(控制反转)容器仅是一个抽象的概念,而BeanFactory和ApplicationContext才是它具体的容器实现。当然可能不全面,既然是这样,那么笔者有必要在本章对此进行详细的补充,以便于让不了解的朋友更能够清晰的明白它们两个彼此之间的依赖关系

先从BeanFactory开始吧。BeanFactory作为IOC容器的顶层抽象接口,从理论上来说该接口仅仅只是一个维护依赖关系建立的Bean工厂,却由它负责IOC容器的初始化工作以及提供访问Bean定义的权限。该接口处于org.springframework.beans包下,其代表派生类有XmlBeanFactory(3.1版本以后该类型已经过时)。BeanFactory接口允许使用InputStream或者Resource的方式加载IOC配置文件。

使用BeanFactory初始化IOC容器:

/* 加载Ioc配置文件 */
Resource resource = new ClassPathResource("root-context.xml");
		
/* 初始化Ioc容器 */
BeanFactory factory = new XmlBeanFactory(resource);
	
或:

/* 加载Ioc配置文件 */
InputStream in = new FileInputStream("root-context.xml");
		
/* 初始化Ioc容器 */
BeanFactory factory = new XmlBeanFactory((Resource)in);

 

BeanFactory为我们提供的方法不多,但最为常用的就是getBean()方法了,我们可以通过该方法获取定义在IOC配置文件中的指定Bean类型的实例

使用BeanFactory获取Bean实例:

public class ClientTest 
{
	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception 
	{
		/* 加载Ioc配置文件,并初始化Ioc容器 */
		Resource resource = new ClassPathResource("root-context.xml");
		BeanFactory context = new XmlBeanFactory(resource);
		
		/* 获取LoginBean实例 */
		LoginBean loginBean = (LoginBean) context.getBean("loginBean");
		System.out.println(loginBean.getUserAccount());
		System.out.println(loginBean.getPassWord());
	}
}

  

ApplicationContext其实也是派生于BeanFactory,并且还进一步扩展了BeanFactory接口。比如:更易与Spring AOP集成、国际化处理、事件传递及各种不同应用层的上下文实现。
由于ApplicationContext完全派生自BeanFactory接口,也就是说BeanFactory所具备的特征,ApplicationContext同样具备,并且还拥有一些独有特性,所以一般开发者多选择ApplicationContext作为IOC容器接口。

ApplicationContext的派生类如下:

1、ClassPathXmlApplicationContext;

2、FileSystemXmlApplicationContext;

3、XmlWebApplicationContext;

 

如上派生类中,以ClassPathXmlApplicationContext最为常用。ClassPathXmlApplicationContext是从ClassPath路径下去加载指定的IOC配置文件。FileSystemXmlApplicationContext则是用磁盘的文件路径去寻找IOC配置文件。至于XmlWebApplicationContext则是从Web应用中去寻找IOC配置文件。你不用想太多,只要记住一点选用ApplicationContext初始化IOC容器时,你选用ClassPathXmlApplicationContext派生类就可以了。

当然除了加载IOC配置文件的路径不同以外,这些派生类初始化IOC容器的方式也是各不相同的。不过不用担心的是一旦成功初始化IOC容器以后,获取指定Bean实例的方式和BeanFactory是一样的,都是采用getBean()方法的形式。

使用ApplicationContext初始化IOC容器:

/* 加载多个配置文件 */
ApplicationContext context1 = new ClassPathXmlApplicationContext(
		new String[]{"a-context.xml", "b-context.xml"});

/* 使用通配符加载多个配置文件 */
ApplicationContext context2 = new ClassPathXmlApplicationContext("*-context.xml");

 

使用Applicationcontext获取Bean实例:

public class ClientTest 
{
	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception 
	{
		/* 加载Ioc配置文件,并初始化Ioc容器 */
		ApplicationContext context = new ClassPathXmlApplicationContext(
				"root-context.xml");
		
		/* 获取LoginBean实例 */
		LoginBean loginBean = (LoginBean) context.getBean("loginBean");
		System.out.println(loginBean.getUserAccount());
		System.out.println(loginBean.getPassWord());
	}
}

 

提示:
BeanFactory和ApplicationContext还是存在一定区别的,当然这些区别并不影响你的使用。在程序中我们一旦成功初始化ApplicationContext容器后,ApplicationContext会自动实例化定义在IOC配置文件中的单例Bean实例以及自动完成参数的依赖装配。BeanFactory恰恰反之,只有当真正用到的时候BeanFactory才会对Bean进行实例以及依赖装配,也就是说BeanFactory其实很大程序上是基于懒加载(Lazy)模式的。


二、初始化IOC容器的二种方式
我们在上一章节中其实已经使用了IOC的初始方式之一,没错那就是手动初始化。你可能会想既然有手动初始化,那必然会有自动初始化?没错不过那是在Web项目中,通常都是由Web容器来负责IOC容器的自动初始化工作,无需开发人员手动干预。也就是说当Web容器启动运行时,便会自动初始化定义在web.xml文件中的IOC容器。
果我们希望由Web容器来帮助咱们实现自动初始化IOC容器的操作时,我们有两种自动实例方式可以选择:

1、ContextLoaderListener;

2、ContextLoaderServlet;

 

如果我们采用ContextLoaderListener的方式实现IOC容器的自动实例化,则需要在web.xml文件中配置如下信息:

<!-- 初始化ioc容器等相关配置 -->
<listener>
	<listener-class>org.springframework.web.context.
        ContextLoaderListener</listener-class>
</listener>
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/*-context.xml</param-value>
</context-param>

 

当你成功配置好上述文件后,一旦Web容器运行时,便会自动初始化咱们的IOC容器。上述程序我们采用监听的形式配置IOC容器,当然此方式仅限于Servlet2.4及以上规范的Servlet容器。如果你的Servlet容器规范低于2.4你可以选用ContextLoaderServlet的方式实现:

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:spring-config/root-context.xml</param-value>
</context-param>
<servlet>
	<servlet-name>context</servlet-name>
	<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
	<load-on-startup>1</load-on-startup>
</servlet>

 

提示:

当Web容器启动的时候,Listener组件的加载顺序必然优先于Servlet组件。 至于ContextLoaderServlet的出现其最大的目的还是为了满足那些低于2.4 Servlet容器规范的Web容器。

 

三、IOC容器中Bean的生命周期

我们都知道一个POJO的生命周期大致可以由3部分组成,new创建-->就绪-->GC回收,但Spring的Bean实例的生命周期却远比这个复杂,下图为Bean的生命周期示例:

 

通过上图我们可以清晰的看见,IOC容器中的Bean实例的生命周期基本可以由5部分组成。首先当IOC(ApplicationContext)容器成功初始化后,便会实例化定义在配置文件中的所有单例Bean实例,接着再对参数进行依赖装配,如果<bean/>标签中配置有“init-method”属性时,则会优先执行Bean实例的指定方法乃至这个Bean实例就绪到最后的销毁,这便是整个Spring的Bean实例的生命周期。

在这里我们要明确一点,定义在配置文件中的Bean实例在缺省状态下都是单例模式,在Spring2.5以前我们可以在<bean/>标签中指定“singleton”属性为 “false”,这样定义在配置文件中的Bean实例则不会再为单利,一般我们在和Struts2.x做集成的时候,往往需要将Struts2.x的Action都设置为非单例,否则请求都会是同一个。只不过在Spring2.5以上后,我们将不能再继续使用 “singleton”属性,取而代之的是“scope”属性,我们只需要将该属性的值改为“prototype”即为非单利即可,缺省为属性值为“singleton”,也就是单例模式。

 

 四、IOC容器中Bean的作用域

在IOC容器中,如果Bean实例选用的是单例模式,其作用域范围为当IOC容器成功初始化后便会对其进行实例化。如果Bean实例选用的是非单例模式,其作用域范围为当IOC容器成功初始化后,并且当真正需要使用到Bean实例时,IOC容器才会对其进行实例化以及依赖装配。

以上是单例模式与非单例模式Bean的不同作用域范围。当然如果Bean实例本身就是单例模式的,但<bean/>标签中的“lazy-init”属性设置为“true”时,其作用域仍然与非单例模式保持一致,都是当需要真正使用到的时候才会对其进行实例化和依赖装配。 

 

五、Bean之懒加载

在上一章节笔者已经给大家介绍了有关Bean懒加载的一些概念,本章笔者将继续对懒加载模式进行讲解。其实所谓懒加载(Lazy)也可以叫做延迟加载,指的是当对象真正需要使用到的时候才会对其进行加载操作。这是概念上对懒加载模式的一种定义,但笔者接下来会告诉你关于如何在IOC配置文件中定义Bean的懒加载模式,以及懒加载模式的一些优缺点。

在配置文件中添加Bean实例的懒加载支持:

<!-- 定义LoginBean实例 -->
<bean id="loginBean" class="org.johngao.bean.LoginBean" lazy-init="true">
	<property name="userAccount" value="JohnGao" />
	<property name="passWord" value="123456" />
</bean>

 

通过上述配置,这个Bean则会采用懒加载模式进行加载,也就是说只有当客户端采用getBean()方法的时候IOC容器才会去对其进行实例化及依赖装配操作。

懒加载模式最大的优点在于可以降低资源开销平率,因为对象只有在真正需要使用到的时候才会选择加载。但懒加载的缺点同样也很显著,由于是运行期加载,很多未知的错误我们无法在程序启动时发现,换句话说就是无法在程序编译时发现错误,这样所对于开发人员而言是不太友好的。

 

提示:

在Spring的配置文件中,实现懒加载模式有2种方式。一种是在"<bean></bean>"标签中定义“lazy-init”属性,而另外一种则是使用“scope”属性定义非单例Bean。通过这2种方式均可以实现Bean的懒加载。

 

六、依赖注入之参数详解

本章节笔者将会对IOC的配置文件做一次详细的讲解,如果你对如何配置IOC容器中的相关参数已经非常熟悉了,笔者建议你直接跳过本章,因为免得耽误你的时间。但是对于刚接触Spring没有多久的开发人员,笔者建议你还是认真的阅读笔者接下来要讲解的诸多知识点,或许只有好处没有坏处,你认为呢?

IOC配置文件的存放路径Spring并没有强制要求你只能一定要放在ClassPath下,你可以选择放在任意目录下,甚至是远程目录,只是你需要选用不同的IOC容器实现来满足你的要求,仅此而已。

我们首先来看,在IOC配置文件中,如果你需要定义一个Bean实例你需要使用<bean>标签即可,在该标签内部你还可以使用<property/>标签或<constructor-arg/>标签来实现依赖装配。先来看一个常规的IOC配置文件:

<?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-3.1.xsd">
	<!-- 定义LoginBean实例 -->
	<bean id="loginBean" class="org.johngao.bean.LoginBean" lazy-init="true" 
		scope="prototype" destroy-method="destroy">
		<property name="userAccount" value="JohnGao" />
		<property name="passWord" value="123456" />
		<property name="objectTest" ref="userInfo" />
		<constructor-arg type="java.lang.String" value="JonGao123"/>
	</bean>
	
	<!-- 定义UserInfo实例 -->
	<bean id="userInfo" class="org.johngao.bean.UserInfo"/>
</beans>

 

笔者在上述配置文件中定义了2个Bean实例,其中loginBean笔者显式的指定了采用懒加载模式,以及非单例模式,并且笔者还才使用了“destroy-method”属性设置Bean在被销毁前要关闭一些资源等操作。在<bean/>标签中<property/>属性用于投值注入,其中“name”属性定义了参数的名称,这里笔者需要提示一下参数名称一定要和Bean的属性名称一致并且要保证唯一性,最后Bean还需要生成setter方法,Spring才会注入(后期笔者会讲解关于如何进行Bean的自动装配)。而“value”属性指定了具体的参数,这里参数不仅限是字符串而是任意类型的参数都可以,也就是Object类型,当然如果你想引用其他<bean/>标签中的实例,你可以使用“ref”属性进行引用。如果是构造注入你还需要标注其参数的类型,这一点仍然很很重要,在<constructor-arg/>标签中除了可以直接定义属性值,仍然可以使用“ref”属性进行引用。

 

提示:

关于Spring配置文件的DTD或者Xmlns笔者将不做讲解,如有需要,你可以参考其他文旦。

 

七、整合多个配置文件

在实际开发过程中,我们不可能只使用一个IOC配置文件来完成我们所有的操作和定义。往往我们需要编写多个配置文件来满足我们的需求,比如:web-context,dao-context,root-context等等,当然定义多少个配置文件在于你,关键是我们需要将这些配置文件整合在一起相互引用,否则这些配置文件分的再多,再细致也起不到丝毫作用。

 

提示:

假设A配置文件中配置有通用参数,B配置文件引用A文件以后,无需再次重复定义。

 

Spring允许在配置文件中使用<import/>标签手动引用其它配置文件:

<import resource="testB-context.xml"/>

 

上述方式我们是通过手动的方式在IOC配置文件中标注的,但如果你的配置文件特别多,而且相互存在引用,这个时候我们根本没有必要在使用<import/>标签,我们完全可以依靠IOC容器在初始化的时候使用通配符来实现:

/* 使用通配符加载多个配置文件 */
ApplicationContext context2 = new ClassPathXmlApplicationContext("*-context.xml"); 

 

一旦IOC容器对其多个配置文件进行加载后,这些配置文件已经存在相互关联和引用,无需我们再配置文件中使用<import/>进行导入。

本章内容到此结束,由于时间仓库,本文或许有很多不尽人意的地方,希望各位能够理解和体谅。关于下一章什么的跟新时间笔者初步订到后天晚上,谢谢各位的支持,晚安。

12
3
分享到:
评论
15 楼 蒙太奇 2013-05-31  
这几天一直在找关于SpringMVC,JPA的资料,没有一遍有你写的这么详细,很有耐心啊,可以出书了!
14 楼 hyl523 2013-01-22  
谢谢作者,很给力,启动spring aop,mvc
13 楼 gao_xianglong 2013-01-19  
好文章,受教了,谢谢。呵呵,提个小问题:这篇博文中总共出现了22次“笔者”,感觉好别扭呀,去掉一
WuNan2 写道
好文章,受教了,谢谢。呵呵,提个小问题:这篇博文中总共出现了22次“笔者”,感觉好别扭呀,去掉一去也许会更通俗流畅。


有吗,我们怎么没发现啊,哈哈
12 楼 WuNan2 2013-01-19  
好文章,受教了,谢谢。呵呵,提个小问题:这篇博文中总共出现了22次“笔者”,感觉好别扭呀,去掉一去也许会更通俗流畅。
11 楼 gao_xianglong 2013-01-17  
bifa37 写道
讲得很好。期待下文!

谢谢支持
10 楼 bifa37 2013-01-17  
讲得很好。期待下文!
9 楼 gao_xianglong 2013-01-17  
eprecated
longfor5 写道
看你这篇文字的时候顺便看了一下spring-beans的中XmlBeanFactory源码,结果发现这个类在spring3.1已经被废弃了。
org.springframework.beans.factory.xml.XmlBeanFactory


@Deprecated


Deprecated. as of Spring 3.1 in favor of DefaultListableBeanFactory and XmlBeanDefinitionReader




是的,确实已经是过时了,谢谢提醒。
8 楼 longfor5 2013-01-17  
看你这篇文字的时候顺便看了一下spring-beans的中XmlBeanFactory源码,结果发现这个类在spring3.1已经被废弃了。
org.springframework.beans.factory.xml.XmlBeanFactory


@Deprecated


Deprecated. as of Spring 3.1 in favor of DefaultListableBeanFactory and XmlBeanDefinitionReader


7 楼 gao_xianglong 2013-01-17  
zhengcong 写道
期待您的下篇文章,  

3q
6 楼 gao_xianglong 2013-01-17  
longfor5 写道
持续关注,再接再厉!

3Q
5 楼 zhengcong 2013-01-17  
期待您的下篇文章,  
4 楼 longfor5 2013-01-17  
持续关注,再接再厉!
3 楼 o_o_0 2013-01-17  
 
2 楼 gao_xianglong 2013-01-17  
duoduodeai 写道
   好文章,期待下篇

感谢支持。
1 楼 duoduodeai 2013-01-16  
   好文章,期待下篇

相关推荐

Global site tag (gtag.js) - Google Analytics