Spring框架学习三:基于XML文件的方式对bean进行配置

 

IoC配置bean的方式:
1. 基于XML文件的方式
2. 基于注解的方式

 

  我们先来看基于XML文件对bean进行配置:

  在XML文件声明 Bean 时,Spring 配置文件的根元素来源于 Spring beans 命名空间所定义的 <beans> 元素。在 <beans> 元素内,你可以放置所有的 Spring 配置信息,包括 <bean> 元素的声明。但是 beans 命名空间并不是你遇到的唯一的 Spring 命名空间。Spring 核心框架自带了10个命名空间配置,如下表所示:

 

命名空间 用途
aop    
为声明切面以及将被 @AspectJ 注解的类代理为 Spring 切面提供了配置元素
beans 支持声明 bean 和装载 bean,是 Spring 最核心也是最原始的命名空间
context 为配置 Spring 上下文提供了配置元素,包括自动检测和自动装配 bean、注入非 Spring 直接管理的对象
jee 提供了与 Java EE API 的集成,例如 JNDI 和 EJB
jms 为声明消息驱动的 POJO 提供了配置元素
lang 支持配置由 Groovy、JRuby 或 BeanShell 等脚本实现的 bean
mvc 启用Spring MVC的能力,例如面向注解的控制器、视图控制器和拦截器
oxm 支持 Spring 的对象到 XML 映射的配置
tx 提供声明式事务配置
util 提供各种工具类元素

 

Spring 支持3种依赖注入的方式

- 属性注入

- 构造器注入

- 工厂方式注入(不推荐使用)

 

以例子来说明

 1 //创建测试类
 2 public class Person
 3 {
 4     private String name;
 5     private int age;
 6     private double height;
 7     private Address address;
 8   
 9    //生成 getter、setter方法,生成带参和不带参的构造器,重写 toString
10 }
//创建测试类
public class Address
{
  private String city;
  public String getCity()
    {
        return city;
    }
    public void setCity(String city)
    {
        this.city = city;
    }
    @Override
    public String toString()
    {
        return "Address [city=" + city  + "]";
    }
}
//创建执行类
public
class PersonTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml"); Person p = (Person) ctx.getBean("p"); System.out.println(p); } }

 

属性注入

属性注入 即通过 setXxx 方法注入bean的属性值或依赖的对象,属性注入使用 <property> 元素,使用 name 属性指定Bean的属性名称,value 属性或 <value> 子节点指定属性值,属性注入是实际应用中最常用的注入方式。

<!-- 配置一个bean -->
<!-- id也可以换成name,使用name时可以使用特殊字符来为name赋值,普遍使用id -->
<bean id="p" class="com.bupt.springtest.beanXml.Person">
  <!-- 为属性name赋值为tom -->
  <!-- 通过属性注入: 通过 setter 方法注入属性值 -->
  <property name="name" value="tom"/>
</bean>
<!-- 打印得到的对象结果为:Person [name=tom, age=0, height=0.0, address=null] -->

 

构造方法注入

通过构造方法注入Bean的属性值或依赖的对象,它保证了Bean实例在实例化后就可以使用

构造器注入在 <constructor-arg> 元素里声明属性,<constructor-arg> 中没有 name 属性

通过构造器注入时,类中必须要有与参数个数和类型对应的构造方法

<!-- 通过构造器注入属性值 -->
    <bean id="p" class="com.bupt.springtest.beanXml.Person">
    <!-- 在 Bean 中必须有对应的构造器,即必须在Person类中含有只有一个参数name的构造器 -->
        <constructor-arg value="tom"/>    
    </bean>

<!-- 打印得到的对象结果为:Person [name=tom, age=0, height=0.0, address=null] -->
<!-- 若一个 bean 有多个构造器, 初始化的参数个数或者顺序都不同时,可以根据 index 或 type 进行更加精确的定位 -->
    <bean id="p" class="com.bupt.springtest.beanXml.Person">
        <constructor-arg value="23" index="1"/>
        <constructor-arg value="tom" index="0"/>
        <constructor-arg value="178.2" type="double"/>
    </bean> 
    <!-- 打印结果为:Person [name=tom, age=23, height=178.2, address=null] -->
    <!-- 若value中包含有特殊字符可以使用<![CDATA[]]>把字面值包裹起来 -->
    <bean id="p" class="com.bupt.springtest.beanXml.Person">
        <constructor-arg>
            <value><![CDATA[<tom>]]></value>
        </constructor-arg>
    </bean>
    <!-- 打印对象结果为:Person [name=<tom>, age=0, height=0.0, address=null] -->

   <!-- 可以使用专用的 <null/> 元素标签为Bean的字符串或其他对象类型的属性注入null值 -->
   <bean id="address" class="com.bupt.springtest.beanXml.Address">
     <!-- 为 Address 的 city 属性赋值为 null, 若某一个 bean 的属性值不是 null, 使用时需要为其设置为 null -->
     <property name="city"><null/></property>
   </bean>

 

引用其它Bean

在Bean的配置文件中,可以通过 <ref> 元素或 ref 属性为Bean的属性或构造器参数指定对其他Bean的引用

    <bean id="address" class="com.bupt.springtest.beanXml.Address">
        <property name="city" value="Beijing"/>
    </bean>

    <bean id="p" class="com.bupt.springtest.beanXml.Person">
        <property name="name" value="tom"/>
        <property name="age" value="20"/>
        <property name="height" value="178.3"/>
        <!-- 通过 ref 属性值指定当前属性指向哪一个 bean -->
        <property name="address" ref="address"/>
    </bean>
    <!-- 打印结果为:Person [name=tom, age=20, height=178.3, address=Address [city=Beijing]] -->

 

内部Bean

也可以在属性或构造器里包含Bean的声明,这种Bean称为内部Bean。内部Bean不能使用在任何其他地方,它定义时仅仅给一个特定的属性使用

内部Bean声明直接包含在 <property><constructor-arg> 元素里,不需要设置任何 id 或 name 属性

    <bean id="p" class="com.bupt.springtest.beanXml.Person">
        <property name="name" value="tom"/>
        <property name="age" value="20"/>
        <property name="height" value="178.3"/>
        <!-- 内部 bean, 类似于匿名内部类对象.不能被外部的 bean 来引用, 也没有必要设置 id 属性 -->
        <property name="address">
            <bean class="com.bupt.springtest.beanXml.Address">
                <property name="city" value="shanghai"/>
            </bean>
        </property>
    </bean>
    <!-- 打印结果为:Person [name=tom, age=20, height=178.3, address=Address [city=shanghai]] -->

 

集合属性

在Spring中可以通过一组内置的xml标签(如:<list><set><map>)来配置集合属性

配置 java.util.List 类型的属性,需要指定 <list> 标签,在标签包里,包含一些元素。这些标签可以通过 <value> 指定简单的常量值,通过 <ref> 指定对其他Bean的引用,通过 <bean> 指定内置Bean定义,通过 <null/> 指定空元素,甚至可以内嵌其他集合。

数组的定义和List一样,都使用 <list>

配置 java.util.Set 需要使用 <set> 标签,定义元素的方法与List一样

修改Person类中定义的 address 属性,将其类型由 Address 改为 List<Address>

    <bean id="address1" class="com.bupt.springtest.beanXml.Address">
        <property name="city" value="beijing"/>
    </bean>
    
    <bean id="p" class="com.bupt.springtest.beanXml.Person">
        <property name="name" value="tom"/>
        <property name="address">
            <!-- 使用list节点为List类型的属性赋值 -->
            <list>
                <!-- 通过 <ref> 指定对其他Bean的引用 -->
                <ref bean="address1"/>
                <!-- 通过 <bean> 指定内置Bean定义 -->
                <bean class="com.bupt.springtest.beanXml.Address">
                    <property name="city" value="shanghai"/>
                </bean>
            </list>            
        </property>
    </bean>
    <!-- 打印结果为:Person [name=tom, age=23, height=178.2, address=[Address [city=beijing], Address [city=shanghai]]] -->

 

java.util.Map通过 <map> 标签定义,<map> 标签里可以使用多个 <entry> 作为子标签,每个条目包含一个键—值对。

必须在 <key> 标签里定义键

因为键和值的类型没有限制,所以可以自由地为他们指定 <value>、<ref>、<bean>、<null/>元素

可以将Map的键和值作为 <entry> 的属性定义:简单常量使用 key 和 value 来定义,Bean 引用通过 key-ref 和  value-ref 属性定义

修改Person类中定义的address属性,将其类型由 Address 改为 Map<String, Address>

    <bean id="address1" class="com.bupt.springtest.beanXml.Address">
        <property name="city" value="beijing"/>
    </bean>
    <bean id="address2" class="com.bupt.springtest.beanXml.Address">
        <property name="city" value="shanghai"/>
    </bean>
    
    <!-- 配置 Map 属性值 -->
    <bean id="p" class="com.bupt.springtest.beanXml.Person">
        <property name="name" value="tom"/>
        <property name="address">
            <!-- 使用 map 节点及 map 的 entry 子节点配置 Map 类型的成员变量 -->
            <map>
                <entry key="A" value-ref="address1"/>
                <entry key="B" value-ref="address2"/>
            </map>            
        </property>
    </bean>
    <!-- 打印结果为:Person [name=tom, age=0, height=0.0, address={A=Address [city=beijing], B=Address [city=shanghai]}]] -->

 

使用 <props> 定义 java.util.Properties,该标签使用多个 <prop> 作为子标签,每个 <prop> 标签必须定义 key 属性

//定义一个测试类
public
class DataSource { private Properties properties; public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } @Override public String toString() { return "DataSource [properties=" + properties + "]"; } }
public class DataSourceTest
{
    public static void main(String[] args)
    {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        
        DataSource dataSource = ctx.getBean(DataSource.class);
        
        System.out.println(dataSource);
    }
}
    <!-- 配置 Properties 属性值 -->
    <bean id="dataSource" class="com.bupt.springtest.beanXml.DataSource">
        <property name="properties">
            <!-- 使用 props 和 prop 子节点来为 Properties 属性赋值 -->
            <props>
                <prop key="user">root</prop>
                <prop key="password">123</prop>
                <prop key="jdbcUrl">jdbc:mysql:///test</prop>
                <prop key="driverClass">com.mysql.jdbc.Driver</prop>
            </props>
        </property>
    </bean>
    <!-- 打印结果为:DataSource [properties={driverClass=com.mysql.jdbc.Driver, user=root, password=123, jdbcUrl=jdbc:mysql:///test}] -->

 

使用 utility schema 定义集合

  使用基本的集合标签定义集合时,不能将集合作为独立的组件定义,导致其他 Bean 无法引用该集合,所以无法在不同 Bean 之间共享集合

可以使用 util schema 里面的集合标签定义独立的集合Bean,需要注意的是,必须在 <beans> 根元素里添加 util schema 定义

编写xml配置之前我们需要导入 util 的命名空间

在配置文件页面点击下方的NameSpaces,勾选添加 util 命名空间

 

修改Person类中定义的address属性,将其类型由 Address 改为 List<Address>

    <bean id="address1" class="com.bupt.springtest.beanXml.Address">
        <property name="city" value="beijing"/>
    </bean>
    <bean id="address2" class="com.bupt.springtest.beanXml.Address">
        <property name="city" value="shanghai"/>
    </bean>
        
    <!-- 配置单独的集合bean,以供多个bean进行引用,需导入util命名空间 -->
    <util:list id="address">
        <ref bean="address1"/>
        <ref bean="address2"/>
    </util:list>

    <bean id="p" class="com.bupt.springtest.beanXml.Person">
        <property name="name" value="jack"/>
        <property name="address" ref="address"/>
    </bean>    
    <!-- 打印结果为:Person [name=jack, age=0, height=0.0, address=[Address [city=beijing], Address [city=shanghai]]] -->

 

使用P命名空间

  为了简化 XML 文件的配置,越来越多的 XML 文件采用属性而非子元素配置信息,引入p命名空间可以通过 <bean> 元素属性的方式配置 Bean 的属性,使用p命名空间后,基于XML的配置方式将进一步简化。

同样,需要将p命名空间添加到配置文件中,这时我们就可以这样来编写配置文件了

    <bean id="p" class="com.bupt.springtest.beanXml.Person" p:name="jason" p:age="30" 
    p:height="180.2" p:address-ref="address"/>
    <!-- 打印结果为:Person [name=jason, age=30, height=180.2, address=[Address [city=beijing], Address [city=shanghai]]] -->

 

 

 


自动装配XML配置里的Bean

Spring IOC 容器可以自动装配 Bean,需要做的仅仅是在 <bean> 的 autowire 属性里指定自动装配的模式,装配模式有三种:

byType(根据类型自动装配): 若 IOC 容器中有多个与目标 Bean 类型一致的 Bean. 在这种情况下,Spring 将无法判定哪个 Bean 最合适该属性,所以不能执行自动装配

byName(根据名称自动装配): 必须将目标 Bean 的名称和属性名设置的完全相同

constructor(通过构造器自动装配): 当 Bean 中存在多个构造器时,此种自动装配方式将会很复杂,不推荐使用

 

将Person类中address属性类型改回Address类型

    <bean id="address" class="com.bupt.springtest.beanXml.Address">
        <property name="city" value="beijing"/>
    </bean>
    <!-- 可以使用 autowire 属性指定自动装配的方式,byName 根据 bean 的名字和当前 bean 的 setter 风格的属性名进行自动装配 -->
    <bean id="p" class="com.bupt.springtest.beanXml.Person"
            p:name="jerry" autowire="byName"/>
    <!-- 打印结果为: Person [name=jerry, age=0, height=0.0, address=Address [city=beijing]]-->
    <!-- 若想使用byName自动装配,被装配的bean中定义的 id 值需与装配的bean中setXxx中的Xxx风格一致,如本例中id值必须为address,若为其他值,则不能装配 -->
    <bean id="address1" class="com.bupt.springtest.beanXml.Address">
        <property name="city" value="beijing"/>
    </bean>
    <!-- 若 id="address1" 则打印结果为: Person [name=jerry, age=0, height=0.0, address=null]-->
    
    <!-- 此时,我们可以选择byType来装配 -->
    <bean id="address1" class="com.bupt.springtest.beanXml.Address">
        <property name="city" value="beijing"/>
    </bean>
    <bean id="p" class="com.bupt.springtest.beanXml.Person"
            p:name="kim" autowire="byType"/>
    <!-- 得到的打印结果同样显示装配成功,打印结果为: Person [name=kim, age=0, height=0.0, address=Address [city=beijing]]-->
    <!-- 若IoC容器中有一个以上的类型匹配的 bean,则会抛异常 -->        
    <bean id="address2" class="com.bupt.springtest.beanXml.Address">
        <property name="city" value="shanghai"/>
    </bean>    
    <!-- 添加此bean后,如果使用 byType 的形式来装配则会出现,打印结果异常 -->

 自动装配的缺点:

1. 在Bean配置文件里设置 autowire 属性进行自动装配将会装配Bean的所有属性。然而,若只希望装配个别属性时,autowire 属性就不够灵活了。

2. autowire 属性要么根据类型自动装配,要么根据名称自动装配,不能二者兼用。

3. 实际项目中很少使用自动装配,因为清晰的配置文档阅读性更好。

 

 


 

Bean之间的关系:继承和依赖

Spring允许继承bean的配置,被继承的bean称为父bean,继承这个父bean的bean叫做子bean,继承包含以下规则:

1. 子bean从父bean中继承配置,包括bean的属性配置

2. 子bean也可以覆盖从父bean继承过来的配置

3. 父bean可以作为配置模板,也可以作为bean的实例。若只想把父bean作为模板可以设置 <bean> 的abstract属性为true,这样Spring将不会实例化这个bean

4. 并不是 <bean> 元素里的所有属性都会被继承,比如:autowire、abstract等

5. 可以忽略(即不写class属性)父 bean 的 calss 属性,此时的父bean不再被实例化,完全就是一个模板 bean,所以abstract必须设置为true

 

下面通过代码来理解

    <bean id="address" class="com.bupt.springtest.beanXml.Address" p:city="beijing"/>
    <bean id="p" class="com.bupt.springtest.beanXml.Person" p:name="A" p:age="26" p:height="172.2" p:address-ref="address"/>
    

  <!-- bean 配置的继承:使用bean的parent属性指定继承哪个bean的配置 --> <bean id="p1" class="com.bupt.springtest.beanXml.Person" p:name="B" parent="p"/>
  <!-- 分别获取父bean和子bean的实例将其打印,结果为: Person [name=A, age=26, height=172.2, address=Address [city=beijing]] Person [name=B, age=26, height=172.2, address=Address [city=beijing]] -->

 

    <bean id="address" class="com.bupt.springtest.beanXml.Address" p:city="beijing"/>
    
  <!-- 抽象bean:bean的 abstract 属性为 true 的 bean, 这样的 bean 不能被 IoC 容器实例化, 只能用来继承配置。 若某个 bean 的 class 属性没有指定, 则该 bean 必须是一个抽象 bean--> <bean id="p" p:name="A" p:age="26" p:height="172.2" p:address-ref="address" abstract="true"/>

  <!-- bean 配置的继承:使用 bean 的 parent 属性指定继承哪个 bean 的配置 --> <bean id="p1" class="com.bupt.springtest.beanXml.Person" p:name="B" parent="p"/>
  <!-- 此时IoC容器只会初始化子bean,打印结果为:Person [name=B, age=26, height=172.2, address=Address [city=beijing]] -->

 

Spring允许用户通过 depends-on 属性设定 bean 前置依赖的 bean,前置依赖的 bean 会在本 bean 实例化之前创建好,如果前置依赖于有多个bean,则可以通过逗号或空格的方式配置bean的名称。

举个实际例子说明 depends-on 属性的作用: DAO bean实例化之前必须要先实例化 Database bean,因为DAO的使用是依赖Database启动的,如果Database不启动,那么DAO即使实例化也是不可用的。在这种情况下,除了在DAO上使用构造函数注入 Database bean 以外,Spring没有任何依赖注入的关系能满足以上条件。但DAO也许根本就不需要Database的实例注入,因为DAO是通过JDBC访问数据库的,它不需要调用Database上的任何属性和方法。在这种情况下,我们就可以使用 depends-on 来定义在DAO被实例化之前去实例化Database。

    <bean id="address" class="com.bupt.springtest.beanXml.Address" p:city="shanghai"/>
    
  <!-- 要求配置Person时,必须有一个关联的Address,换句话说person这个bean依赖于Address这个bean -->
<bean id="p" class="com.bupt.springtest.beanXml.Person" p:name="jack" depends-on="address"/>
  <!-- 打印结果:Person [name=jack, age=0, height=0.0, address=null] -->

 


 

 

Bean的作用域

在Spring中,可以在 <bean>  元素的 scope 属性里设置 bean 的作用域,默认情况下,Spring只为每个在IoC容器里声明的 bean 创建唯一一个实例整个IoC容器范围内都能共享该实例,所有后续的 getBean() 调用和 bean 引用都将返回这个唯一的 bean 实例。该作用域被称为 singleton它是所有 bean 的默认作用域。

除了 singleton 作用域外,还有其他几种作用域,说明如下:

 

类别 说明
singleton     在Spring容器中仅存在一个bean实例,bean以单实例的方式存在
prototype 每次调用 getBean() 都会返回一个新的实例
request 每次HTTP请求都会创建一个新的bean,该作用域仅适用于 WebApplicationContext 环境
session 同一个Http Session共享一个bean,不同的Http Session使用不同的 bean。该作用域仅适用于 WebApplicationContext 环境

 

 

 

 

 

 

 

    <!-- 使用 bean 的 scope 属性来配置 bean 的作用域 
         singleton:默认值,容器初始化时(即new ClassPathXmlApplictionContext()时)创建 bean 的实例,在整个容器的生命周期内只创建这一个bean
         prototype:容器初始化时不创建bean实例,而在每次请求时(即调用getBean()方法时)都创建一个新的bean实例返回
    -->
    <bean id="address" class="com.bupt.springtest.beanXml.Address" p:city="shanghai"/>
    <bean id="p" class="com.bupt.springtest.beanXml.Person" p:name="jack" depends-on="address" scope="prototype"/>

注意:Spring 有关单例的概念仅限于 Spring 上下文范围内。不想真正的单例,在每个类加载器中保证只有一个实例。Spring 的单例 bean 只能保证在每二个应用上下文中只有一个 bean 的实例。没有人可以阻止你使用传统的方法实例化同一个 bean。

 

Spring 中 Bean 的初始化时机

1. 如果你使用 BeanFactory 作为 Spring Bean 的工厂类,则所有的 bean 都是在第一次使用该 bean 的时候初始化。

2. 如果你使用 ApplicationContext 作为 Spring Bean 的工厂类,则又分为如下几种情况:

a. 如果该 bean 的配置为:<bean id = "xxx" class="xxx" scope="singleton" lazy-init="false"/> 时,则 Spring 在容器初始化(容器启动)时就实例化该 bean,并将实例化的 bean 放在一个 map 结构的缓存中,下次使用该 bean 时,直接从缓存中获取。

b. 如果该 bean 的配置为:<bean id = "xxx" class="xxx" scope="singleton" lazy-init="true"/> 时,则 Spring 在第一次使用该 bean 时(调用 getBean())进行初始化。

c. 如果该 bean 的scope = prototype,则不管 lazy-init 的值是什么都是在第一次使用该 bean 时进行实例化。

其中,默认情况下 lazy-init = false

 

不同创建时机下的优缺点:

1. Spring 容器启动时创建:

优点: Spring 容器与 web 容器整合时,当 web 容器启动的时候就可以初始化 Spring 容器了,如果 Spring 容器内部有错误,则立马就会报错。

缺点:如果 bean 中存放着大量的数据,而且数据的初始化发生在 bean 初始化的时候,这就会导致数据过早加载到内存中。

2. 使用时再创建:

优点:按需求加载数据,什么时候用就什么时候加载。

缺点:不能提早发现问题,增加了排错的代价。

 

 

在 bean 中使用外部属性文件

在配置文件里配置 Bean 时,有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径,数据源配置信息等),而这些部署细节实际上需要和 Bean 配置相分离。Spring 提供了一个 PropertyPlaceholderConfigurer 的 BeanFactory 后置处理器,这个处理器允许用户将 Bean 配置的部分内容外移到属性文件中。可以在 Bean 配置文件里使用形式为 ${var} 的变量。

PropertyPlaceholderConfigurer 从属性文件里加载属性,并使用这些属性来替换变量。Spring 还允许在属性文件中使用 ${propName},以实现属性之间的相互引用。

#src下创建一个名字为db.properties的属性文件,存放连接数据库的信息
user=root
password=000
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///test
    <!-- 增加context命名空间,导入属性文件,类路径下的db.properties文件 -->
    <context:property-placeholder location="classpath:db.properties"/>
    
  <!-- 导入c3p0数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 使用外部化属性文件的属性 --> <property name="user" value="${user}"/> <property name="password" value="${password}"/> <property name="driverClass" value="${driverClass}"/> <property name="jdbcUrl" value="${jdbcUrl}"/> </bean>

<!--

  启动mysql,导入相应jar包,获取dataSource实例,打印dataSource.getConnection(),
  结果为:com.mchange.v2.c3p0.impl.NewProxyConnection@1a794e5 [wrapping: com.mysql.jdbc.JDBC4Connection@329302]
-->

 


 

Spring表达式语言:SpEL

Sping表达式语言(简称SpEL):是一个支持运行时查询和操作对象图的强大表达式语言,它为 bean 的属性进行动态赋值提供了便利

SpEL使用 #{...} 作为定界符,所有在大括号中的字符都将被认为是SpEL

通过SpEL我们可以实现以下功能:

1. 通过 bean 的 id 对 bean 进行引用

2. 调用方法以及引用对象中的属性

3. 计算表达式的值

4. 正则表达式的匹配

 

    <!-- 字面量的表示,以下value分别表示为:整数、小数、科学计数法、布尔值 -->
    <property name="a" value="#{3}"/>
    <property name="b" value="#{23.4}"/>
    <property name="c" value="#{10e3}"/>
    <property name="d" value="#{true}"/>
    
    <!-- String可以使用单引号或者双引号作为字符串的定界符号 -->
    <property name="name" value="#{'tom'}"/>
    <property name="name" value='#{"tom"}'/>
    <!-- 引用其它对象 -->
    <!-- 通过value属性和SpEL配置bean之间的应用关系 -->
    <propety name="prefix" value="#{prefixGenerator}"/>
    
    <!-- 应用其他对象属性 -->
    <!-- 通过value属性和SpEL配置suffix属性值为另外一个bean的suffix属性值 -->
    <property name="suffix" value="#{sequenceGeneraor.suffix}"/>
    
    <!-- 调用其它方法,还可以链式操作 -->
    <!-- 通过value属性和SpEL配置suffix属性值为另外一个bean的方法返回值 -->
    <property name="suffix" value="#{sequenceGenerator.toString()}"
    <!-- 方法的连缀 -->
    <property name="suffix" value="#{sequenceGenerator.toString().toUpperCase()}"/>
    <!-- SpEL支持的运算符号:+,-,*,/,%,^ -->
    <property name="count" value="#{counter.total + 42}"/>
    <property name="count" value="#{counter.total - 20}"/>
  <!-- 调用静态方法或静态属性:通过T()调用一个类的静态方法,它将返回一个Class Object,然后在调用相应的方法和属性 --> <property name="count" value="#{2 * T(java.lang.Math).PI * cirle.radius}"/> <property name="count" value="#{counter.total / counter.num}"/> <property name="count" value="#{counter.total % counter.num}"/> <property name="count" value="#{T{java.lang.Math}.PI * circle.radius ^ 2}"/> <!-- 加号还可以用作字符串的连接 --> <constructor-arg value="#{performer.firstName + ' ' + performer.lastName}"/> <!-- 比较运算符:<,>,==.<=,>=,lt,gt,eq,le,ge --> <property name="equal" value="#{counter.total le 100000}"/> <property name="equal" value="#{counter.total == 100000}"/> <!-- 逻辑运算符号:and,or,not --> <propery name="largeCircle" value="#{shape.kind == 'circle' and shape.perimer gt 10000}"/> <property name="outOfStock" value="#{!product.available}"/> <property name="outOfStock" value="#{not product.available}"/> <!-- if-else 运算符,类似于三目运算符 -->
   <!-- 如果歌曲为'Jingle Bells'就弹钢琴,否则演奏萨克斯 --> <constructor-arg value="#{songSelector.selectSong()=='Jingle Bells' ? piano : saxophone}"/>
   <!-- 如果kenny.song不为null,那么表达式结果是kenny.song,否则为'Greensleeves' -->
   <property name='song' value="#{kenny.song ?: 'Greensleeves'}"/>
<!-- 正则表达式:matches --> <constructor-arg value="#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z{2,4}]'}"/>

 


 

IoC容器中的生命周期

Spring IoC容器可以管理 bean 的生命周期,Spring允许在 bean 生命周期特定点执行定制的任务

Spring IoC容器对 bean 的生命周期进行管理的过程:

1. 通过构造器或工厂方法创建 bean 的实例

2. 为 bean 的属性设置值和对其他 bean 的引用

3. 调用 bean 的初始化方法

4. bean 可以使用了

5. 当容器关闭时,调用 bean 的销毁方法

 

在 bean 的配置声明里设置 init-method destory-method 属性,为 bean 指定初始化和销毁的方法。

//定义一个测试类
public class BeanLifeCycle
{
    private String name;
    
    public BeanLifeCycle()
    {
        System.out.println("constructor...");
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
        System.out.println("setMethod...");
    }
    
    public void beanInit()
    {
        System.out.println("initMethod...");
    }
    
    public void beanDestroy()
    {
        System.out.println("destroyMethod...");
    }
}
    <!-- 配置ApplicationContext.xml,设置 init-method 和 destroy-method -->
    <bean id="beanLifeCycle" class="com.bupt.springtest.beanXml.BeanLifeCycle" init-method="beanInit" destroy-method="beanDestroy">
        <property name="name" value="tom"/>
    </bean>
public class BeanLifeCycleTest
{
    public static void main(String[] args)
    {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        
        BeanLifeCycle blc = (BeanLifeCycle) ctx.getBean("beanLifeCycle");
        
        System.out.println(blc);
        
        //关闭IoC服务器
        ctx.close();
    }
}
/** * 打印结果: * constructor...(创建bean实例) * setMethod...(设置属性) * initMethod...(调用初始化方法) * com.bupt.springtest.beanXml.BeanLifeCycle@1a5c39e(使用bean) * destroyMethod...(容器关闭,调用销毁方法) */

 

如果上下文中定义的很多 bean 都拥有相同名字的初始化方法和销毁方法,你不必每一个 bean 都声明初始化和销毁方法,你可是使用  <beans> 元素的 default-init-method default-destory-method 属性

<?xml version="1.0" encoding="utf-8">
<beans xmlns= .....
              .....
        .....
default-init-method="beanInit" default-destroy-method="beanDestroy"> ........ </beans>

 

创建 bean 的后置处理器

bean 后置处理器允许在调用初始化方法前后对 bean 进行额外的处理,它会对IoC容器中的所有 bean 实例逐一处理,而非单一实例。其典型应用是:检查 bean 属性的正确性或根据特定的标准更改 bean 的属性。对 bean 后置处理而言,需要实现 org.springframework.beans.factory.config 包下的 Interface BeanPostProcessor 。在初始化方法被调用前后,Spring将把每个 bean 实例分别传递给上述接口的以下两个方法:

postProcessorBeforeInitialization(Object bean, String beanName)

postProcessorAfterInitialization(Object bean, String beanName)

//创建一个后置处理器的实现类,实现BeanPostProcessor接口,提供具体实现
public class MyBeanPostProcessor implements BeanPostProcessor
{
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
    {
        System.out.println("postProcessorBeforeInitialization...");
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
    {
        System.out.println("postProcessorAfterInitialization...");
        return bean;
    }
}
    <!-- 配置ApplicationContext.xml,设置 init-method 和 destroy-method -->
    <bean id="beanLifeCycle" class="com.bupt.springtest.beanXml.BeanLifeCycle" init-method="beanInit" destroy-method="beanDestroy">
        <property name="name" value="tom"/>
    </bean>
        
    <!-- 配置 bean 的后置处理器:不需要配置id,IoC容器自动识别为后置处理器 -->
    <bean class="com.bupt.springtest.beanXml.MyBeanPostProcessor"/>    
public class BeanLifeCycleTest
{
    public static void main(String[] args)
    {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        
        BeanLifeCycle blc = (BeanLifeCycle) ctx.getBean("beanLifeCycle");
        
        System.out.println(blc);
        
        //关闭IoC服务器
        ctx.close();
    }
}

/**
 * 打印结果:
 * constructor...
 * setMethod...
 * postProcessorAfterInitialization...
 * initMethod...
 * postProcessorBeforeInitialization...
 * com.bupt.springtest.beanXml.BeanLifeCycle@10ab905
 * destroyMethod...
 */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
    {
        //因为后置处理器是对IoC容器所有的 bean 进行处理,所以我们可以加上判断语句,以达到对特定bean的处理
        if("beanLifeCycle".equals(beanName))
        {
            //...
        }
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
    {
        //可以在方法内重新实例化一个bean,返回或是设置属性,此时,BeanLifeCycle的name属性不再是xml里配置的tom而变成了jack
        BeanLifeCycle bfc = new BeanLifeCycle();
        bfc.setName("jack");
        return bfc;
    }

 


 

 

通过工厂方法配置bean

bean 的配置方式可以通过全类名即反射的方式来配置,也可以通过工厂方法 (静态工厂方法和实例工厂方法) 以及spring为我们提供的 FactoryBean 方法来配置。

调用静态工厂方法创建bean:是将对象创建过程封装到静态方法中。当客户端需要对象时,只需要简单地调用静态方法,而不需要关心创建对象的细节。

要声明通过静态方法创建的bean,需要在bean的class属性里面指定拥有该工厂的方法的类,同时在 factory-method 属性里指定工厂方法的名称。最后使用

<constructor-arg> 元素为该方法传递方法参数。

package com.bupt.springtest.beanFactory;

//创建一个bean 实例
public class Address
{
    private String city;
    private String street;
    public String getCity()
    {
        return city;
    }
    public void setCity(String city)
    {
        this.city = city;
    }
    public String getStreet()
    {
        return street;
    }
    public void setStreet(String street)
    {
        this.street = street;
    }
    @Override
    public String toString()
    {
        return "Address [city=" + city + ", street=" + street + "]";
    }
    public Address(String city, String street)
    {
        this.city = city;
        this.street = street;
    }
}
package com.bupt.springtest.beanFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * 静态工厂方法: 直接调用某个类的静态方法就可以返回 bean 的实例
 */
public class StaticAddressFactory
{
    private static Map<String, Address> address = new HashMap<>();
    
    static
    {
        address.put("A", new Address("beijing", "xidan"));
        address.put("B", new Address("shanghai", "nanjiinglu"));
    }
    
    //静态工厂方法
    public static Address getAddress(String name)
    {
        return address.get(name);
    }
}
  <!-- 使用静态工厂方法来配置bean,注意不是配置静态工厂方法实例,而是配置bean实例,此例中即配置Address实例 -->
  <!--
     class属性:指向静态工厂方法的全类名
     factory-method:指向静态工厂方法的名字
     constructor-arg如果工厂方法需要传入参数则使用 constructor-arg 来配置参数
  --> <bean id="address" class="com.bupt.springtest.beanFactory.StaticAddressFactory" factory-method="getAddress"> <constructor-arg value="A"/> </bean>
public class FactoryTest
{
    public static void main(String[] args)
    {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        
        Address address = (Address) ctx.getBean("address");
        
        System.out.println(address);
    }
}
//打印结果:Address [city=beijing, street=xidan]

 

实例工厂方法:将对象的创建过程封装到另一个对象实例的方法里。当客户端需要请求对象时,只需要简单地调用该实例方法而不需要关心对象的创建细节。

要声明通过实例工厂方法创建的bean需要:

1. 在bean的 factory-bean 属性里指定拥有该工厂方法的bean;

2. 在 factory-method 属性里指定该工厂方法的名称;

3. 使用 <constructor-arg>元素为工厂方法传递参数。

/**
 * 实例工厂方法:需要创建工厂本身,再调用工厂的实例方法来返回 bean 的实例
 */
public class InstanceAddressFactory
{
    private static Map<String, Address> address = null;
    
    public InstanceAddressFactory()
    {
        address = new HashMap<>();
        address.put("A", new Address("beijing", "changanjie"));
        address.put("B", new Address("shanghai", "nanjinglu"));
    }
    public Address getAddress(String name)
    {
        return address.get(name);
    }
}
    <!-- 配置工厂的实例 -->
    <bean id="addressFactory" class="com.bupt.springtest.beanFactory.InstanceAddressFactory"/>
    
    <!-- 通过实例工厂方法来配置 bean -->
    <bean id="address" factory-bean="addressFactory" factory-method="getAddress">
        <constructor-arg value="B"/>
    </bean>
    <!-- 打印结果:Address [city=shanghai, street=nanjinglu] -->

 

FactoryBean

Spring中有两类bean,一种是普通的 bean,另一种是工厂 bean,即 FactoryBean

工厂 bean 和普通的 bean 不同,其返回值的对象不是指定类的一个实例,而是该工厂 bean 的getObject()方法所返回的对象

当配置某一个 bean 时需要用到IoC容器中其它的 bean ,这时通过 FactoryBean 去配置是最合适的

public class AddressFactoryBean implements FactoryBean<Address>
{
    private String city;

    public void setCity(String city)
    {
        this.city = city;
    }

    //返回 bean 的对象
    @Override
    public Address getObject() throws Exception
    {
        return new Address(city, "xindan");
    }

    //指定返回 bean 的类型
    @Override
    public Class<?> getObjectType()
    {
        return Address.class;
    }

    //返回的实例是否为单例
    @Override
    public boolean isSingleton()
    {
        return true;
    }
}
    <!-- 
        通过 FactoryBean 来配置 bean 的实例
        class:指向 FactoryBean 的全类名
        property:配置 FactoryBean 的属性
        
        实际上返回的实例是 FactoryBean 的 getObject()方法返回的实例
     -->
    <bean id="address" class="com.bupt.springtest.beanFactory.AddressFactoryBean">
        <property name="city" value="beijing"/>
    </bean>
   <!-- 打印结果:Address [city=beijing, street=xindan] -->

posted on 2016-06-12 17:09  Traveling_Light_CC  阅读(647)  评论(0编辑  收藏  举报

导航