spring-IoC

如何解决客户端对象依赖具体实现类的问题呢?

// 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 实例的生命周期。

Paste_Image.png

使用 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 的定义时指定销毁方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • Spring容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof阅读 2,794评论 1 24
  • 来源:关于Spring IOC (DI-依赖注入)你需要知道的一切作者:zejian Dao层(AccountDa...
    杨井阅读 5,324评论 0 27
  • 1- IOC的概念 IOC:也即控制反转,DI即依赖注入,控制反转IOC和依赖注入DI其实就是同个概念的两个不同...
    zhanglbjames阅读 2,995评论 1 3
  • 马蓉和宝宝离婚了 宝宝很伤心 网友很伤心 我看到宝宝那么伤心 于是,我也就很伤心 但我还是想说 马蓉,真心谢谢你 ...
    杨小龙说阅读 227评论 0 1