`
足至迹留
  • 浏览: 486739 次
  • 性别: Icon_minigender_1
  • 来自: OnePiece
社区版块
存档分类
最新评论

<3> 高级bean装配

阅读更多
相对上篇,这次内容有些不太常用,但有些还是非常有用,不能忽略。掌握之后对理解spring的源码也很有好处。

一、父bean和子bean

bean也可以继承,为此spring提供了两个属性:
parent: 指明bean的id.它对于<bean>的作用就相当于java关键字extends(但功能又有所不同,后面会看到子bean可以和父bean具有不同的class类型).
abstract: 如果设置为true,就表示<bean>是抽象的,不能被实例化,一般作为父bean.但父bean不一定就是抽象的。

通过使用parent属性可以指定父bean,子bean可以从父bean继承<property>和class属性,无法继承depends-on, autowire, dependency-check, singleton, scope, lazy-init等属性。
如:
<bean id=”fatherBean” class=”com.Instrumentalist” abstract=”true”>
<property name=”instrument” ref=”saxophone” />
<property name=”song” value=”jingle bells” />
</bean>
作为父bean,abstract=”true”不是必须的,这里只是说这个bean不能实例化,只提供公共属性。
<bean id=”kenny” parent=”fatherBean” />
<bean id=”david” parent=”fatherBean” />
这两个子bean都继承了父bean的属性。同时,子bean还可以覆盖父bean的属性,如:
<bean id=”david” parent=”fatherBean” >
    <property name=”song” value=”young” />   ------这里就覆盖了song属性。
</bean>

Sping里,子bean还可以不必具有相同的父类型,两个class属性不同的bean仍然可以从一个父bean继承一组相同的属性。
如:
<bean id=”fatherBean” abstract=”true” >
<property name=”song” value=”rainbow” />
</bean>

<bean id=”taylor” class=”com.spring.Vocalist” parent=”fatherBean” />
<bean id=”stevie” class=”com.spring.Instrument” parent=”fatherBean” />
这两个子bean具有不同的class类型,只是继承了父类共同的属性。

二、Bean的方法注入
上一篇介绍了bean的常用注入方式,setter和constructor方式,这里介绍下第三种注入方式,方法注入。上一篇中提到了factory-method使用静态方法注入bean,下面介绍下静态工厂方法注入,实例工厂方法注入和Spring支持的另外两种形式的方法注入:
方法替换: 可以在运行时用新的实现替换现有方法(抽象或具体的)。
获取器注入:可以在运行时用新的实现替换现有方法(抽象或具体的),从spring上下文返回特定的bean。
1) 静态工厂方法注入(factory-method)
•  <bean id="foo" class="...Foo"> 
•      <property name="barInterface"> 
•          <ref bean="bar"/> 
•      </property> 
•  </bean> 
•  
•  <bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/>
对于bar这个bean, class指定静态方法工厂类,factory-method指定工厂方法名称,必须是static的,然后容器调用该静态方法工厂类的指定工厂方法(getInstance),并返回方法调用后的结果,即BarInterfaceImpl的实例。也就是说,为foo注入的 bar实际上是BarInterfaceImpl的实例,即方法调用后的结果,而不是静态工厂方法类 (StaticBarInterfaceFactory)。我们可以实现自己的静态工厂方法类返回任意类型的对象实例,但工厂方法类的类型与工厂方法返回 的类型没有必然的相同关系。
某些时候,有的工厂类的工厂方法可能需要参数来返回相应实例,而不一定非要像我们的getInstance()这样没有任何参数。对于这种情况,可以通过<constructor-arg>来指定工厂方法需要的参数
•  <bean id="bar" class="...StaticBarInterfaceFactory"  factory-method="getInstance"> 
•         <constructor-arg> 
•               <ref bean="foobar"/> 
•         </constructor-arg> 
•  </bean> 
•  
•  <bean id="foobar" class="...FooBar"/>
唯一需要注意的就是,针对静态工厂方法实现类的bean定义,使用<constructor-arg>传入的是工厂方法的参数,而不是静态工厂方法实现类的构造方法的参数。
还可以参考:http://book.51cto.com/art/200908/147087.htm

2) 非静态工厂方法注入(factory-bean)
<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"/>
NonStaticBarInterfaceFactory是作为正常的bean注册到容器的,而bar的定义则与静态工厂方法的定义有些不同。现在使用factory-bean属性来指定工厂方法所在的工厂类实例,而不是通过class属性来指定工厂方法所在类的类型。指定工厂方法名则相同,都是通过factory-method属性进行的,有了factory-bean指定的实例工厂类,这里factory-method指定的工厂方法就不需要一定是static的了。
如果非静态工厂方法调用时也需要提供参数的话,处理方式是与静态的工厂方法相似的,都可以通过<constructor-arg>来指定方法调用参数。
还可以参考:http://book.51cto.com/art/200908/147088.htm

3)方法替换(replaced-method)
<bean id=”magicBox” class=”com.MagicBoxImpl”>
<replaced-method name=”getContents” replacer=”tigerReplacer” />
</bean>
<bean id=”tigerReplacer” class=”com.TigerReplacer” />
本例中replaced-method的name属性指定magicBox的getContents()方法将被替换,替换成tigerReplacer的reimplement方法。tigerReplacer实现了spring的MethodReplacer接口,需要实现此接口的reimplement方法。

4) 获取器注入(lookup-method)
Spring利用<lookup-method>配置元素来实现获取器注入。
<bean id=”stevie” class=”com.spring.Instrumentalist”>
<lookup-method name=”getInstrument” bean=”guitar” />
</bean>
<bean id=”guitar” class=”com.Guitar” />
<lookup-method>的name属性指明了Instrumentalist的getInstrument()方法要被替换,bean=”guitar”指明getInstrument()方法替换后会返回bean(id=”guitar”)。任何返回值不是void的方法都可以被替换掉。
这个是利用cglib实现的,
可以参考:http://www.360doc.com/content/07/0327/13/18042_416051.shtml

当<bean id=”guitar” class=”com.Guitar” scope=”prototype” />bean是原型作用域时,lookup-mehtod替换掉的方法返回值每次获取的都是不同的guitar。这一点很有用,当singleton的bean依赖非singleton的bean时,用普通的注入方式则只会注入一次,每次获取的非singleton类型的bean也都一样了,这时用lookup-mehtod方法注入就对了。

5) factorybean(注意跟上面的标签<factory-bean>不是同一个概念)
Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean。工厂Bean跟普通Bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂Bean的getObject方法所返回的对象。在Spring框架内部,AOP相关的功能及事务处理中,很多地方使用到工厂Bean。
所谓FactoryBean就是指实现了spring的org.springframework.beans.factory.FactoryBean接口的bean类。配置该bean时跟普通bean一样,没有任何不同。
当配置文件中<bean>的class属性配置的实现类是FactoryBean时,通过getBean方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。这样就可以实现自定义功能的bean了,比如把一个特殊字符串解析并注入到一个对象属性里。
还可以参考:http://wenku.baidu.com/view/ad5b414ef7ec4afe04a1dfa7.html


三、 注入非sping创建的bean(使用@Configurable),省略不记录了。

四、注册自定义属性编辑器
举个普通的例子
<bean id=”aa” class=”…”>
  <constructor-arg type=”int” value=”15” />
  <constructor-arg type=”java.lang.String” value=”15” />
</bean>
这里两个属性值都是15,类型一个是int,一个是String,即使不指明type(如果bean的构造函数唯一),spring也会自动解析正确类型。但还有些复杂类型spring也能自动解析,比如java.net.URL。可以直接配置:
<property name=”wsdlDocUrl” value=http://www.service.wsdl />
这是因为spring内部实现了一些类,这些类实现了java自带的java.beans.ProperyEditor接口。Java.beans.PropertyEditorSupport类是java自己提供的实现了该接口的类,我们自己(包括spring也是)直接继承PropertyEditorSupport类(根据需要重写setAsText或getAsText方法,一般给属性注入值只需要重写setAsText方法)就可以实现一些自己的属性编辑器类,然后注册到spring,这样就可以提供一些自动解析复杂对象的功能了。



Spring提供了几个自定义属性编辑器:




自定义属性编辑器的步骤:
1. 继承PropertyEditorSupport类,根据需要重写setAsText或getAsText方法/
2. 注册到spring中。使用CustomEditorConfigurer注册,这是一个BeanFactoryPostProcessor。
还可以参考:
http://www.cnblogs.com/rollenholt/archive/2012/12/27/2835191.html
这个里面用到了spring自带的属性编辑器
org.springframework.beans.propertyeditors.CustomDateEditor。
http://blog.chinaunix.net/uid-21227800-id-65926.html
这个里面可以参考写出自己的属性编辑器。

注:自定义属性编辑器和factorybean有类似的功能,都可以以灵活的方式给bean注入复杂属性,但又有区别,这个可以根据实际使用场景决定使用哪个。
还可以参考:http://blog.csdn.net/intlgj/article/details/5662399


五、使用spring的特殊bean

有些特殊的bean被当作spring框架本身的一部分,可以:
  @通过对bean配置的后处理来介入bean的创建和bean工厂的生命周期。
  @从外部属性文件加载配置信息。
  @从属性文件加载文本信息,包括国际化的消息。
  @监听和响应由其他bean和spring容器本身发布的程序事件。
  @知道它们在spring容器里的身份。

1. 后处理bean
在第二篇《基本bean配置》里有bean的生命周期图,图中可以看到spring提供了两个机会让我们可以介入bean的生命周期(这两个机会是调用BeanPostProcessor的预初始化方法和后初始化方法。),查看或改变它的配置,这杯成为“后处理”。所谓后处理就是bean的创建和装配之后来修改。BeanPostProcessor接口为我们提供了这两个机会。



postProcessBeforeInitialization()方法是在bean初始化之前被调用(即InitializingBean的afterPropertiesSet()方法和bean的自定义init-method)之前被调用的。类似的,postProcessAfterinitialization()方法是在初始化之后立即被调用的。

实现步骤:
1) 自定义类实现BeanPostProcessor接口,实现该接口的两个方法。
2) 如果bean是在BeanFactory中,则需要编写代码,使用工厂的addBeanPostProcessor注册每个BeanPostProcessor.
        如果是使用的上下文ApplicationContext,则只需要在容器里把自定义类定义为一个普通bean就可以了,容器会自动识别。这也是第一篇导读里说的ApplicationContext的更多的强大功能。
Spring框架在私下使用了BeanPostProcessor的多个实现,比如
ApplicationContextAwareProcessor就是一个BeanPostProcessor.

2. bean工厂的后处理
bean被加载后,当BeanPostProcessor对它进行后处理时,BeanFactoryPostProcessor(只能是针对上下文容器,不能是beanFactory容器)对整个spring容器进行后处理。



在全部bean定义被加载后(加载,是在实例化之前的步骤),在任何一个bean被实例化之前,spring容器会调用postProcessBeanFactory()方法。
前面出现过的自定义编辑器CustomEditorConfigurer就是BeanFactoryPostProcessor的一个实现。
BeanFactoryPostProcessor另一个很有用的实现是PropertyPlaceholderConfigurer,它从一个或多个外部属性文件(.properties文件)加载属性,还可以填充bean装配文件里的占位变量(如:${url}).
注:所有相关BeanFactoryPostProcessor的应用都只能在上下文容器(ApplicationContext,不是BeanFactory)中使用。
如:



可以配置成:
<bean id=”propertyConfigurer”
class=”org.springframework.beans.factory.config.PropertyPlaceholderConfigurer”>
<property name=”location” value=”jdbc.properties” />
</bean>
Location属性告诉spring到哪寻找属性文件。如果指定多个文件还可以使用:
<property name=”locations”>
<list>
    <value> jdbc.properties</value>
    <value> security.properties</value>
</list>
</property>
本例中,jdbc.properties包含下面信息:
Database.url=jdbc:hsqldb:Training
Datebase.driver=org.hsqldb.jdbcDriver
Database.user=appUser
Database.password=password
现在就可以定义dataSource了:



这样就可以不用硬编码了。

注:除了可以向上面那样指定属性文件,还可以使用Spring 2.5引进Context命名空间, 可以通过context:property-placeholder元素进行配置。
可以参考:http://www.2cto.com/kf/201211/167827.html


3. 国际化信息支持
可以通过spring提供的MessageSource接口实现国际化信息。Spring提供了默认的MessageSource实现,ResourceBundleMessageSource等。
这里不详细记录了,后面有问题时再详细研究。
可参考:
http://beastiedog.blog.163.com/blog/static/23984625200642224329415/
http://hi.baidu.com/iduany/item/d52245ab11021611a9cfb771
http://blog.csdn.net/xiongyayun428/article/details/7063835
http://blog.csdn.net/xiongyayun428/article/details/7062772

4. 发布事件和监听事件
依赖注入是Spring提高程序对象之间松耦合的主要手段,但并不唯一。对象之间交互的另一种方式是发布和监听程序事件。
在spring里,容器中的任何bean都可以作为事件监听者或发布者或两者都是。
1) 创建事件,只需要实现ApplicationEvent类。
2) 发布事件,利用ApplicationContext(这就要求bean能获取到上下文,后面会讲怎么获取)接口的publishEvent()方法可以发布ApplicationEvents。
除了bean要发布的事件,spring容器本身就会发布一些事件,比如:
ContextCloseEvent:程序上下文关闭事件。
ContextRefreshedEvent:程序上下文被初始化或刷新事件。
RequestHandleEvent:当一个请求被处理时,在web上下文里发布。
3) 创建监听器,只需要实现ApplicationListener接口,这个接口要求bean实现onApplicationEvent()方法来响应事件。
4) 注册监听器,只需要创建监听器的bean,如:
<bean id=”refreshListener” class=”com.spring.foo.ResfreshListener” />
当容器在程序上下文里加载这个bean时会注意到它实现了ApplicationListener,并会在时间被发布时调用它的onApplicationEvent()方法。

注:程序事件是同步处理的,因此对事件的处理要迅速,否则会降低程序的性能。
Bean必须要了解容器(即能获取到容器实例)才能发布事件。

5 让bean了解容器
1)让bean了解bean自己的名称
通过实现BeanNameAware接口,告诉bean它自己的名字。这个接口只有一个setBeanName()方法。只要实现了这个接口,bean被加载时,容器会检测到它实现了BeanNameAware,然后自动调用setBeanName,把<bean>元素里的id或name属性传递过去。

3) 让bean了解所在的容器
跟上面类似,只要bean实现了ApplicationContextAware和BeanFactoryAware接口就可以了。

6.spring里还可以内嵌脚本化的bean,可以是非java实现的bean,没有研究。不梳理了。
  • 大小: 58.6 KB
  • 大小: 50.3 KB
  • 大小: 36.6 KB
  • 大小: 90.7 KB
  • 大小: 88.4 KB
分享到:
评论

相关推荐

    《精通Spring2.X企业应用开发详解》20-23

    概述&lt;br&gt;第1章 Spring概述&lt;br&gt;第2章 快速入门&lt;br&gt;第2篇 Spring核心技术&lt;br&gt;第3章 IoC容器概述&lt;br&gt;第4章 在IoC容器中装配Bean&lt;br&gt;第5章 Spring容器高级主题&lt;br&gt;第6章 Spring AOP基础&lt;br&gt;第7章 基于@AspectJ和Schema的...

    《精通Spring2.X企业应用开发详解》16-19章

    概述&lt;br&gt;第1章 Spring概述&lt;br&gt;第2章 快速入门&lt;br&gt;第2篇 Spring核心技术&lt;br&gt;第3章 IoC容器概述&lt;br&gt;第4章 在IoC容器中装配Bean&lt;br&gt;第5章 Spring容器高级主题&lt;br&gt;第6章 Spring AOP基础&lt;br&gt;第7章 基于@AspectJ和Schema的...

    《精通Spring2.X企业应用开发详解》随书源码1-15章

    概述&lt;br&gt;第1章 Spring概述&lt;br&gt;第2章 快速入门&lt;br&gt;第2篇 Spring核心技术&lt;br&gt;第3章 IoC容器概述&lt;br&gt;第4章 在IoC容器中装配Bean&lt;br&gt;第5章 Spring容器高级主题&lt;br&gt;第6章 Spring AOP基础&lt;br&gt;第7章 基于@AspectJ和Schema的...

    xmljava系统源码-SpringInAction4:《SpringInAction4th》学习笔记

    xml java系统源码 SpringInAction4 《Spring In Action 4th》学习笔记 第一部分 Spring的核心 1. Spring之旅 ...bean的初始化过程 ...装配Bean ...&lt;context&gt; 配置自动扫描 在基于Java的配置中使用@Configura

    Spring in Action 中文版 第2版 第二部分

    第3章 高级bean装配 第4章 通知bean 第二部分 企业spring 第5章 使用数据库 第6章 事务管理 第7章 保护spring 第8章 spring和基于pojo的远程服务 第9章 在spring中建立契约优先web服务 第10章 spring消息 第...

    Spring in Action 中文版 第2版 第一部分

    第3章 高级bean装配 第4章 通知bean 第二部分 企业spring 第5章 使用数据库 第6章 事务管理 第7章 保护spring 第8章 spring和基于pojo的远程服务 第9章 在spring中建立契约优先web服务 第10章 spring消息 第...

    Spring3.x企业应用开发实战(完整版) part1

    4.6 <bean>之间的关系 4.6.1 继承 4.6.2 依赖 4.6.3 引用 4.7 整合多个配置文件 4.8 Bean作用域 4.8.1 singleton作用域 4.8.2 prototype作用域 4.8.3 Web应用环境相关的Bean作用域 4.8.4 作用域依赖问题 4.9 ...

    Spring.3.x企业应用开发实战(完整版).part2

    4.6 <bean>之间的关系 4.6.1 继承 4.6.2 依赖 4.6.3 引用 4.7 整合多个配置文件 4.8 Bean作用域 4.8.1 singleton作用域 4.8.2 prototype作用域 4.8.3 Web应用环境相关的Bean作用域 4.8.4 作用域依赖问题 4.9 ...

    Spring in Action(第2版)中文版

    第3章高级bean装配 3.1声明父bean和子bean 3.1.1抽象基bean类型 3.1.2抽象共同属性 3.2方法注入 3.2.1基本的方法替换 3.2.2获取器注入 3.3注入非springbean 3.4注册自定义属性编辑器 3.5使用spring的特殊...

    Spring in Action(第二版 中文高清版).part2

    第3章 高级Bean装配 3.1 声明父Bean和子Bean 3.1.1 抽象基Bean类型 3.1.2 抽象共同属性 3.2 方法注入 3.2.1 基本的方法替换 3.2.2 获取器注入 3.3 注入非Spring Bean 3.4 注册自定义属性编辑器 3.5 使用...

    Spring in Action(第二版 中文高清版).part1

    第3章 高级Bean装配 3.1 声明父Bean和子Bean 3.1.1 抽象基Bean类型 3.1.2 抽象共同属性 3.2 方法注入 3.2.1 基本的方法替换 3.2.2 获取器注入 3.3 注入非Spring Bean 3.4 注册自定义属性编辑器 3.5 使用...

    高级开发spring面试题和答案.pdf

    面试高级开发的期间整理的面试题目,记录我面试遇到过的spring题目以及答案 目录 spring ThreadLocal的底层对象; 为什么@Service和@Repository放到实现类上面而不是接口类上面; spring 三种注入(就是从spring容器...

    SpringBoot高级特性-自动装配&自定义starter

    Condition是Spring在4.0引入的条件判断功能,Spring根据这个功能选择性的创建Bean。 下面我们使用SpringDataRedis 来演示一下 案例 创建SpringBoot工程,引入springboot基础设施依赖 org.springframework.boot

    Spring-Reference_zh_CN(Spring中文参考手册)

    9.5.5. &lt;tx:advice/&gt; 有关的设置 9.5.6. 使用 @Transactional 9.5.6.1. @Transactional 有关的设置 9.5.7. 插入事务操作 9.5.8. 结合AspectJ使用 @Transactional 9.6. 编程式事务管理 9.6.1. 使用 ...

    Spring中文帮助文档

    9.5.5. &lt;tx:advice/&gt; 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 事务传播 9.5.8. 通知事务操作 9.5.9. 结合AspectJ使用 @Transactional 9.6. 编程式事务管理 9.6.1. 使用TransactionTemplate 9.6.2. ...

    Spring API

    9.5.5. &lt;tx:advice/&gt; 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 事务传播 9.5.8. 通知事务操作 9.5.9. 结合AspectJ使用 @Transactional 9.6. 编程式事务管理 9.6.1. 使用TransactionTemplate 9.6.2. ...

    Spring攻略(第二版 中文高清版).part2

    1.12 用@Autowired和@Resource自动装配Bean 41 1.12.1 问题 41 1.12.2 解决方案 41 1.12.3 工作原理 41 1.13 继承Bean配置 47 1.13.1 问题 47 1.13.2 解决方案 47 1.13.3 工作原理 48 1.14 从...

    Spring攻略(第二版 中文高清版).part1

    1.12 用@Autowired和@Resource自动装配Bean 41 1.12.1 问题 41 1.12.2 解决方案 41 1.12.3 工作原理 41 1.13 继承Bean配置 47 1.13.1 问题 47 1.13.2 解决方案 47 1.13.3 工作原理 48 1.14 从...

    spring chm文档

    9.5.5. &lt;tx:advice/&gt; 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 插入事务操作 9.5.8. 结合AspectJ使用 @Transactional 9.6. 编程式事务管理 9.6.1. 使用 TransactionTemplate 9.6.2. 使用 ...

Global site tag (gtag.js) - Google Analytics