Spring IoC

IoC的直译是控制反转。
在IoC模式下,控制权限从应用程序转移到了IoC容器中。组件不是由应用程序负责创建和配置,而是由IoC容器负责。
使用IoC的情况下,对象是被动地接收依赖类而不是主动地查找。对象不是从容器中查找他的依赖类,而是容器在实例化对象时,主动地将他所依赖的对象注入给他。
应用程序只需要直接使用已经创建并且配置好的组件即可,而不必自己负责创建和配置。
在实际的工作中人们发现使用IoC来表述这种机制,并不是很准确甚至有些晦涩,于是引发了另外一个概念:DI(依赖注入)
目前人们认为使用依赖注入(DI—— Dependency Injection )来阐述IoC更为合适。
而事实上早在2004年初,Martin Fowler在他的站点谈论控制反转时曾提问: “问题在于,它们转变的是什么方面的控制?”。 Fowler建议重命名该原则(或至少给它一个更加明确的名称),并开始使用“依赖注入这个术语。
解析IoCDI
(一)
IoC解析
IoC(控制反转)是Spring容器的内核,AOP以及声明式事务都是在此之上完成的。IoC包括两层含义:1.控制,2反转。关键的问题在于他把什么东西的控制给反转了。
控制指组件之间的调用关系由程序自身控制(从而造成了紧耦合)。
反转是说,将这种调用关系的控制权从程序中移除,并交给第三方管理。
也就是组件的调用关系由程序自身管理转变为第三方管理,是组件依赖关系的控制权进行了反转
对于上述机制,应用IoC来描述并不准确,而且有些晦涩。
(二)
DI解析
之所以会产生组件调用,是为了获取被调用组件的功能,调用者将自己应该做的事情,委派给了被调用的对象。也就是说,调用者要完成自身的任务,必须依赖被调用的对象。这种关系实际上就是一般依赖关系(通俗点说,就是一个组件内部包含另外一个组件的实例,把该自己干的事交给自己包含的组件去完成)。因此IoC所表述的机制,实际上就是将调用者对接口实现类的依赖关系,从程序中移除,转交第三方管理实例。并且,由第三方在运行期间将调用者依赖的具体类填充进来。也就是说组件之间的依赖关系,是在程序运行期间由第三方来管理的。这个就是依赖注入的概念(DI),基于上述分析,DI比IoC更准确。
实际上就是将调用者为完成功能所依赖的实现类,在程序运行期间,由容器自动填充给调用者,这个就是依赖注入的核心思想。在依赖注入的应用中,组件并不关心被注入的对象是谁,只关系这个对象能完成的功能,也就是这个对象是哪个接口的具体类实例。
Simple Object范围:
POJO:简单的Java对象(Plain Old Java Objects)实际就是普通JavaBean
VO:value object值对象
PO:persistant object 持久对象
DTO:Data Transfer Object 数据传输对象
DAO:data access object 数据访问对象
1、注:
POJO-POJO定义:
简单的Java对象(Plain Old Java Objects)实际就是普通JavaBeans。
POJO-POJO使用:
使用POJO名称是为了不和EJB混淆起来, 而且简称比较直接. 其中有一些属性及其getter setter方法的类,有时可以作为value object或dto(Data Transform Object)来 使用.当然,如果你有一个简单的运算属性也是可以的,但不允许有业务方法,也不能携带有connection之类的方法。
2、一些名词:
PO : persistant object 持久对象
VO : value object值对象
DAO : data access object 数据访问对象
BO : business object 业务对象
POJO : plain ordinary java object 简单的java对象
DTO : Data Transfer Object 数据传输对象
1)侧重数据的对象,对象中主体是字段,字段包含数据,方法都是数据字段的getXX或setXXX等行为,没有真正意义行为。属于这种的有POJO VO PO DTO等
2)侧重功能行为的对象,和上面相反,则重方法,方法体内有很重要的行为,字段不重要。属于这种的有BO DAO JDO ADO等
DTO : Data Transfer Object 数据传输对象,主要用于远程调用等需要大量传输对象的地方。 比如我们一张表有100个字段,那么对应的PO就有100个属性。 但是我们界面上只要显示10个字段, 客户端用WEB service来获取数据,没有必要把整个PO对象传递到客户端, 这时我们就可以用只有这10个属性的DTO来传递结果到客户端,这样也不会暴露服务端表结构.到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为VO。
Bean是什么(一)
具有唯一id的Simple Object
由IoC容器管理其生命周期及其依赖关系
简单地讲,bean就是由Spring容器初始化、装配及被管理的对象
bean定义以及bean相互之间的依赖关系将通过配置元数据来描述
一般在XML文件中定义
1、在Spring中,那些组成应用的主体及由Spring IoC容器所管理的对象被称之为Bean。
简单地讲,bean就是由Spring容器初始化、装配及被管理的对象,除此之外,bean就没有特别之处了(与应用中的其它对象没有什么区别)。
而bean定义以及bean相互之间的依赖关系将通过配置元数据来描述。
2、JavaBean原始形态是一种符合特定规范的java对象,有无参构造器,通过get/set提供外界对私有属性的访问,是最简单最基本的java组件,即可被用作数据承载的载体即VO,也可以用来执行业务逻辑。
随着编程思维以及现实环境的发展, JavaBean的规范在实际应用中日趋模糊,日渐与POJO融合。
在Spring中,大力推荐使用的JavaBean,实际上就是POJO,凡是可以被实例化或是可以被JNDI获得的对象,都可以被Spring容器管理,都是Bean
Bean定义(二)
bean定义与应用程序中实际使用的对象一一对应。通常情况下bean的定义包括:
服务层对象
数据访问层对象(DAO)
类似Struts Action的表示层对象
Hibernate SessionFactory对象等等。
项目的复杂程度将决定bean定义的多寡。
Bean 的定义和命名(三)
使用Spring IoC容器来管理一个或多个bean,这些bean将通过配置文件中的bean定义被创建(在XML格式中为<bean/>元素)。
在容器内部,这些bean定义由BeanDefinition对象来表示,该定义将包含以下信息(在配置文件中体现为属性或子标记):
全限定类名这通常就是已定义bean的实际实现类。
bean行为的定义,即创建模式(prototype还是singleton)、自动装配模式、依赖检查模式、初始化以及销毁方法。这些定义将决定bean在容器中的行为
用于创建bean实例的构造器参数及属性值。比如使用bean来定义连接池,可以通过属性或者构造参数指定连接数,以及连接池大小限制等。
bean之间的关系,即协作 (或者称依赖)。
配置文件中Bean的基本属性(四)
Spring IoC
Name 属性:当使用基于XML的配置元数据时,将通过id或name属性来指定bean标识符。id属性具有唯一性,而且是一个真正的XML ID属性,因此其他xml元素在引用该id时,可以利用XML解析器的验证功能。通常情况下最好为bean指定一个id。尽管XML规范规定了XML ID命名的有效字符,但是bean标识符的定义不受该限制,因为除了使用指定的XML字符来作为id,还可以为bean指定别名,要实现这一点可以在name属性中使用逗号、冒号或者空格将多个id分隔。
bean的作用域:
作用域描述singleton在每个Spring IoC容器中一个bean定义对应一个对象实例。
prototype一个bean定义对应多个对象实例。
request在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。
session在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
global session在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。
BeanFactory(一)
轻量级的IoC容器
org.springframework.beans.factory.BeanFactory是Spring IoC容器的实际代表者,IoC容器负责容纳此前所描述的bean,并对bean进行管理。
管理Bean的生命周期及其依赖关系
尽可能晚的初始化Bean,适用于对内存要求很高的应用
最常用的实现是XmlBeanFactory
e.g.:BeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
e.g.:BeanFactory factory = new XmlBeanFactory(new UrlResource("beans.xml"));
e.g.:BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
e.g.:BeanFactory factory = new XmlBeanFactory(new ServletContextResource("beans.xml"));
key API
boolean containsBean(String)
Object getBean(String)
boolean isSingleton(String)
1、对内存要求很高:指对内存精打细算,不浪费内存的。
2、XmlBeanFactory就是最常用的一个Spring的BeanFactory的实现。该实现将以XML方式描述组成应用的对象以及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并用它来构建一个完全可配置的系统或应用。
3、BeanFactory提供的方法极其简单。它仅提供了六种方法供客户代码调用:
1)boolean containsBean(String):如果BeanFactory包含给定名称的bean定义(或bean实例),则返回true.
2)Object getBean(String):返回以给定名字注册的bean实例。根据bean的配置情况,如果为singleton模式将返回一个共享的实例,否则将返回一个新建的实例。如果没有找到指定的bean,该方法可能会抛出BeansException异常(实际上将抛出NoSuchBeanDefinitionException异常),在对bean进行实例化和预处理时也可能抛出异常.
3)Object getBean(String, Class):返回以给定名称注册的bean实例,并转换为给定class类型的实例,如果转换失败,相应的异常(BeanNotOfRequiredTypeException)将被抛出。上面的getBean(String)方法也适用该规则。
4)Class getType(String name):返回给定名称的bean的Class。如果没有找到指定的bean实例,则抛出NoSuchBeanDefinitionException异常。
5)boolean isSingleton(String):判断给定名称的bean定义(或bean实例)是否为singleton模式(singleton将在bean的作用域中讨论),如果bean没找到,则抛出NoSuchBeanDefinitionException异常。
6)String[] getAliases(String):返回给定bean名称的所有别名。
ApplicationContext(二)
ApplicationContext(建议使用这种方式获取容器)
继承了BeanFactory接口,是BeanFactory功能的延伸
尽可能早的初始化Bean
支持国际化
支持事件监听机制
提供通用的方式获取资源
主要实现
ClassPathXmlApplicationContext:
从类路径下的XML文件中载入上下文定义信息
FileSystemXmlApplicationContext:
从文件系统路径下的XML文件中载入上下文定义信息
XmlWebApplicationContext:
通过ContextLoaderListener从内部导入context文件
注:
ApplicationContext與BeanFactory的區別:
spring的Ioc容器根据XML配置文件来初始化Bean.需要注意的一点是,ApplicationContext初始化Bean和基
本的BeanFactory有所不同,基本的BeanFactory总是延迟加载Bean,直到第一次调用getBean("BeanId")方法
请求Bean实例时,BeanFactory才会创建这个Bean,而ApplicationContext在自身初始化时就一次性创建了
所有的Bean,了解这一点是非常重要的,因为ApplicationContext在初始化时就能验证XML配置文件的正确性.
而使用BeanFactory,直到调用getBean("BeanId")方法获取Bean实例时,才可能会发现配置错误而导致抛出
异常.
只有在非常简单的情况下,使用基本的BeanFactory才可能满足我们的需求.绝大多时候我们使用
ApplicationContext是最佳的选择.在启动的时候就能检测配置文件的错误,这比使用基本的BeanFactory
在运行一段时间后调用getBean("BeanId")抛出异常要好得多.并且,延迟加载会带来性能上的损失.
ApplicationContext由于在启动时需要一次性别实例化所有的Bean,如果定义的Bean比较多,则启动的时间
会比较长.
ApplicationContext实例化(三)
//1、ClassPathXmlApplicationContext
String[] ctxs = new String[]{"ctx1.xml", "ctx2.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(ctxs);
//2、 FileSystemXmlApplicationContext
ApplicationContext ctx =
new FileSystemXmlApplicationContext("c:/beans.xml");
//3、 XmlWebApplicationContext
XmlWebApplicationContext ctx = new XmlWebApplicationContext();
ctx.setServletContext(servlet.getServletContext());
ctx.setConfigLocations(new String[] { configFile });
ctx.refresh();
获取资源(四)
默认实现
ClassPathResource:
通过 ClassPathResource 以类路径的方式进行访问;
FileSystemResource:
通过 FileSystemResource 以文件系统绝对路径的方式进行访问;
ServletContextResource:
通过 ServletContextResource 以相对于Web应用根目录的方式进行访问;
UrlResource:
通过java.net.URL来访问资源,当然它也支持File格式,如“file:”;
ApplicationContext提供了获取资源的便利途径,获取资源API:
Resource getResource(String location)
绝对路径,e.g. “file:C:/test.txt”
相对路径,e.g. “WEB-INF/test.txt”
Classpath, e.g. “classpath:test.txt”
ApplicationContextBeanFactory(五)
BeanFactory
采用延迟加载Bean,直到第一次使用getBean()方法获取Bean实例时,才会创建Bean。
ApplicationContext
ApplicationContext在自身被实例化时一次完成所有Bean的创建。大多数时候使用ApplicationContext。
区别
在服务启动时ApplicationContext就会校验XML文件的正确性,不会产生运行时bean装配错误。
BeanFactory在服务启动时,不会校验XML文件的正确性,获取bean时,如果装配错误马上就会产生异常。
1、BeanFactory提供的高级配置机制,使得管理任何性质的对象成为可能;ApplicationContext是BeanFactory的扩展,功能得到了进一步的增强,比如更易与spring AOP 集成、消息资源处理(国际化处理)、事件传递及各种不同应用层的Context实现。
2、简而言之,BeanFactory提供了配置框架功能,而ApplicationContext则增加了更多支持企业核心内容的功能。ApplicationContext完全由BeanFactory扩展而来,因而BeanFactory所具有的能力和行为也适用于ApplicationContext。
依赖注入的三种方式
设置属性注入
set注入指的就是在接受注入的类中定义一个set方法,并在参数中定义需要注入的元素。
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
public void setBeanOne(AnotherBean beanOne2) { beanOne = beanOne2; }
public void setBeanTwo(YetAnotherBean beanTwo2) { beanTwo = beanTwo2; }
}
<bean id="exampleBean" class="eg.ExampleBean">
<property name="beanOne“><ref bean="anotherExampleBean"/> </property>
<property name="beanTwo“> <ref bean="yetAnotherBean"/> </property>
</bean>
<bean id="anotherExampleBean" class="eg.AnotherBean"/>
<bean id=“yetAnotherBean” class=“eg.YetAnotherBean”/>
构造注入
构造注入指的就是在接受注入的类中定义一个构造方法,并在参数中定义需要注入的元素。
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
public ExampleBean(AnotherBean b1, YetAnotherBean b2, int i) {
this.beanOne = b1;
this.beanTwo = b2; } }
<bean id="exampleBean" class="eg.ExampleBean">
<constructor-arg><ref bean="anotherExampleBean"/> </constructor-arg>
<constructor-arg> <ref bean="yetAnotherBean"/> </constructor-arg>
</bean>
<bean id="anotherExampleBean" class="eg.AnotherBean"/>
<bean id=“yetAnotherBean” class=“eg.YetAnotherBean”/>
1、注:使用Constructor还是Setter方法来完成依赖注入?
答:这个问题就等于在讨论一个古老的问题:要在对象建立时就准备好所有的资源,或是在对象创建好后,再使用Setter方法来进行设定。
使用Constructor的好处之一是,可以在构造对象的同时一并完成依赖关系的建立,不好的地方是,使用Constructor injection会在构造方法上留下
一长串的参数,且不易记忆,这时使用Setter方法是一个不错的选择。
然而使用Setter方法(Setter方法不许private)时,由于提供setXXX()方法,因此不能保证相关的数据成员或资源在执行时期不会被更改设定,因为程序员可能直接执行Setter方法来
设定相关的属性,所以如果想要让一些数据成员或资源变为只读或是私有,使用Constructor injection会是简单的选择。
2、存在多个构造函数时,容易引起IoC容器的混乱,需要明确告诉容器,程序中使用的是那个构造子。语法如下:
<bean id="b2" class="com.qhit.business.ioc.Business2">
<constructor-arg index="0" value="2" type="int"></constructor-arg>
<constructor-arg index="1" value="1" type="int"></constructor-arg>
</bean>
属性含义:
index :第几个参数,从零开始(一般不使用index这个属性,只根据type可以区分出使用哪个构造器)
type:参数类型
接口注入
接口注入指的就是在接口中定义要注入的信息,并通过接口完成注入。
由于接口注入的繁琐与侵入性过高,Spring没有提供对接口注入的实现。
实现不同类型属性的注入
基本类型注入
基本数据类型的注入在配置文件中直接注入即可,不需要数据类型,IoC容器会自动的根据bean中属性的类型,完成类型转换、并赋值。注入语法如下
<bean id="classes" class="com.neu.impl.Classes">
<property name="sid" value="1"/>
<property name="sname" value="貂禅"/>
</bean>
其中id表示bean在整个工程中的唯一标识,class表示该标识的引用类
property的另一种使用(子标记):
<property name="sname">
<value>貂禅</value>
</property>
Null的注入
如果某个Bean中某个属性没有值,我们应该注入null 语法如下
<property name="state">
<null/>
</property>
空字符串的注入
<bean class="ExampleBean">
<property name="email">
<value></value>
</property>
</bean>
引用类型的注入
如果我们注入的不是基本类型,而是引用类型,即对于委派关系进行注入,我们需要用<ref> 代替<value>:
<bean id="classes" class="com.neu.bean.Classes">
<property name="cid" value="1"/>
<property name="cname" value="天山"/>
</bean>
<bean id="student" class="com.neusoft.bean.Student">
<property name="sid" value="1"/>
<property name=“sname” value=“王五"/>
<property name="state" value="1"/>
<property name="classes" ref="classes"/>
</bean>
集合类型注入-set(一)
Set中基本数据类型的注入
等同于普通类中基本数据类型的注入,但是set会自动过滤重复数据。 语法如下
<property name="students">
<set>
<value>A</value>
<value>B</value>
<value>C</value>
<value>B</value>
</set>
</property>
集合类型注入-set(二)
Set中引用数据类型的注入
如果属性承载的也是对bean 引用,在set内部等同于对象的引用 语法如下
<property name="students">
<set>
<ref bean="stu1"/>
<ref bean="stu2"/>
<ref bean="stu3"/>
<ref bean="stu4"/>
</set>
</property>
Map的注入
<property name="homes">
<!-- map映射关键字 -->
<map>
<!-- map中的一个实体,以及该实体值 -->
<entry key="R1">
<!-- map中该实体的值 -->
<value>优秀班级</value>
</entry>
<!-- map中该实体的值是一个引用类型 -->
<entry key="R2“> <ref bean="stu1"/> </entry>
</map>
</property>
List及数组注入
<property name="home">
<list>
<value>黑风洞</value>
<value>花果山</value>
<value>灵山</value>
</list>
</property>
或者:
<property name="home">
<list>
<ref bean=“hello”/>
<ref bean="stu1"/>
</list>
</property>
property属性的用法
<property name="propsProperty">
<props>
<prop key="key1">bar1</prop>
<prop key="key2">bar2</prop>
</props>
</property>
Bean的实例化
就Spring IoC容器而言,bean定义基本上描述了创建一个或多个实际bean对象的内容。当需要的时候,容器会从bean定义列表中取得一个指定的bean定义,并根据bean定义里面的配置元数据使用反射机制来创建一个实际的对象。因此需要告知Spring IoC容器我们将要实例化的对象的类型以及如何实例化对象。
1、用构造器来实例化:需要指定class属性(常用);
<bean id="XXXbean" class=""/>
1、用构造器来实例化
当采用构造器来创建bean实例时,Spring对class并没有特殊的要求,我们通常使用的class都适用。也就是说,被创建的类并不需要实现任何特定的接口,或以特定的方式编码,只要指定bean的class属性即可。不过根据所采用的IoC类型,class可能需要一个默认的空构造器。
此外,IoC容器不仅限于管理JavaBean,它可以管理任意的类。不过大多数使用Spring的人喜欢使用实际的JavaBean(具有默认的(无参)构造器及setter和getter方法),但在容器中使用非bean形式(non-bean style)的类也是可以的。比如遗留系统中的连接池,很显然它与JavaBean规范不符,但Spring也能管理它。
当使用基于XML的元数据配置文件,可以这样来指定bean类:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
2、使用静态工厂方法实例化
当采用静态工厂方法创建bean时,除了需要指定class属性外,还需要通过factory-method属性来指定创建bean实例的工厂方法。Spring将调用此方法(其可选参数接下来介绍)返回实例对象,就此而言,跟通过普通构造器创建类实例没什么两样。
下面的bean定义展示了如何通过工厂方法来创建bean实例。注意,此定义并未指定返回对象的类型,仅指定该类包含的工厂方法。在此例中, createInstance()必须是一个static方法
<bean id="exampleBean" class="examples.ExampleBean2" factory-method="createInstance"/>
3、使用实例工厂方法实例化
使用静态工厂方法实例化类似,用来进行实例化的实例工厂方法位于另外一个已有的bean中,容器将调用该bean的工厂方法来创建一个新的bean实例为使用此机制,class属性必须为空,而factory-bean属性必须指定为当前(或其祖先)容器中包含工厂方法的bean的名称,而该工厂bean的工厂方法本身必须通过factory-method属性来设定(参看以下的例子)。
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="myFactoryBean" class="..."> ... </bean>
<!-- the bean to be created via the factory bean -->
<bean id="exampleBean" factory-bean="myFactoryBean" factory-method="createInstance"/>
虽然设置bean属性的机制仍然在这里被提及,但隐式的做法是由工厂bean自己来管理以及通过依赖注入(DI)来进行配置。
2、使用 静态工厂方法实例化:需要指定class属性外 ,还需要通过factory-method属性来指定创建bean实例的工厂方法;
<bean id="XXXbean" class="XXXfactoryClass" factory-method=""/>
3、使用实例工厂方法实例化 : class属性必须为空,而factory-bean属性必须指定为当前(或其祖先)容器中包含工厂方法的bean的名称,而该工厂bean的工厂方法本身必须通过factory-method属性来设定;
<bean id="XXXfacotry" class=""/>
<bean id="XXXbean" factory-bean="XXXfacotry" factory-method=""/>
scope属性(singleton
注:
1、在Spring中,从BeanFactory或ApplicationContext取得的实例为Singleton(单例),也就是预设为每一个Bean的别名只维持一个实例。
例如:每一次context.getBean(“helloBean”)取得的对象都是同一个,而不是每一次都产生一个新的对象。
2、使用Singleton模式产生单一实例,对单线程的程序来说并不会有什么问题,但对于多线程的程序,您必须注意到线程安全(Thread-safe)
的议题,防止多个线程同时存取共享资源所引发的数据不同步问题。
3、在Spring中,可以设定每次从BeanFactory或ApplicationContext指定别名并取得Bean时都产生一个新的实例。例如:
<bean id=“helloBean” class=“neu.danny.HelloBean” singleton=“false”>。
4、在Spring中singleton属性预设是”true”,籍由将其设定为”false”,则每次指定别名来取得Bean时都会产生一个新的实例。
5、Spring1.2的DTD继续使用“singleton”属性,但是新的Spring2.0 DTD不允许使用“singleton”属性了,改成“scope”属性来描述bean的生命周期。
scope属性(prototype
注:
Prototype(原型)作用域:
prototype作用域的Bean会导致在每次对该Bean请求(将其注入到另一个Bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的Bean实例。
根据经验,对有状态的Bean应该使用prototype作用域,而对无状态的Bean则应该使用singleton作用域。
注:
对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:
1、容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。
2、不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法。
3、但对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。
4、清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被prototype作用域bean占用资源的一种可行方式是),通过使用bean的后置处理器,该处理器持有要被清除的bean的引用。
二、bean的作用域:在创建一个bean定义(通常为XML配置文件)时,你可以简单的将其理解为:用以创建由该bean定义所决定的实际对象实例的一张“处方(recipe)”或者模板。就如class一样,根据一张“处方”你可以创建多个对象实例。
你不仅可以控制注入到对象(bean定义)中的各种依赖和配置值,还可以控制该对象的作用域。这样你可以灵活选择所建对象的作用域,而不必在Java Class级定义作用域。
- 使用singleton,每次调用getBean()时返回相同的实例;当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把一个bean定义设置为singlton作用域时,Spring IoC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例。
- 使用prototype,每次调用getBean()时返回不相同的实例;比如,在用Spring集成Struts时,我们将action的创建交给Spring来创建,就可以使用prototype,这样使得每次过来一个线程时都给你一个不同的action实例,以避免线程安全的问题。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。根据经验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
Spring Framework支持五种作用域(其中有三种只能用在基于web的Spring ApplicationContext)。
Bean的懒加载
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true">
<!-- various properties here... -->
</bean>
<bean name="not.lazy" class="com.foo.AnotherBean">
<!-- various properties here... -->
</bean>
<!-- 也可以在 beans 标签中加入“默认lazy”模式 -->
<beans default-lazy-init=“true”>
<! - - no beans will be eagerly pre-instantiated… - - >
</beans>
注:
ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化。
提前实例化意味着作为初始化过程的一部份,ApplicationContext实例会创建并配置所有的singleton bean。
然而,有时候这种默认处理可能并不是你想要的。
如果你不想让一个singleton bean在ApplicationContext实现在初始化时被提前实例化,那么可以将bean设置为延迟实例化。
一个延迟初始化bean将告诉IoC容器是在启动时还是在第一次被用到时实例化。
注:
需要说明的是,如果一个bean被设置为延迟初始化,而另一个非延迟初始化的singleton bean依赖于它,
那么当ApplicationContext提前实例化singleton bean时,
它必须确保所有上述singleton依赖bean也被预先初始化,
当然也包括设置为延迟实例化的bean。因此,如果Ioc容器在启动的时候创建了那些设置为延迟实例化的bean的实例,你也不要觉得奇怪,因为那些延迟初始化的bean可能在配置的某个地方被注入到了一个非延迟初始化singleton bean里面。
Bean的继承
<bean id="inheritedTestBean" class="org.springframework.beans.TestBean“ abstract="true" >
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean“ init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
注:
在bean定义中包含了大量的配置信息,其中包括容器相关的信息(比如初始化方法、静态工厂方法名等等)以及构造器参数和属性值。
子bean定义就是从父bean定义继承配置数据的bean定义。
它可以覆盖父bean的一些值,或者添加一些它需要的值。
使用父/子bean定义的形式可以节省很多的输入工作。
实际上,就是一种模板形式。
Bean的初始化
1、非侵入性
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
} }
2、侵入性(不建议使用)
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
} }
Bean的销毁
从结果进行分析,Bean的初始化顺序应该是
1.构造函数
2.初始化属性
3.如果实现了BeanFactoryAware 接口执行setBeanFactory方法
4..如果实现了InitializingBean 接口执行afterPropertiesSet方法
5.如果在配置文件中指定了init-method,那么执行该方法
6..如果实现了BeanFactoryPostProcessor 接口在 “new”其他类之前执行 postProcessBeanFactory 方法(通过这个方法可以改变配置文件里面的属性值的配置)
7.如果实现了BeanFactoryPostProcessor 接口,那么会在其他bean初始化方法之前执行postProcessBeforeInitialization 方法,之后执行postProcessAfterInitialization方法
!感觉奇怪的地方就是没有执行destroy方法,目前还不知道原因在什么地方
如果想调用destroy方法,需要使用子类ClassPathXmlApplicationContext来声明对象;
1、非侵入性
<bean id="exampleInitBean" class="examples.ExampleBean"
destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
} }
2、侵入性(不建议使用)
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
} }
Bean的高级特性及其它接口
自动组装(不建议使用,代码混乱)
<bean id=“helloBean” class=“neu.danny.HelloBean“ autowire=“autodetect”/>
<property name=“hellWorld”>
<value>Hello!</value>
</property>
</bean>
依赖检查(不建议使用)
<bean id=“helloBean” class=“neu.danny.HelloBean“ autowire=byName
dependency-check=“all”/>
<property name=“hellWorld”>
<value>Hello!</value>
</property>
</bean>
1、Spring IoC容器可以自动装配(autowire)相互协作bean之间的关联关系。因此,如果可能的话,可以自动让Spring通过检查BeanFactory中的内容,来替我们指定bean的协作者(其他被依赖的bean)。由于autowire可以针对单个bean进行设置,因此可以让有些bean使用autowire,有些bean不采用。
autowire的方便之处在减少或者消除属性或构造器参数的设置,这样可以给我们的配置文件减减肥!在xml配置文件中,autowire一共有五种类型,可以在<bean/>元素中使用autowire属性指定:
注意:约定由于配置的原则,即各个bean 中属性的命名与配置文件中的id 的值要一致。还有一点就是要在配置文件的头部声明部分加入:
default-autowire=“byName”、default-autowire=“byType”
开发期间建议使用这个方式;而项目完成后稳定期,不建议使用。因为在维护升级时,我们难以查询bean之间的依赖关系。
2、 Spring除了能对容器中bean的依赖设置进行检查外。还可以检查bean定义中实际属性值的设置,当然也包括采用自动装配方式设置属性值的检查。
当需要确保bean的所有属性值(或者属性类型)被正确设置的时候,那么这个功能会非常有用。当然,在很多情况下,bean类的某些属性会具有默认值,或者有些属性并不会在所有场景下使用,因此这项功能会存在一定的局限性。就像自动装配一样,依赖检查也可以针对每一个bean进行设置。依赖检查默认为not, 它有几种不同的使用模式,在xml配置文件中,在<bean>标签使用时设定"dependency-check",可以有四种依赖检查方式:
"simple":只检查简单的属性是否完成依赖关系,像是原始数据类型或字符串对象。
"objects":设定检查对象类型的属性是否完成依赖关系
"all":检查全部的属性是否完成依赖关系
"none":设定是默认值,表示不检查依赖性。
注:
一旦使用了自动绑定时加入了依赖检查设定,如果进行依赖检查时发现有未完成的依赖关系,则执行程序时
会抛出UnsatisfiedDependencyException异常(如果没有设置依赖检查,那么只有运行到出错代码时才出错;有了依赖检查,在装载容器时就报错)
注:
Spring的依赖检查特性只对属性是否通过 setter 方法设置进行检查。 所以,即使通过构造器注入,依然会抛出异常。
autowire="autodetect"
autowire="byName"
autowire="byType"
autowire="constructor"
基于注解的配置(Annotation-Based)
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config/>
</beans>
基于注解的配置[email protected]
在 Spring 2.5中已经可以用注解的方式去驱动 Spring 的依赖注射了
@Autowired注解提供了与上一节中描述的同样功能,并且提供了更细致的控制与更好的适应性。
当然,要使注解可用,您必须使用 Java 5 或更新的版本,以使得可以访问源代码层次的注解。
注解可以被注册为独立 bean 的定义,但它们也可以被隐式地注册,通过基于 XML 的配置方式
@Autowired 注解可以用于“传统的”setter 方法
@Autowired注解甚至可以用于构造器与字段
因为通过类型的自动连接可能会有多个候选,因此经常需要在选择过程中加以控制。一种方法去完成这个控制就是使用@Qualifier注解
如果注入的属性允许为空@Autowired(required=false)
基于注解的配置[email protected]
javax.annotation.Resource javaee版本的通用注解可以替换autowire,
默认byName匹配
也可以与@Qualifier连用
基于注解的配置[email protected]
通过组件配置的方式自动配置依赖关系
建议添加name属性
基于注解的配置[email protected]@PreDestroy
初始化,销毁方法的处理