上篇文章我们对Spring做了初步的学习,了解了基本的依赖注入思想、学会简单的配置bean、能够使用Spring容器管理我们的bean实例等。但这还只是相对较浅显的内容,本篇将介绍bean的相关更高级的配置,主要涉及内容如下:

  • 三种方式配置Bean
  • 深入理解容器中的Bean
  • 管理Bean的生命周期
  • 高级的依赖关系配置
  • 使用XML Schema简化DTD配置
  • 使用SpEL表达式语言

一、三种方式配置Bean

     在这之前,我们一直使用下面这种方式配置我们的bean。

  1. <bean id="" class=""></bean>

在bean元素中指定两个属性,id属性指定了该实例在容器中唯一标识,class属性指定该实例的类型。我们也说过,Spring会使用反射技术读取class并创建一个该类型的实例返回。这种方式配置bean相对而言较常见,但是Spring中还有其他两种配置bean的方式,静态工厂和实例工厂。

静态工厂配置bean实例:

使用静态工厂配置bean实例,在bean元素中需要指定至少两个属性值。

  • class:指向静态工厂类
  • factory-method:指定用于生成bean的静态工厂方法

下面我们定义一个静态工厂类及其静态工厂方法:

  1. /*person bean*/
  2. public class Person {
  3. private String name;
  4. private int age;
  5. private String address;
  6. //省略setter方法
  7. }
  1. /*静态工厂类及静态工厂方法*/
  2. public class BeanStaticClass {
  3. public static Person getPerson(String name,int age){
  4. Person person = new Person();
  5. person.setName(name);
  6. person.setAge(age);
  7. return person;
  8. }
  9. }

配置bean:

  1. <bean id="person" class="MyPackage.BeanStaticClass" factory-method="getPerson">
  2. <constructor-arg value="single"/>
  3. <constructor-arg value="22"/>
  4. </bean>

如果需要向工厂传入参数,可以使用元素<constructor-arg value="" />传入参数。最终外部从容器中获取person实例,打印信息:

这里需要再说明一点的是,除了使用<constructor-arg value="" />传入参数去初始化bean的属性外,我们也是可以通过property元素驱动Spring再次执行person的setter方法的,例如上述未被初始化的address属性也可以在配置bean的时候进行初始化。

  1. <bean id="person" class="MyPackage.BeanStaticClass" factory-method="getPerson">
  2. <constructor-arg value="single"/>
  3. <constructor-arg value="22"/>
  4. <property name="address" value="nanjing"/>
  5. </bean>

实例工厂配置bean实例

实例工厂生成bean实例的配置其实和静态工厂是类似的,只不过一个调用的是静态方法,一个调用的是实例方法而已。使用实例工厂创建bean需要配置以下属性:

  • factory-bean:指定工厂的实例
  • factory-method:指定工厂方法

这种方式和静态工厂方法创建bean的方式及其类似,此处不再赘述。

显然,后两者于前者对于配置bean实例来说是两种截然不同的方式,一种是声明式配置,由Spring替我们生成bean实例,另一种则是我们程序员手动的去返回bean实例,各有各的优缺点,适情况选择。

二、深入理解容器中的bean

     首先我们看一段配置bean的代码片段,

  1. <bean id="person" class="MyPackage.Person">
  2. <property name="name" value="single"/>
  3. <property name="age" value="22"/>
  4. <property name="address" value="nanjing"/>
  5. </bean>
  6. <bean id="student" class="MyPackage.Student">
  7. <property name="name" value="single"/>
  8. <property name="age" value="22"/>
  9. <property name="grade" value="100"/>
  10. </bean>

我们配置两个bean实例,但是发现这两个bean中存在大量相同的信息。如果容器中的bean越来越多,那么这样大范围的重复代码必然导致整个配置文件臃肿,烦杂。

Spring中为我们提供一种机制,让bean于bean之间可以继承。例如上述代码等同于以下代码:

  1. <bean id="info" abstract="true">
  2. <property name="name" value="single"/>
  3. <property name="age" value="22"/>
  4. </bean>
  5. <bean id="person" class="MyPackage.Person" parent="info">
  6. <property name="address" value="nanjing"/>
  7. </bean>
  8. <bean id="student" class="MyPackage.Student" parent="info">
  9. <property name="grade" value="100"/>
  10. </bean>

我们抽象出来一个id为info的bean,该bean中初始化属性name和age的值,然后我们的person和age bean通过属性parent继承了info,那么他们的相应属性的值将继承自info。当然,如果父bean和子bean中对同一属性做了初始化,结果会用子bean中的值覆盖父bean中的值注入到具体的bean实例中。

子bean将继承父bean的属性值,但是有些属性是不能被继承的,例如:

  • scope:bean的作用域
  • depends-on:属性依赖
  • autowire:自动装配
  • lazy-init:延迟加载

包括abstract属性也是不能被继承的。这里需要对比于Java中的类继承机制,类的继承关系其实是一种属性字段和方法的继承,而bean的继承主要是属性及其值的继承。一个倾向于结构上的继承关系,一个则倾向于值上的继承关系。

接着我们看如何根据bean的引用获取该bean在容器中的id值,

由于某种需要,有些时候我们需要在握有bean的实例的时候,想要获取该实例在容器中的id。Spring允许我们通过继承一个接口:BeanNameAware,该接口中有一个方法:setBeanName(String name),这个name的值就是该bean在容器中的id。看程序:

  1. /*person类实现了BeanNameAware 接口*/
  2. public class Person implements BeanNameAware {
  3. private String personId;
  4. @Override
  5. public void setBeanName(String s) {
  6. this.personId = s;
  7. }
  8. }

配置文件没有变化,

  1. Person person = (Person) context.getBean("person");
  2. System.out.println(person.getPersonId());

输出结果:

  1. person

当容器创建person实例之后,它扫描该实例是否实现了接口BeanNameAware,如果实现了该接口,那么容器将自动调用该实例中的setBeanName方法,并将当前实例的id作为参数传入,于是我们就可以保存下该实例在容器中的id。

三、Bean的生命周期

     在Spring容器中,只有作用域为singleton的bean才会被容器追踪,而对于作用域为prototype的bean,容器只负责将它实例化出来,并不会追踪它何时被初始化,何时被销毁等。Spring容器提供两个时机供我们追踪Bean的生命周期:

  • 注入依赖结束时
  • Bean实例被销毁时

对于第一种方式,我们只需要在定义bean的时候为其指定 init-method属性的值即可。该属性的值是一个方法的名称,容器会在注入依赖结束的时候自动调用实例中的该方法。例如:

  1. public class Person {
  2. private String name;
  3. private int age;
  4. public void init(){
  5. System.out.println("依赖注入结束。。。");
  6. }
  7. //省略setter方法
  8. }

配置bean:

  1. <bean id="person" class="MyPackage.Person" init-method="init">
  2. <property name="name" value="single"/>
  3. <property name="age" value="22"/>
  4. </bean>

这样,当容器对person完成注入依赖的时候,就会自动调用我们为其指定的init方法。代码比较简单,就不贴出运行结果了。

对于第二个时机,其实也是类似,只需要配置 属性destory-method的值即可在bean被销毁之前调用。此处不再赘述。

四、高级的依赖关系配置

     一直以来,我们对于依赖关系的注入,要么使用常量注入到属性中,要么使用引用注入到容器中。相对而言,这两种方式对属性的注入来说,几乎是把"死值"注入给属性,这样的程序灵活性必然很差,我们平常也很少使用Spring为属性注入固定的常量值。Spring中允许我们把任意方法的返回值、类或对象的属性值以及其他bean的引用注入给我们的属性。

1、获取其他bean的属性值

我们可以通过PropertyPathFactoryBean来获取配置在容器中的其他bean的某个属性的值(也就是调用它的getter方法)。在配置bean的时候必须为其指定如下两个属性值:

  • targetObject:告诉容器需要调用那个bean实例
  • propertyPath:告诉容器需要调用那个属性的getter方法

PropertyPathFactoryBean是Spring内置的一个特殊的bean,它可以获取指定实例的指定getter方法的返回值。对于这个返回值,Spring将其封装在PropertyPathFactoryBean类型的bean实例中,我们可以选择直接将该bean用于赋值,或者将其定义成新的bean保存在容器中。例如:

  1. public class Person {
  2. private String name;
  3. private int age;
  4. //省略setter方法
  5. }
  6. public class Student{
  7. private String name;
  8. private int age;
  9. //省略setter方法
  10. }

下面我们给出配置bean的代码段,对于person中age我们调用Student实例中getage作为注入值。

  1. <bean id="student" class="MyPackage.Student">
  2. <property name="name" value="single"/>
  3. <property name="age" value="22"/>
  4. </bean>
  5. <bean id="person" class="MyPackage.Person">
  6. <property name="name" value="cyy"/>
  7. <property name="age">
  8. <bean id="student.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
  9. </property>
  10. </bean>

在为person的age属性注入值的时候,我们通过另一个bean的值为其注入,这个bean就是PropertyPathFactoryBean,其中我们通过它的id属性指定需要调用Student对象的getAge方法作为返回值。

当然,我们也可以将PropertyPathFactoryBean返回的值定义成新的bean并指定它id属性,保存在容器中。例如:

  1. <bean id="student" class="MyPackage.Student">
  2. <property name="name" value="single"/>
  3. <property name="age" value="22"/>
  4. </bean>
  5. <bean id="stuAge" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
  6. <property name="targetBeanName" value="student"/>
  7. <property name="propertyPath" value="age"/>
  8. </bean>
  9. <bean id="person" class="MyPackage.Person">
  10. <property name="name" value="cyy"/>
  11. <property name="age" ref="stuAge"/>
  12. </bean>

stuAge中,我们通过注入PropertyPathFactoryBean的targetBeanName属性值,告诉它目标对象在容器中的id,通过注入propertyPath属性值,告诉它目标对象的具体getter方法的名称。这样,PropertyPathFactoryBean就可以调用具体的getter方法,将返回值注入到一个新bean中,此bean的id也已经被指定。于是我们在后续的bean配置中就可以直接使用该bean所包含的值了。

2、获取静态字段值

对于提供了getter方法的属性,我们可以使用上述方法通过getter方法获取到该属性的值。对于并为提供getter方法的属性值,我们也可以直接获取,但前提是该属性访问权限足够(private肯定是不能够获取得到的)。本小节学习的是获取静态的字段,对于非静态字段,Spring也提供了方法获取,但是一般的程序对于非静态字段都会使用private修饰,提供良好的封装性,因此我们也不能获取得到,所以对于非静态字段的获取意义不大。

和前面一样,想要获取一个静态字段的值需要以下两个步骤:

  • 指定具体类名
  • 指定具体字段名

对于静态字段的获取,我们使用Spring中的 FiledRetrievingFactoryBean。和上述情况类似,可以直接赋值注入,也可以重新定义成bean保存在容器中。例如:

  1. <bean id="person" class="MyPackage.Person">
  2. <property name="name" value="cyy"/>
  3. <property name="age">
  4. <bean id="MyPackage.BeanStaticClass.age" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
  5. </property>
  6. </bean>

其中,MyPackage.BeanStaticClass是一个类,其中有一个age的静态字段,在这之前我们已经为该静态字段赋值了,此处我们依然使用和PropertyPathFactoryBean类似的用法。它的第二种用法如下:

  1. <bean id="staticAge" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
  2. <property name="targetClass" value="MyPackage.BeanStaticClass"/>
  3. <property name="targetField" value="age"/>
  4. </bean>
  5. <bean id="person" class="MyPackage.Person">
  6. <property name="name" value="cyy"/>
  7. <property name="age" ref="staticAge"/>
  8. </bean>

用法类似,此处不再赘述。

3、获取任意方法的返回值

根据方法的类型不同,我们大致可以分为以下两个类别:

  • 静态方法的调用
  • 实例方法的调用

不同类型的方法调用需要指定的参数类型也是不尽相同的。

对于静态方法:

  • 指定调用类的名称
  • 指定调用类的方法名称
  • 指定需要传入的参数

对于实例方法:

  • 指定调用实例的名称
  • 指定调用实例中的方法名称
  • 指定需要传入的参数

例如:

  1. <bean id="person" class="MyPackage.Person">
  2. <property name="name" value="cyy"/>
  3. <property name="age">
  4. <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  5. <property name="targetClass" value="MyPackage.BeanStaticClass"/>
  6. <property name="targetMethod" value="getMyAge"/>
  7. </bean>
  8. </property>
  9. </bean>

两个属性值,一个指定了目标类的名称,一个指定了目标方法的名称,如果需要传入参数,可以使用Arguments属性通过list传入参数数组。实例方法的调用类似,此处不再赘述了。

至此,我们对于Spring中bean的配置做了进一步的理解,限于篇幅,有关XML Schema和SpEL部分内容留待下篇。

Spring框架学习之高级依赖关系配置(一)的更多相关文章

  1. Spring框架学习之高级依赖关系配置(二)

    紧接着上篇内容,本篇文章将主要介绍XML Schema的简化配置和使用SpEL表达式语言来优化我们的配置文件. 一.基于XML Schema的简化配置方式 从Spring2.0以来,Spring支持使 ...

  2. spring-第九篇之高级依赖关系配置

    1.关于配置文件一些使用 组件与组件之间的耦合,采用依赖注入管理:基本类型的成员变量值,应该直接在代码中设置. 2.获取其他bean的属性值 PorpertyPathFactoryBean用来获取目标 ...

  3. Spring框架学习笔记(3)——配置bean

    1.属性注入 (1)根据setter方法属性注入,这里使用的是property标签.需要bean属性提供对应的setter方法,比如笔记(1)里的 HelloWorld使用的就是这种方法. <! ...

  4. Spring框架学习笔记(4)——配置bean more

    1.配置List属性 <!-- 配置List属性 --> <bean id="person4" class="com.broadtext.beans.c ...

  5. Spring框架学习之注解配置与AOP思想

         上篇我们介绍了Spring中有关高级依赖关系配置的内容,也可以调用任意方法的返回值作为属性注入的值,它解决了Spring配置文件的动态性不足的缺点.而本篇,我们将介绍Spring的又一大核心 ...

  6. Spring框架学习一

    Spring框架学习,转自http://blog.csdn.net/lishuangzhe7047/article/details/20740209 Spring框架学习(一) 1.什么是Spring ...

  7. Spring框架学习1

    AnonymouL 兴之所至,心之所安;尽其在我,顺其自然 新随笔 管理   Spring框架学习(一)   阅读目录 一. spring概述 核心容器: Spring 上下文: Spring AOP ...

  8. Spring框架学习之IOC(一)

    Spring框架学习之IOC(一) 先前粗浅地学过Spring框架,但当时忙于考试及后期实习未将其记录,于是趁着最近还有几天的空闲时间,将其稍微整理一下,以备后期查看. Spring相关知识 spri ...

  9. Spring框架学习笔记(5)——Spring Boot创建与使用

    Spring Boot可以更为方便地搭建一个Web系统,之后服务器上部署也较为方便 创建Spring boot项目 1. 使用IDEA创建项目 2. 修改groupid和artifact 3. 一路n ...

随机推荐

  1. java乱码问题处理

    java乱码问题处理 java乱码出现的问题有很多,这里主要解释tomcat,jsp,html,http(get,post请求乱码处理).常见的问题可能是tomcat,http请求乱码问题,对于jsp ...

  2. 逆向实用干货分享,Hook技术第一讲,之Hook Windows API

    逆向实用干货分享,Hook技术第一讲,之Hook Windows API 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) ...

  3. Hive 存储类型 StoreType

    file_format: : SEQUENCEFILE | TEXTFILE -- (Default, depending on hive.default.fileformat configurati ...

  4. hdu 4090--GemAnd Prince(搜索)

    题目链接 Problem Description Nowadays princess Claire wants one more guard and posts the ads throughout ...

  5. Musical Theme poj1743(后缀数组)

    Musical Theme Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 16757   Accepted: 5739 De ...

  6. Longest Uncommon Subsequence I

    Given a group of two strings, you need to find the longest uncommon subsequence of this group of two ...

  7. C++PrimerPlus第6版 第四章——复合类型

    1,复合类型主要包含:数组.结构.联合.枚举.类.指针.引用等. 2,数组.长度必须确定.即编译阶段,数组的长度就得确定好.所以只能使用常量(#define.const)声明数组长度.如果使用变量声明 ...

  8. clone github报Permission denied (publickey) 解决方案

    问题描述 问题产生的原因,不是很清楚,就不管了.在执行git clone git@github.com:****.git 的时候报了Permission denied (publickey). War ...

  9. 在项目中创建单元测试时junit的配置和使用

    首先配置项目中AndroidMainfest.xml文件,加入 <instrumentation android:name="android.test.InstrumentationT ...

  10. btsync 分享资源

    Btsync是一款跨平台软件,可以在不同的设备之间共享文件. Btsync类似于BT下载,用户对用户(多用户)之间的传送. 文档的分享者可以将资源放到文件夹下,生成共享Key,分享给接受者,接受者只需 ...