本书可作为王富强所著<<Spring揭秘>>一书的读书笔记 

第四章 BeanFactory的xml之旅

bean的scope

scope有时被翻译为"作用域",scope用来声明容器中的对象所应该处的限定场景或者说该对象的存活时间,即容器在对象进入其相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象。

scope共有5个,singleton,prototype,request,session,global session

前两个是通用的,中间三个只是在web系统中才才用到,最后一个global session只有应用在基于portlet的Web应用程序中才有意义,它映射到portlet的global范围的session。

我们可以通过使用<bean>的singleton或者scope属性来指定相应对象的scope,其中,scope属性只能在 SD格式的文档声明中使用,类似于如下代码所演示的形式:   

DTD: 

<bean id="mockObject1" class="...MockBusinessObject" singleton="false"/> 

XSD: 

<bean id="mockObject2" class="...MockBusinessObject"  scope="prototype"/>

singleton

singleton:容器中只有一个对象,谁要都是这个一个。只有容器不销毁或者退出,她就一直存在。

配置情况,因为scope默认的就是singleton,下面三种方式效果一样。

<!-- DTD or XSD --> 

<bean id="mockObject1" class="...MockBusinessObject"/> 

<!-- DTD --> 

<bean id="mockObject1" class="...MockBusinessObject" singleton="true"/> 

<!-- XSD --> 

< bean id="mockObject1" class="...MockBusinessObject" scope="singleton"/>

prototype

prototype:每次请求都是一个新的对象。而且,一旦这个新的对象给了请求方,那么容器就不在持有对这个对象的引用。就像嫁出去的姑娘,泼出去的水,娘家以后不管了。

配置方式:

<!-- DTD --> 

<bean id="mockObject1" class="...MockBusinessObject" singleton="false"/> 

<!-- XSD --> 

<bean id="mockObject1" class="...MockBusinessObject"  scope="prototype"/>

request

request:和prototype没什么区别,在web情况下每来一个请求,分配一个实例。

配置方式

<bean id="requestProcessor" class="...RequestProcessor"  scope="request"/>

session

session:对于放到session中的信息,可以将scope设置为session,它除了比request更长的存活时间外,其他方面没什么区别。

配置方式:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

工厂方法与FactoryBean

我们看下面的代码:

public class Foo
{
  private BarInterface barInstance;
  public Foo()    {
     // 我们应该避免这样做
     // instance = new BarInterfaceImpl();
  }
  // ...
}  

为什么要避免这么做?

因为接口与实现类之间有了耦合。

有了耦合咋了,为什么就不能有耦合。

如果我不想用BarInterfaceImpl,先换成Bar2InterfaceImpl。

上面的方法就是得改Foo类的编码,如果n个类中都有instance = new BarInterfaceImpl(); 那么我岂不是得要改n处。

另一方面,如果实现类(例如barInstance)是来自外部jar包的,你没办法把它纳入spring的管理范围,哪有如何?

为之奈何?

工厂方法

public class Foo { 

  private BarInterface barInterface;
  public Foo() {
    // barInterface = BarInterfaceFactory.getInstance();
    // 或者
    // barInterface = new BarInterfaceFactory().getInstance();
  }
   ...
}  

这样一来,Foo直接依赖于BarInterfaceFactory。如果产品有了变化,我只用改工厂,而不用"告诉"每一个使用者产品改变了。

静态工厂方式

public class StaticBarInterfaceFactory {
  public static BarInterface getInstance()   {
   return new BarInterfaceImpl();
  }
}  

Foo中的BarInterface有对应的get/set方法。

要想通过静态工厂把BarInterfaceImpl注入到Foo中,我们可以这样

xml如下:

<bean id="foo" class="...Foo">
  <property name="barInterface">
   <ref bean="bar"/>
  </property>
</bean> 

<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/>  

bean中有了factory-method,就说明这个类并不是使用默认的构造方法产生的,而是使用getInstance方法。再换句话说,bar这个bean最后的返回不是StaticBarInterfaceFactory,而是getInstance这个方法的返回值。

如果getInstacne需要参数呢?例如:

public class StaticBarInterfaceFactory {
  public static BarInterface getInstance(Foobar foobar) {
   return new BarInterfaceImpl(foobar);
  }
} 

xml如下:

<bean id="foo" class="...Foo">
  <property name="barInterface">
   <ref bean="bar"/>
  </property>
</bean> 

<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance">
  <constructor-arg>
   <ref bean="foobar"/>
  </constructor-arg>
</bean> 

<bean id="foobar" class="...FooBar"/>  

只有bar中的constructor-arg下的参数是给getInstance用的,而不是StaticBarInterfaceFactory。(另一方面,静态工厂也没有显示的构造方法呀)

非静态工厂方式

public class NonStaticBarInterfaceFactory {
	public BarInterface getInstance() {
		 return new BarInterfaceImpl();
	}
	...
} 

因为工厂方法为非静态的,我们只能通过某个NonStaticBarInterfaceFactory实例来调用该方法(哦,错了,是容器来调用),那么也就有了如下的配置内容:

<bean id="foo" class="...Foo">
  <property name="barInterface">
   <ref bean="bar"/>
  </property>
</bean> 

<bean id="barFactory" class="...NonStaticBarInterfaceFactory"/>
<bean id="bar" factory-bean="barFactory" factory-method="getInstance"/> 

bar这个bean,中有factory-bean这个属性,就是告诉容器,bar=barFactory.getInstance();

如果barFactoy的getinstance也需要参数呢?

同样使用:<constructor-arg>

FactoryBean

上面写的工厂方法还有一个替代方法,就是FactoryBean

FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,请不要将其与容器名称BeanFactory相混淆。FactoryBean,其主语是Bean,定语为Factory,也就是说,它本身与其他注册到容器的对象一样,只是一个Bean而已,只不过,这种类型的Bean本身就是生产对象的工厂(Factory)。 

我们先看看FactoryBean的签名。

public interface FactoryBean {
  Object getObject() throws Exception;
  Class getObjectType(); //如果不能确定返回的类型,getObjectType就返回null
  boolean isSingleton();
}  

完全能看懂,没有压力呀。

如果我们想每次得到的日期都是第二天,可以使用如下的代码

import org.joda.time.DateTime;
import org.springframework.beans.factory.FactoryBean; 

public class NextDayDateFactoryBean implements FactoryBean { 

  public Object getObject() throws Exception {
   return new DateTime().plusDays(1);
  } 

  public Class getObjectType() {
   return DateTime.class;
  } 

  public boolean isSingleton() {
   return false;
  } 

} 

要使用NextDayDateFactoryBean,只需要如下这样将其注册到容器即可:

<bean id="nextDayDateDisplayer" class="...NextDayDateDisplayer">
  <property name="dateOfNextDay">
   <ref bean="nextDayDate"/>
  </property>
</bean> 

<bean id="nextDayDate" class="...NextDayDateFactoryBean">
</bean>

从xml配置上似乎和以前没有什么差别,但是nextDayDateDisplayer里的属性dateOfNextDay并不是NextDayDateFactoryBean而是NextDayDateFactoryBean中getObject方法返回的对象。

public class NextDayDateDisplayer {
  private DateTime dateOfNextDay;
  // 相应的setter方法
  // ...
} 

如果一定要取得FactoryBean本身的话,可以通过在bean定义的id之前加前缀&来达到目的。

Object nextDayDate = container.getBean("nextDayDate");
assertTrue(nextDayDate instanceof DateTime);
Object factoryBean = container.getBean("&nextDayDate");
assertTrue(factoryBean instanceof FactoryBean);
assertTrue(factoryBean instanceof NextDayDateFactoryBean); 

Object factoryValue = ((FactoryBean)factoryBean).getObject();
assertTrue(factoryValue instanceof DateTime);
assertNotSame(nextDayDate, factoryValue);
ssertEquals(  ((DateTime)nextDayDate).getDayOfYear(),
			((DateTime)factoryValue).getDayOfYear()   ); 

感谢glt

Spring揭秘 读书笔记 三 bean的scope与FactoryBean的更多相关文章

  1. spring揭秘 读书笔记 六 bean的一生

    我们知道,Spring容器具有对象的BeanDefinition来保存该对象实例化时需要的数据. 对象通过container.getBean()方法是才会初始化该对象. BeanFactory 我们知 ...

  2. spring揭秘 读书笔记 二 BeanFactory的对象注册与依赖绑定

    本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,而且IoC Service Pr ...

  3. spring揭秘 读书笔记 一 IoC初探

    本文是王福强所著<<spring揭秘>>一书的读书笔记 ioc的基本概念 一个例子 我们看下面这个类,getAndPersistNews方法干了四件事 1 通过newsList ...

  4. spring揭秘 读书笔记 二 BeanFactory的对象注冊与依赖绑定

    本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,并且IoC Service Pr ...

  5. Spring揭秘读书笔记 八 数据访问异常体系

    这篇博客 来自spring揭秘一书的第十三章 为什么要有访问异常都有一个体系,这个我们得从DAO模式说起. DAO模式 任何一个系统,不管是一个最简单的小系统,还是大规模的系统,都得跟数据打交道,说白 ...

  6. Spring揭秘 读书笔记 四----方法注入

    我们知道,拥有prototype类型scope的bean,在请求方每次向容器请求该类型对象的时候,容器都会返回一个全新的该对象实例. 我们看下面的例子: public class MockNewsPe ...

  7. Spring揭秘 读书笔记 七 BeanFactory的启动分析

    首先,先看我自己画的BeanFactory启动时的时序图. 第一次接触时序图,可能有些地方画的不是很符合时序图的规则,大家只关注调用顺序即可. public static void main(Stri ...

  8. spring揭秘读书笔记----spring的ioc容器之BeanFactory

    spring的ioc容器是一种特殊的Ioc Service Provider(ioc服务提供者),如果把普通的ioc容器认为是工厂模式(其实很相似),那spring的ioc容器只是让这个工厂的功能更强 ...

  9. Spring揭秘 读书笔记 五 容器的启动

    Spring的IoC容器所起的作用,就是生产bean,并维持bean间的依赖关系.它会以某种方式加载Configuration Metadata(通常也就是XML格式的配置信息),然后根据这些信息绑定 ...

随机推荐

  1. IDEA中Git的使用

    工作中多人使用版本控制软件协作开发,常见的应用场景归纳如下: 假设小组中有两个人,组长小张,组员小袁 场景一:小张创建项目并提交到远程Git仓库 场景二:小袁从远程Git仓库上获取项目源码 场景三:小 ...

  2. Bootstrap3 表格-响应式表格

    将任何 .table 元素包裹在 .table-responsive 元素内,即可创建响应式表格,其会在小屏幕设备上(小于768px)水平滚动.当屏幕大于 768px 宽度时,水平滚动条消失. Fir ...

  3. 六星经典CSAPP-笔记(10)系统IO

    六星经典CSAPP-笔记(10)系统I/O 1.Unix I/O 所有语言的运行时系统都提供了高抽象层次的I/O操作函数.例如,ANSI C在标准I/O库中提供了诸如printf和scanf等I/O缓 ...

  4. Eclipse中配置javap命令

    Run→External Tools→External Tools Configurations-进入如下图二所示的Program配置界面.也可以通过如下图一所示的工具栏按钮进入Program配置界面 ...

  5. SQLite 删除表(http://www.w3cschool.cc/sqlite/sqlite-drop-table.html)

    SQLite 删除表 SQLite 的 DROP TABLE 语句用来删除表定义及其所有相关数据.索引.触发器.约束和该表的权限规范. 使用此命令时要特别注意,因为一旦一个表被删除,表中所有信息也将永 ...

  6. Dynamics CRM2016 业务流程之Task Flow(一)

    Task Flow 属于CRM移动端的特性,如果在项目实施中用不到CRM自带的APP或者对自APP不感冒的,那就没有往下看的必要了,移步吧. 该功能默认是不开启的,需要我们去系统设置中开启它,打勾,选 ...

  7. windows平台下 c/c++进行http通信的教训

    由于需要使用c++开发一个桌面应用软件,需要用到http请求进行通讯,也是本人第一次进行网络相关的开发工作,遇到了不少坑. 由于是在windows下开发和使用的应用软件,自然而然想到了调用Window ...

  8. Swift函数柯里化(Currying)简谈

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 下面简单说说Swift语言中的函数柯里化.简单的说就是把接收多 ...

  9. 如何将Ubuntu部署到U盘中,用U盘安装linux操作系统

    http://jingyan.baidu.com/article/d5c4b52be79960da560dc59f.html 用U盘装一个linux系统是非常简单的事情,不会就看看这篇文章吧,后期我会 ...

  10. 2.关于QT中数据库操作,简单数据库连接操作,数据库的增删改查,QSqlTableModel和QTableView,事务操作,关于QItemDelegate 代理

     Linux下的qt安装,命令时:sudoapt-get install qt-sdk 安装mysql数据库,安装方法参考博客:http://blog.csdn.net/tototuzuoquan ...