澳门新浦京8455com七、高级依赖关系配置

澳门新浦京8455com 1

前言

在Spring配置文件中使用XML文件进行配置,实际上是让Spring执行了相应的代码,例如:

  • 使用<bean>元素,实际上是让Spring执行无参或有参构造器
  • 使用<property>元素,实际上是让Spring执行一次setter方法

但Java程序还可能有其他类型的语句:调用getter方法、调用普通方法、访问类或对象的Field等,而Spring也为这种语句提供了对应的配置语法:

  • 调用getter方法:使用PropertyPathFactoryBean
  • 调用类或对象的Filed值:使用FiledRetrievingFactoryBean
  • 调用普通方法:使用MethodInvokingFactoryBean

获取其他Bean的属性值:

PropertyPathFactoryBean用来获取目标Bean的属相值(实际上就是它的getter方法的返回值),获得的值可以注入给其他Bean,也可以直接定义成新的Bean。
使用PropertyPathFactoryBean来调用其他Bean的getter方法需要指定如下信息:

  • 调用哪个对象。有PropertyPathFactoryBean的setTargetPbject(Object
    targetObject)方法指定。
  • 调用哪个getter方法。由PropertyPathFactoryBean的setPropertyPath(String
    propertyPath)方法指定。

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd" >

      <!-- 定义一个将要被应用的目标Bean -->
      <bean id="persion" class="entity.Persion">
         <property name="age" value="30"/>
         <property name="son">
            <!-- 使用嵌套Bean定义setSon()方法的参数值 -->
            <bean class="entity.Son">
               <property name="age" value="11"/>
            </bean>
         </property>
      </bean>
      <!-- 将指定Bean实例的getter方法返回值定义成son1 Bean -->
      <bean id="son1" class="entity.Son1">
         <!--确定目标Bean,指定son1 Bean来自哪个Bean的getter方法 -->
         <property name="targetBeanName" value="persion"/>
         <!-- 指定son1 Bean来自目标Bean的那个getter方法,son代表getSon() -->
         <property name="propettyPath" value="son"/>
      </bean>
</beans>

SpringTest.java

public class SpringTest
{
    public static void main(String[] args)
    { 
         ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
         System.out.println("系统获取son1:"ctx.getBean("son1"));
    }
}

输出

系统获取son1: Son[age=11]

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

注入其他Bean的属性值

PropertyPathFactoryBean用来获得目标Bean的属性值(实际上就是调用getter方法返回的值),获得的值可以注入给其他的Bean,也可以直接定义新的Bean。看如下的配置文件:

<bean id="person" class="com.abc.Person">
    <property name="age" value="30" />
    <property name="son">
        <!-- 使用嵌套Bean定义属性值 -->
        <bean class="com.abc.service.Son">
            <property name="age" value="11" />
        </bean>
    </property>
</bean>

<bean id="son2" class="com.abc.service.Son">
    <!-- age属性不是直接注入,而是将person中的son的age属性赋值给son2的age属性 -->
    <property name="age">
        <!-- 注意这里使用的是PropertyPathFactoryBean -->
        <bean id="person.son.age" 
            class="org.springframework.beans.factory.config.PropertyPathFactoryBean" />
    </property>
</bean>

其中Person类和Son类的属性可以从配置文件中看出,这不再给出。主程序如下:

public class Test {
    public static void main(String args[]) {
        ApplicationContext ac = 
            new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println("age=" + ac.getBean("son2", Son.class).getAge());
    }
}

输出结果:

age=11

Bean实例的属性值,不仅可以注入另一个Bean,还可将Bean实例的属性值直接定义成Bean实例,这也是通过PropertyPathFactoryBean完成的。对上面的配置文件增加这样一段:

<bean id="son1" 
    class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
    <!-- 确定目标Bean,表明son1来自哪个Bean的组件 -->
    <property name="targetBeanName" value="person" />
    <!-- 确定属性,表明son1来自目标Bean的哪个属性 -->
    <property name="propertyPath" value="son" />
</bean>

执行上面的Test类,把son2换成son1,结果一样。

获取Field值:

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

注入其他Bean的Field值

通过FieldRetrievingFactoryBean类,可以将其他Bean的Field值注入给其他Bean,或者直接定义新的Bean。下面是配置片段:

<bean id="son" class="com.abc.service.Son">
    <property name="age">
        <bean id="java.sql.connection.TRANSACTION_SERIALIZABLE"
            class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
    </property>
</bean>

测试主程序与上文定义的类似,这里不再提供,执行结果如下:

age=8

在这个配置中,son对象的age的值,等于java.sql.Connection.TRANSACTION_SERIALIZABLE的值。在上面的定义中,定义FieldRetrievingFactoryBean工厂Bean时,指定的id并不是该Bean实例的唯一标识,而是指定Field的表达式(即将要被取出来的值)。

注意:Field既可以是静态的,也可以是非晶态的。上面的配置片段指定的Field表达式是静态Field值,因此可以通过类名直接访问。如果Field值是非静态的,则应该通过容器中已经存在的Bean来访问——即Field表达式的第一个短语应该是容器中已经存在的Bean。

Field值也可以定义成Bean实例,例如,在配置文件中增加下面一段:

<bean id="age" 
    class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
    <!-- targetClass指定Field所在的目标类 -->
    <property name="targetClass" value="java.sql.Connection" />
    <!-- targetField指定Field名 -->
    <property name="targetField" value="TRANSACTION_SERIALIZABLE" />
</bean>

在主程序中增加如下输出:

System.out.println("age=" + ac.getBean("age"));

执行结果和上文一样。

使用FieldRetrievingFactoryBean获取Field值时,必须指定如下两个属性:

  • targetClass或targetObject:分别用于指定Field值所在的目标累或目标对象。如果需要获得的Field是静态的,则使用targetClass指定目标累;如果Field是非静态的,则使用targetObject指定目标对象
  • targetField:指定目标类或目标对象的Field名

如果Field是个静态Field,则有一种更加简洁的写法:

<bean id="age" 
    class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
    <!-- value指定哪个类的哪个静态域值 -->
    <property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE" />
</bean>

获取方法返回值:

Spring框架的本质就是通过XML配置来执行java代码,因此几乎可以把所有的Java代码放到Spring配置文件中管理:

  • 调用构造器创建对象(包括使用工厂方法创建对象),用<bean…/>元素。
  • 调用setter方法,用<property…/>元素。
  • 调用getter方法,PropertyPathFactoryBean或<util:property-path…/>元素。
  • 调用普通方法,用MethodInvokingFactoryBean工厂Bean。
  • 获取Field的值,用FieldRetrievingFactoryBean或<util:constant…/>元素。

一般来说,应该讲如下两类信息放到XML配置文件中管理:

  • 项目升级、维护时经常需要改动的信息。
  • 控制项目类各组件耦合关系的代码。

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

注入其他Bean的方法返回值

通过MethodInvokingFactoryBean工厂Bean,可将目标方法的返回值注入为Bean的属性值。这个工厂Bean用来获取指定方法的返回值,该方法既可以是静态方法,也可以是实例方法;这个值既可以被注入到指定Bean实例的指定属性,也可以直接定义成Bean实例。看例子:

<bean id="valueGenerator" class="com.abc.util.ValueGenerator" />
<bean id="son1" class="com.abc.service.Son">
    <property name="age">
        <!-- 获取方法返回值:调用valueGenerator的getValue方法 -->
        <bean 
            class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="targetObject" ref="valueGenerator" />
            <property name="targetMethod" value="getValue" />
        </bean>
    </property>
</bean>

下面是ValueGenerator:

public class ValueGenerator {
    public int getValue() { return 2; }
    public static int getStaticValue () { return 3;}
}

测试程序依旧打印son1中age的值,代码略,结果如下:

age=2

如果要调用静态方法,则把配置修改为:

<bean id="son1" class="com.abc.service.Son">
    <property name="age">
        <!-- 获取方法返回值:调用valueGenerator的getStaticValue方法 -->
        <bean 
            class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="targetClass" value="com.abc.util.ValueGenerator" />
            <property name="targetMethod" value="getStaticValue" />
        </bean>
    </property>
</bean>

测试结果为:

age=3

由于Java是支持重载的,只给定方法名,还不足以能够确定调用哪个方法,通过上面的配置能调用成功是因为ValueGenerator中的两个方法都没有参数。如果方法中有参数,该如何配置呢?在配置文件中加入以下内容:

<bean id="sysProps" 
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetClass" value="java.lang.System" />
    <property name="targetMethod" value="getProperties" />
<bean>
<bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <!-- 指向上面的sysProps Bean -->
    <property name="targetObject" value="sysProps" />
    <property name="targetMethod" value="getProperty" />
    <!-- 这里配置参数 -->
    <property name="arguments">
        <!-- 使用list元素列出调用方法的多个参数 -->
        <list>
            <value>java.version</value>
        </list>
    </property>
<bean>

上例中相当于用”java.version”作为参数调用了java.lang.System的getProperty方法,返回值将创建一个名为javaVersion的Bean。即相当于:

javaVersion = java.lang.System.getProperty("java.version");

和前文中的Field一样,如果要调用的方法为静态方法,也有一种更加简洁的方法:

<bean id="myBean"
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <!-- 使用staticMethod属性,直接指定目标类的目标方法 -->
    <property name="staticMethod" value="com.abc.util.ValueGenerator.getStaticValue" />
</bean>
<bean id="" class=""></bean>

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

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

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

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

/*person bean*/
public class Person {
    private String name;
    private int age;
    private String address;
    //省略setter方法
}

/*静态工厂类及静态工厂方法*/
public class BeanStaticClass {

    public static Person getPerson(String name,int age){
        Person person = new Person();
        person.setName(name);
        person.setAge(age);
        return person;
    }
}

配置bean:

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

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

澳门新浦京8455com 1

这里写图片描述

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

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

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

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

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

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

二、深入理解容器中的bean
     首先我们看一段配置bean的代码片段,

<bean id="person" class="MyPackage.Person">
    <property name="name" value="single"/>
    <property name="age" value="22"/>
    <property name="address" value="nanjing"/>
</bean>

<bean id="student" class="MyPackage.Student">
    <property name="name" value="single"/>
    <property name="age" value="22"/>
    <property name="grade" value="100"/>
</bean>

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

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

<bean id="info" abstract="true">
    <property name="name" value="single"/>
    <property name="age" value="22"/>
</bean>

<bean id="person" class="MyPackage.Person" parent="info">
    <property name="address" value="nanjing"/>
</bean>

<bean id="student" class="MyPackage.Student" parent="info">
    <property name="grade" value="100"/>
</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。看程序:

/*person类实现了BeanNameAware 接口*/
public class Person implements BeanNameAware {
    private String personId;

    @Override
    public void setBeanName(String s) {
        this.personId = s;

    }
}

配置文件没有变化,

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

输出结果:

person

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

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

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

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

public class Person {
    private String name;
    private int age;

    public void init(){
        System.out.println("依赖注入结束。。。");
    }
    //省略setter方法
}

配置bean:

<bean id="person" class="MyPackage.Person" init-method="init">
    <property name="name" value="single"/>
    <property name="age" value="22"/>
</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保存在容器中。例如:

public class Person {
    private String name;
    private int age;
    //省略setter方法
}
public class Student{
    private String name;
    private int age;
    //省略setter方法
}

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

<bean id="student" class="MyPackage.Student">
    <property name="name" value="single"/>
    <property name="age" value="22"/>
</bean>

<bean id="person" class="MyPackage.Person">
    <property name="name" value="cyy"/>
    <property name="age">
        <bean id="student.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
    </property>
</bean>

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

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

<bean id="student" class="MyPackage.Student">
    <property name="name" value="single"/>
    <property name="age" value="22"/>
</bean>

<bean id="stuAge" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
    <property name="targetBeanName" value="student"/>
    <property name="propertyPath" value="age"/>
</bean>

<bean id="person" class="MyPackage.Person">
    <property name="name" value="cyy"/>
    <property name="age" ref="stuAge"/>
</bean>

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

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

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

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

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

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

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

<bean id="staticAge" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
    <property name="targetClass" value="MyPackage.BeanStaticClass"/>
    <property name="targetField" value="age"/>
</bean>

<bean id="person" class="MyPackage.Person">
    <property name="name" value="cyy"/>
    <property name="age" ref="staticAge"/>
</bean>

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

3、获取任意方法的返回值
根据方法的类型不同,我们大致可以分为以下两个类别:

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

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

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

对于实例方法:

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

例如:

<bean id="person" class="MyPackage.Person">
    <property name="name" value="cyy"/>
    <property name="age">
        <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="targetClass" value="MyPackage.BeanStaticClass"/>
            <property name="targetMethod" value="getMyAge"/>
        </bean>
    </property>
</bean>

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

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

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图