如何解决客户端对象依赖具体实现类的问题呢?
// MemoryUserDao是UserDao的具体实现
UserDao userDao = new MemoryUserDao();
userDao.save(user);
解决方案:工厂(Factory)模式
使用一个工厂类来实现 userDao 对象的创建,这样客户端只要知道这一个工厂类就可以了,不用依赖任何具 体的 UserDao 实现
创建 userDao 对象的工厂类 UserDaoFactory 代码如下:
public class UserDaoFactory {
public static UserDao createUserDao(){
return new MemoryUserDao();
}
}
客户端 UserRegister 代码片断如下:
UserDao userDao = UserDaoFactory. CreateUserDao();
userDao.save(user);
现在如果再要更换持久化方式,比如使用文本文件持久化用户信息。就算有再多的客户代码调用了用户持久化对象我们都不用担心了。因为客户端和用户持久化对象的具体实现完全解耦。我们唯一要修改的只是一个类。
工厂模式改进
消除更换持久化方式时带来的硬编码。
在属性文件中可以这样配置: userDao = com.test.MemoryUserDao 。 UserDao 的工厂类将从这个属性文件 中取得 UserDao 实现类的全名
public class UserDaoFactory {
public static UserDao createUserDao(){
String className = "";
// ......从属性文件中取得这个UserDao的实现类全名。
UserDao userDao = null;
try {
userDao = (UserDao)Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace(); }
return userDao;
}
工厂模式的终极方案——IoC 模式
使用 IoC 容器
public class UserRegister {
private UserDao userDao = null;//由容器注入的实例对象
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
// UserRegister的业务方法
}
Spring 的配置文件 applicationContext.xml 代码片断如下:
<bean id="userRegister" class="com.dev.spring.simple.UserRegister">
<property name="userDao"><ref local="userDao"/></property>
</bean>
<bean id="userDao" class="com.dev.spring.simple.MemoryUserDao"/>
BeanFactory
BeanFactory 是 Spring 的“心脏”。它就是 Spring IoC 容器的真面目。Spring 使用 BeanFactory 来实例化、配置和管理 Bean。但是,在大多数情况我们并不直接使用 BeanFactory,而是使用 ApplicationContext。它也是 BeanFactory 的一个实现,但是它添加了一系列“框架”的特征,比如:国际化支持、资源访问、事件传播等。
BeanFactory 其实是一个接口-org.springframework.beans.factory.BeanFactory,它可以配置和管理几乎所有的 Java 类。当然,具体的工作是由实现 BeanFactory 接口的实现类完成。我们最常用的 BeanFactory 实现是org.springframework.beans.factory.xml.XmlBeanFactory。它从 XML 文件中读取 Bean 的定义信 息。当 BeanFactory 被创建时,Spring 验证每个 Bean 的配置。当然,要等 Bean 创建之后才能设置 Bean 的属性。单例(Singleton)Bean 在启动时就会被 BeanFactory 实例化,其它的 Bean 在请求时创建。根据 BeanFactory 的 Java 文档(Javadocs)介绍,“Bean 定义的持久化方式没有任何的限制:LDAP、RDBM S、XML、属性文件,等等”。现在 Spring 已提供了 XML 文件和属性文件的实现。无疑,XML 文件是定义 Bean 的最佳方式。
BeanFactory 是初始化 Bean 和调用它们生命周期方法的“吃苦耐劳者”。注意,BeanFactory 只能管理单 例(Singleton)Bean 的生命周期。它不能管理原型(prototype,非单例)Bean 的生命周期。这是因为原型 Bea n 实例被创建之后便被传给了客户端,容器失去了对它们的引用。
BeanFactory管理Bean(组件)的生命周期
Bean 的生命周期,它是由 IoC 容器控制。IoC 容器定义 Bean 操作的规则,即 Bean 的定义。Bean 的定义包含了 BeanFactory 在创建 Bean 实例时需要的所有信息。BeanFactory 首先 通过构造函数创建一个 Bean 实例,之后它会执行 Bean 实例的一系列之前初始化动作,初始化结束 Bean 将进 入准备就绪(ready)状态,这时应用程序就可以获取这些 Bean 实例了。最后,当你销毁单例(Singleton)B ean 时,它会调用相应的销毁方法,结束 Bean 实例的生命周期。
使用 Spring 定义了一个用户持久化类:
<bean id="userDao" class="com.dev.spring.simple.MemoryUserDao"/>
这是一个最简单的 Bean 定义。它类似于调用了语句:
MemoryUserDao userDao = new MemoryUserDao()。
id属性必须是一个有效的 XML ID,这意味着它在整个 XML 文档中必须唯一。它是一个 Bean 的“终身代号(95 27)”。同时你也可以用 name 属性为 Bean 定义一个或多个别名(用逗号或空格分开多个别名)。name 属 性允许出现任意非法的 XML 字母。
name="userDao*_1, userDao*_2"
class属性定义了这个 Bean 的类名(包名+类名)。Spring 能管理几乎所有的 Java 类。一般情况,这 个 Java 类会有一个默认的构造函数,用 set 方法设置依赖的属性。
Bean 属性:
<bean
# id:Bean 的唯一标识名。它必须是合法的 XML ID,在整个 XML 文档中唯一。
id="beanId"(1)
# name: 用来为 id 创建一个或多个别名。它可以是任意的字母符合。多个别名之间用逗号或空格分开。
name="beanName"(2)
# class: 用来定义类的全限定名(包名+类名)。只有子类 Bean 不用定义该属性。
class="beanClass"(3)
# parent: 子类 Bean 定义它所引用它的父类 Bean。这时前面的 class 属性失效。子类 Bean 会继承父类 Bean 的所有属性,子类 Bean 也可以覆盖父类 Bean 的属性。注意:子类 Bean 和父类 Bean 是同一个 Java 类。
parent="parentBean"(4)
# abstract(默认为”false”):用来定义 Bean 是否为抽象 Bean。它表示这个 Bean 将不会被实例 化,一般用于父类 Bean,因为父类 Bean 主要是供子类 Bean 继承使用。
abstract="true | false"(5)
# singleton(默认为“true”):定义 Bean 是否是 Singleton(单例)。如果设为“true”,则在 BeanF actory 作用范围内,只维护此 Bean 的一个实例。如果设为“flase”,Bean将是 Prototype(原型)状态,B eanFactory 将为每次 Bean 请求创建一个新的 Bean 实例。
singleton="true | false"(6)
# lazy-init(默认为“default”):用来定义这个 Bean 是否实现懒初始化。如果为“true”,它将在 Bea nFactory 启动时初始化所有的 Singleton Bean。反之,如果为“false”,它只在 Bean 请求时才开始创建 Sing leton Bean。
lazy-init="true | false | default"(7)
# autowire(自动装配,默认为“default”):它定义了 Bean 的自动装载方式。
• “no”:不使用自动装配功能。
• “byName”:通过 Bean 的属性名实现自动装配。
• “byType”:通过 Bean 的类型实现自动装配。
• “constructor”:类似于 byType,但它是用于构造函数的参数的自动组装。
• “autodetect”:通过 Bean 类的反省机制(introspection)决定是使用“constructor”还是使用“byT ype”。
autowire="no | byName | byType | constructor | autodetect | default"(8)
# dependency-check(依赖检查,默认为“default”):它用来确保 Bean 组件通过 JavaBean 描述的 所以依赖关系都得到满足。在与自动装配功能一起使用时,它特别有用。
• none:不进行依赖检查。
• objects:只做对象间依赖的检查。
• simple:只做原始类型和 String 类型依赖的检查
• all:对所有类型的依赖进行检查。它包括了前面的 objects 和 simple。dependency-check = "none | objects | simple | all | default"(9)
# depends-on(依赖对象):这个 Bean 在初始化时依赖的对象,这个对象会在这个 Bean 初始化之前 创建。
depends-on="dependsOnBean"(10)
# init-method:用来定义 Bean 的初始化方法,它会在 Bean 组装之后调用。它必须是一个无参数的方法。
init-method="method"(11)
# destroy-method:用来定义 Bean 的销毁方法,它在 BeanFactory 关闭时调用。同样,它也必须是一 个无参数的方法。它只能应用于singleton Bean。
destroy-method="method"(12)
# factory-method:定义创建该 Bean 对象的工厂方法。它用于下面的“factory-bean”,表示这个 Be an 是通过工厂方法创建。此时,“class”属性失效。
factory-method="method"(13)
# factory-bean:定义创建该 Bean 对象的工厂类。如果使用了“factory-bean”则“class”属性失效。
factory-bean="bean">(14)
</bean>
根据 bean 属性中描述的对象依赖来组装(wire)bean 实例。如:userDao 对象 的一个属性“sessionFactory”引用了另外一个 Bean 对象:
<bean id="userDao" class="com.dev.spring.simple.HibernateUserDao">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> </bean>
在 ref 标签中,我们使用了一个“local”属性指定它所引用的Bean对象。除了 local 属性之外,还有一些其它的属性可以用来指定引用对象。下面列出元素的所有可用的指定方式:
- bean:可以在当前文件中查找依赖对象,也可以在应用上下文(ApplicationContext)中查找其它配置文件的对象。
- local:只在当前文件中查找依赖对象。这个属性是一个 XML IDREF,所以它指定的对象必须存在,否则它的验证检查会报错。
- external:在其它文件中查找依赖对象,而不在当前文件中查找。
总的来说,和大部分的时候可以通用。“bean”是最灵活的方式,它允许你在多个文件之间共享 Bean。而“lo cal”则提供了便利的XML验证。
复杂的属性值
Spring 的 bean 工厂不仅允许用 String 值和其他 bean 的引用作为 bean 组件的属性值,还支持更复杂的值,例如数组、java.util.List、java.util.Map和java.util.Properties。数组、set、list和map中的值不仅可以是 String 类型,也可以是其他 bean 的引用;map 中的键、Properties 的键和值都必须是 String 类型的;map 中的值可以是 set、list 或者 map 类型 。
Null:
<property name=“bar”><null/></property>
List和数组:
<property name=“bar”>
<list>
<value>ABC</value>
<value>123</value>
</list>
</property>
Map:
<property name=“bar”>
<map>
<entry key=“key1”><value>ABC</value></entry>
<entry key=“key2”><value>123</value></entry>
</set>
</property>
Bean初始化
Bean 工厂使用 Bean 的构造函数创建 Bean 对象之后,紧接着它会做一件非常重要的工作——Bean 的初始 化。它会根据配置信息设置 Bean 的属性和依赖对象,执行相应的初始化方法。
自动装配
一般不推荐在大型的应用系统中使用自动装配。当然,它可以很好的用于小型应用系统。如果一个 Bean 声明被标志为“autowire(自动装配)”,bean 工厂会自动将其他的受管对象与其要求的依赖关系进行匹配,从而完成对象的装配——当然,只有当对象关系无歧义时才能完成自动装配。因为不需要明确指定某个协作对象,所以 可以带来很多的便利性。
举个例子,如果在 Bean 工厂中有一个 SessionFactory 类型的实例,HibernateUserDao 的 属性就可以获得这个实例。这里可以使用的 autowire 属性,就象这样:
<bean id="userDao" class="com.dev.spring.simple.HibernateUserDao" autowire=”byType”>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
.....
</bean>
注意,在 userDao 的定义中并没有明确引用。由于它的“autowire”被设置为“byType”,所有只要有一个类型为的属性并有一个 set 方法,Bean 工厂就会自动将组装到中。
如果一个应用有两个数据库,这时就对应有两个。这时 autowire="byType"就无法使用了。我们可以使用另外一种自动装配方式“byName”。它将根据属性的名称来匹配依赖对象,这样如果你的配置文件中可以同时存在多个类型相同的,只要他们定义的名称不同就可以了。这种方式的缺点是:需要精确匹配 Bean 的名称,即必须要保证属性的名称和它所依赖的 Bean 的名称相等,这样比较容易出错。
注意:我们还是强烈推荐手工指定 Bean 之间的依赖关系。这种用法最强大,因为它允许按名称引用特定的 Be an 实例,即使多个 Bean 具有相同类型也不会混淆。同时,你可以清楚的知道一个 Bean 到底依赖哪些其它的 Bean。如果使用自动装载,你只能去 Bean 的代码中了解。甚至,Bean 工厂也许会自动装载一些你根本不想 依赖的对象。
依赖检查
如果你希望 Bean 严格的设置所有的属性,“dependency-check”(依赖检查)属性将会非常有用。它默认为“none”,不进行依赖检查。“simple”会核对所有的原始类型和 String 类型的属性。“objects”只做对象间的关联检查(包括集合)。“all”会检查所有的属性,包括“simple”和“objects”。
举个例子:一个 Bean 有如下的一个属性:
private int intVar = 0;
public void setIntVar(int intVar) {
this.intVar = intVar;
}
这个 Bean 的配置文件设置了“dependency-check=”simple””。如果这个Bean的配置中没有定义这个属性“intVar”,则在进行这个 Bean 的依赖检查时就会抛出异常:org.springframework.beans.factory.Unsat isfiedDependencyException。
setXXX()
set 方法非常简单,它会给 class 注入所有依赖的属性。这些属性都必须是在配置文件中使用元素定义,它们可以是原始类型,对象类型(Integer,Long),null 值,集合,其它对象的引用。
afterPropertiesSet()
有两种方法可以实现 Bean 的之前初始化方法。
- 使用“init-method”属性,在 Spring 的配置文件中定义回调方法。
- 实现接口 InitializingBean 并实现它的 afterPropertiesSet() 方法。接口 InitializingBean 的代码如下:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
在 JavaBean 的所有属性设置完成以后,容器会调用 afterPropertiesSet() 方法,应用对象可以在这里执行任何定制的初始化操作。这个方法允许抛出最基本的 Exception 异常,这样可以简化编程模型。
在 Spring 框架内部,很多 bean 组件都实现了这些回调接口。但我们的 Bean 组件最好不要通过这种方式实现 生命周期的回调,因为它依赖于 Spring 的 API。无疑,第一种方法是我们的最佳选择。
init-method
init-method 的功能和 InitializingBean 接口一样。它定义了一个 Bean 的初始化方法,在 Bean 的所有属性设 置完成之后自动调用。这个初始化方法不用依赖于 Spring 的任何 API。它必须是一个无参数的方法,可以抛出 Exception。
例如:我们的 Bean 组件 UserManger 中定义一个初始化方法 init() 。这样,我们就可以在 Bean 定义时指定这 个初始化方法:
<bean id=”userManger” class=”com.dev.spring.um.DefaultUserManager” init-method=”init”>
......
</bean>
Bean 的准备就绪(Ready)状态
Bean 完成所有的之前初始化之后,就进入了准备就绪(Ready)状态。这就意味着你的应用程序可以取得这些 Bean,并根据需要使用他们。
在你关闭(或重启)应用程序时,单例(Singleton)Bean 可以再次获得生命周期的回调,你可以在这时销毁 Bean 的一些资源。第一种方法是实现 DisposableBean 接口并实现它的 destroy() 方法。更好的方法是用“dest roy-method”在 Bean 的定义时指定销毁方法。