7 -- Spring的基本用法 -- 5... Spring容器中的Bean;容器中Bean的作用域;配置依赖;

    7.5 Spring容器中的Bean

      7.5.1 Bean的基本定义和Bean别名

        <beans.../>元素是Spring配置文件的根元素,该元素可以指定如下属性:

          default-lazy-init : 指定该<beans.../> 元素下配置的所有Bean默认的延迟初始化行为。

          default-merge : 指定该<beans.../> 元素下配置的所有Bean默认的merge行为。

          default-autowire : 指定该<beans.../> 元素下配置的所有Bean 默认的自动装配行为。

          default-autowire-candidates: : 指定该<beans.../> 元素下配置的所有Bean 默认是否作为自动装配的候选Bean。

          default-init-method : 指定该<beans.../> 元素下配置的所有Bean 默认的初始化方法。

          default-destroy-method : 指定该<beans.../> 元素下配置的所有Bean 默认的回收方法。

        <beans.../>元素下所能指定的属性都可以在每个<bean.../>子元素中指定----将属性名去掉default即可。区别是:为<bean.../>指定的这些属性,只对特定Bean起作用;如果在<beans.../>元素下指定这些属性,这些属性将会对<beans.../>包含的所有Bean都起作用。当二者所指定的属性不一致时,<bean.../>下指定的属性会覆盖<beans.../>下指定的属性。

        <bean.../>元素的id属性具有唯一性,而且是一个真正的XML ID 属性,因此其他XML元素在引用id时,可以利用XML解析器的验证功能。

        指定别名有两种方式:

        ⊙ 定义<bean.../>元素时通过name属性指定别名;如果需要为Bean实例指定多个别名,则可以在name属性中使用逗号、冒号或者空格来分隔多个别名,后面通过任一别名即可访问该Bean实例。

        ⊙ 通过<alias.../>元素为已有的Bean指定别名。

          <alias name="" alias=""/> name:指定一个Bean实例的标识名,表明将为该Bean实例指定别名;alias:指定一个别名。

<bean id="person" class="edu.pri.lime._7_4_5.bean.Person" name="chinese,lime,oracle"/>
    <alias name="person" alias="jackson"/>
    <alias name="person" alias="tomcat"/>

      7.5.2 容器中Bean的作用域

        当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。

          ⊙ singleton : 单例模式,在整个Spring IoC容器中,singleton作用域的Bean将只生成一个实例。

          ⊙ prototype : 每次通过容器的getBean()方法获取prototype作用域的Bean时,都将产生一个新的Bean实例。

          ⊙ request : 对于一次HTTP请求,request作用域的Bean将只生成一个实例,这意味着,在同一次HTTP请求内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。

          ⊙ session : 对于一次HTTP回话,session作用域的Bean将只生成一个实例,这意味着,在同一次HTTP回话内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。

          ⊙ global session : 每个全局的HTTP Session对应一个Bean实例。在典型的情况下,仅在使用portlet context的时候有效。只有在Web应用中使用Spring时,该作用域才真正有效。

        比较常用的是singleton和prototype两种作用域,对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new关键字创建Bean实例,一旦创建成功,容器就不再跟踪实例,也不会维护Bean实例的状态。

        如果不指定Bean的作用域,Spring默认使用singleton作用域。

        Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域的Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,就可以重复使用。

        Spring配置文件通过scope属性指定Bean的作用域。

        对于request作用域:

<bean id="loginAction" class="edu.pri.lime._7_5_2.bean.LoginAct" scope="request"/>

        针对每次HTTP请求,Spring容器会根据loginAction Bean定义创建一个全新的LoginAction Bean实例,且该loginAction Bean 实例仅在当前HTTP Request内有效。因此,如果程序需要,完全可以自由更改Bean实例的内部状态;其他请求所获得的loginAction Bean实例无法感受到这种内部状态的改变。当处理请求结束时,request作用域的Bean实例将被销毁。

        request、session作用域的Bean只对Web应用才真正有效。实际上通常只会将Web应用的控制器Bean指定成request作用域。

        request和session作用域只在Web应用中才有效,并且必须在Web应用中增加额外配置才会生效。为了让request和session两个作用域生效,必须将HTTP请求对象绑定到为该请求提供服务的线程上,这是的具有request和session两个作用域的Bean实例能够在后面的调用链中被访问到。

        对于支持Servlet2.4 及更新规范的Web容器,可以在Web应用的web.xml文件中增加如下Listener配置,该Listener负责是request作用域生效。

        Web : Listener

<listener>
    <listener-class>
        org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>

        对于只支持Servlet2.4 以前规范的Web容器,则该容器不支持Listener规范,故无法使用这种配置方式,只能改为使用Filter配置方式。

        Web :Filter

<filter>
    <filter-name>requestContextFilter</filter-name>
    <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>requestContextFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

        一旦web.xml中增加了如上任意一种配置,程序就可以在Spring配置文件中使用request或session作用域了。

        xml :app_7_5_2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <!-- 指定使用request作用域 -->
    <!-- 这样Spring容器会为每次HTTP请求生成一个Person实例,当请求响应结束时,该实例也随之消失。 -->
    <bean id="per" class="edu.pri.lime._7_4_5.bean.Person" scope="request" />

    <!-- 默认作用域为singleton -->
    <bean id="chinese" class="edu.pri.lime._7_4_5.bean.Person" />
    <!-- 指定作用域为prototype -->
    <bean id="french" class="edu.pri.lime._7_4_5.bean.Person" scope="prototype" />
    <bean id="date" class="java.util.Date" />
    <bean id="loginAction" class="edu.pri.lime._7_5_2.bean.LoginAct"
        scope="request" />

</beans>

        如果Web应用直接使用Spring MVC作为MVC框架,即用SpringDispatcherServlet或DispatcherPorlet来拦截所有用户请求,则无须这些额外的配置,因为Spring DispatcherServlet和DispatcherPortlet已经处理了所有和请求有关的状态处理。

        Jsp :

<%
    /* 获取Web应用初始化的Spring容器 */
    WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext("app_7_5_2.xml");
    /* 两次获取容器中id为per的Bean */
    Person perA = (Person)ctx.getBean("per");
    Person perB = (Person)ctx.getBean("per");
    out.println((perA == perB) + "<br/>");
    out.println(perA);
    
%>

      7.5.3 配置依赖

        Java 应用中各组件相互调用的实质可以归纳为依赖管理,根据注入方式的不同,Bean的依赖注入通常有如下两种形式。

          设置注入 : 通过<property.../>元素驱动Spring执行setter方法。

          构造注入 : 通过<constructor-arg.../>元素驱动Spring执行带参数的构造器。

        通常不建议使用配置文件管理Bean的基本类型的属性值;通常只使用配置文件管理容器中Bean与Bean之间的依赖关系。

        对于singleton作用域的Bean,如果没有强行取消其预初始化行为,系统会在创建Spring容器时预初始化所有的singleton Bean,与此同时,该Bean所以来的Bean也被一起实例化。

        BeanFactory 与 ApplicationContext 实例化容器中Bean的时机不同;BeanFactory等到程序需要Bean实例时才创建Bean;而ApplicationContext在容器创建ApplicationContext实例时,会预初始化容器中所有的singleton Bean。

        因为采用ApplicationContext作为Spring容器,创建容器时会同时创建容器中所有singleton作用域的Bean,因此可能需要更多的系统开销。但一旦创建成功,应用后面的响应速度更快,因此,对于普通的Java EE 应用,推荐使用ApplicationContext作为Spring容器。

        Spring的作用就是管理Java EE组件,Spring把所有的Java对象都称为Bean。因此完全可以把任何Java类都部署在Spring容器中----只要该Java类具有响应的构造器即可。Spring可以为任何Java对象注入任何类型的属性------只要该Java对象为该属性提供了对应的setter方法即可。

        Java 类的成员变量可以是各种数据类型,出了基本类型值、字符串类型值等,还可以是其他Java 实例,也可以是容器中的其他Bean实例,甚至是Java 集合、数组等,所以Spring允许通过如下元素为setter方法、构造器参数指定参数值:

        ⊙ value

        ⊙ ref

        ⊙ bean

        ⊙ list、set、map 及 props

      7.5.4 设置普通属性值------<value>

        <value.../>元素用于指定基本类型及其包装、字符串类型的参数值,Spring使用XML解析器来解析出这些数据,然后利用java.beans.PropertyEditor完成类型转换:从java.lang.String类型转换为所需的参数值类型。

        Class : ExampleBean

package edu.pri.lime._7_5_4.bean;

public class ExampleBean {

//    定义一个int型的成员变量
    private int integerField;
//    定义一个double型的成员变量
    private double doubleField;
//    integerField和doubleField的setter和getter值
    public int getIntegerField() {
        return integerField;
    }
    public void setIntegerField(int integerField) {
        this.integerField = integerField;
    }
    public double getDoubleField() {
        return doubleField;
    }
    public void setDoubleField(double doubleField) {
        this.doubleField = doubleField;
    }
    @Override
    public String toString() {
        return "ExampleBean [integerField=" + integerField + ", doubleField=" + doubleField + "]";
    }
}

        XML :

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <bean id="exampleBean" class="edu.pri.lime._7_5_4.bean.ExampleBean">
        <property name="integerField" value="1" />
        <property name="doubleField" value="3.1" />
    </bean>

</beans>

        Class : Test

package edu.pri.lime._7_5_4.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import edu.pri.lime._7_5_4.bean.ExampleBean;

public class Test {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_5_4.xml");
        ExampleBean exb = ctx.getBean("exampleBean",ExampleBean.class);
        System.out.println(exb.toString());
    }
}

       7.5.5 配置合作者Bean------<ref.../>

        如果需要为Bean设置的属性值是容器中的另一个Bean实例,则应该使用<ref.../>元素。使用<ref.../>元素时可指定一个bean属性,该属性用于引用容器中其他Bean实例的id属性值。

        

   <!-- 配置名为chinese的Bean。其实现类是edu.pri.lime._7_3_3.bean.impl.Chinese类 -->
    <bean id="chinese" class="edu.pri.lime._7_3_3.bean.impl.Chinese">
        <!-- 驱动Spring调用Chinese的带一个参数的构造器来创建对象 -->
        <constructor-arg ref="stoneAxe"/>
    </bean>

        <contructor-arg.../>运算也可增加ref属性,从而指定将容器中另一个Bean作为构造器参数。

      7.5.6 使用自动装配注入合作者Bean------<beans default-autowire="" .../> 或 <bean autowire="" .../>

        Spring 能自动装配Bean与Bean之间的依赖关系,即无须使用ref显示指定依赖Bean,而是有Spring容器检查XML配置文件内容,根据某种规则,为调用者Bean注入被依赖的Bean。

        Spring 的自动装配可通过<beans.../>元素的default-autowire属性指定,该属性对配置文件中所有的Bean起作用;也可通过<bean.../>元素的autowire属性指定,该属性只对该Bean起作用。

        自动装配可以减少配置文件的工作量,但降低了依赖关系的透明性和清晰性。

        default-autowire、autowire属性可以接受如下值:

        ⊙ no : 不使用自动装配。Bean依赖必须通过ref元素定义。这是默认配置,在较大的部署环境中不鼓励改变这个配置,显示配置合作者能够得到跟清晰的依赖关系。

        ⊙ byName : 根据setter方法名进行自动装配。Spring 容器查找容器中的全部Bean,找出其id与setter方法匹配的Bean作为参数来完成注入。如果没有找到匹配的Bean实例,则Spring不会进行任何注入。

        ⊙ byType : 根据setter方法的形参类型来自动装配。Spring容器查找容器中的全部Bean,如果正好有一个Bean类型与setter方法的形参类型匹配,就自动注入这个Bean;如果找到多个这样的Bean,就抛出一个异常;如果没有找到这样的Bean,则什么都不会发生,setter方法不会被调用。

        ⊙ constructor : 与byType类似,区别是用于自动匹配构造器的参数。如果容器不能恰好找到一个与构造器类型匹配的Bean,则会抛出一个异常。

        BUG ⊙ autodetect : Spring容器根据Bean内部结构,自行决定使用constructor或byType策略。如果找到一个默认的构造函数,那么就会应用byType策略。

        ⊙ default : 

        7.5.6.1 byName 规则

          byName 规则是指setter方法的方法名与Bean的id进行匹配,假如Bean A的实现类包含setB()方法,而Spring的配置文件恰好包含id为b的Bean,则Spring容器会将b实例注入Bean A中。如果容器中没有名字匹配的Bean,Spring则不会做任何事情。

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <!-- 指定使用byName策略,Spring会根据setter方法的方法名与Bean的id进行匹配 -->
    <bean id="chinese" class="edu.pri.lime._7_5_6_1.bean.impl.Chinese" autowire="byName"/>
    
    <bean  id="axe" class="edu.pri.lime._7_3_2.bean.impl.SteelAxe"/>
</beans>

        7.5.6.2 byType 规则

          byType 规则是根据setter方法的参数类型与Bean的类型进行匹配。假如A实例有setB(B b)方法,而Spring的配置文件中恰好有一个类型为B的Bean实例,容器为A注入类型匹配的Bean实例,如果容器中没有类型为B的实例,Spring不会调用setB()方法;但如果容器中包含多于一个的B实例,程序将会抛出异常。

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <!-- 指定使用byType策略,Spring会根据setter方法的参数类型与Bean的类型进行匹配 -->
    <bean id="chinese" class="edu.pri.lime._7_5_6_1.bean.impl.Chinese" autowire="byType"/>
    
    <bean  id="steelAxe" class="edu.pri.lime._7_3_2.bean.impl.SteelAxe"/>
</beans>

          当一个Bean既使用自动装配依赖,又使用ref显式指定依赖时,则显式指定的依赖覆盖自动装配依赖。

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <!-- 即使china类中有setAxe(Axe axe) 方法,Spring也依赖以steelAxe作为调用setAxe()方法的参数,而不会以axe作为参数 -->
    <bean id="china" class="edu.pri.lime._7_5_6_1.bean.impl.Chinese" autowire="byName">
        <property name="axe" ref="stoneAxe"/>
    </bean>

    <bean  id="axe" class="edu.pri.lime._7_3_2.bean.impl.SteelAxe"/>
    
    <bean id="stoneAxe" class="edu.pri.lime._7_3_2.bean.impl.StoneAxe"/>
</beans>

          在某些情况下,程序希望将某些Bean排除在自动装配之外,不作为Spring自动装配策略的候选者,此时可设置autowire-candidate属性,通过为<bean.../>元素设置autowire-candidate=“false”,即可将Bean排除在自动装配之外,容器在查找自动装配Bean时将不考虑该Bean。

          还可通过在<beans.../>元素中指定default-autowire-candidates属性将一批Bean排除在自动装配之外。default-autowire-candidates属性的值允许使用模式字符串,例如指定default-autowire-candidates=“*abc”,则所有以“abc”结尾的Bean都将被排除在自动装配之外。不仅如此,该属性甚至可以指定多个模式字符串,这样所有匹配任意模式字符串的Bean都将被排除在自动装配之外。

      7.5.7 注入嵌套Bean

        如果某个Bean所依赖的Bean不想被Spring容器直接访问,则可以使用嵌套Bean。

        把<bean.../>配置成<property.../>或<constructor-args.../>的子元素,那么该<bean.../>元素配置的Bean仅仅作为setter注入、构造注入的参数,这种Bean就是嵌套Bean。由于容器布恩那个获取嵌套Bean,因此它不需要指定id属性。

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <bean id="chinese" class="edu.pri.lime._7_5_6_1.bean.impl.Chinese">
    <!-- 驱动调用chinese的setAxe()方法,使用嵌套Bean作为参数 -->
        <property name="axe">
            <!-- 嵌套Bean配置的对象仅作为setter方法的参数,嵌套Bean不能被容器访问,因此无须指定id属性 -->
            <bean class="edu.pri.lime._7_3_2.bean.impl.SteelAxe"/>
        </property>
    </bean>

</beans>

        使用嵌套Bean与使用ref引用ref引用容器中另一个Bean在本质上是一样的。

        Spring框架的本质就是通过XML配置文件来驱动Java代码,当程序要调用setter方法或有参数的构造器时,程序总需要传入参数值,随参数类型的不同,Spring配置文件当然也要随之改变。

        ⊙ 形参类型是基本类型、String、日期等,直接使用value指定字面值即可。

        ⊙ 形参类型是复合类,那就需要传入一个Java队形作为实参,于是有两种方式:① 使用ref引用一个容器中已配置的Bean;② 使用<bean.../>元素配置一个嵌套Bean。

      7.5.8 注入集合值

        如果需要调用形参类型为集合的setter方法,或调用形参类型为集合的构造器,则可使用集合元素<list.../>、<set.../>、<map.../>和<props.../>分别来设置类型为List、Set、Map和Properties的集合参数值。

        Class : Chinese

package edu.pri.lime._7_5_8.bean.impl;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import edu.pri.lime._7_5_8.bean.Axe;
import edu.pri.lime._7_5_8.bean.Person;

public class Chinese implements Person {

//    集合类型的成员变量
    private List<String> schools;
    private Map scores;
    private Map<String,Axe> phaseAxes;
    private Properties health;
    private Set axes;
    private String[] books;
    public Chinese() {
        super();
        System.out.println("Spring 实例化主调bean:Chinese实例...");
    }
    public List<String> getSchools() {
        return schools;
    }
    public void setSchools(List<String> schools) {
        this.schools = schools;
    }
    public Map getScores() {
        return scores;
    }
    public void setScores(Map scores) {
        this.scores = scores;
    }
    public Map<String, Axe> getPhaseAxes() {
        return phaseAxes;
    }
    public void setPhaseAxes(Map<String, Axe> phaseAxes) {
        this.phaseAxes = phaseAxes;
    }
    public Properties getHealth() {
        return health;
    }
    public void setHealth(Properties health) {
        this.health = health;
    }
    public Set getAxes() {
        return axes;
    }
    public void setAxes(Set axes) {
        this.axes = axes;
    }
    public String[] getBooks() {
        return books;
    }
    public void setBooks(String[] books) {
        this.books = books;
    }
//    访问全部的集合类型的成员变量
    public void test(){
        System.out.println(schools);
        System.out.println(scores);
        System.out.println(phaseAxes);
        System.out.println(health);
        System.out.println(axes);
        System.out.println(Arrays.toString(books));
    }
}

        XML : 

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <!-- 定义2个普通的Axe Bean -->
    <bean id="stoneAxe" class="edu.pri.lime._7_5_8.bean.impl.StoneAxe" />
    <bean id="steelAxe" class="edu.pri.lime._7_5_8.bean.impl.SteelAxe" />
    <!-- 定义chinese Bean -->
    <bean id="chinese" class="edu.pri.lime._7_5_8.bean.impl.Chinese">
        <!-- Spring 对List集合和数组的处理是一样的,都用<list.../>元素来配置 -->
        <property name="schools">
            <!-- 为调用setSchools()方法配置List集合作为参数值 -->
            <list>
                <!-- 每个value、ref、bean...都可配置一个List元素 -->
                <value>小学</value>
                <value>中学</value>
                <value>大学</value>
            </list>
        </property>
        <property name="scores">
            <!-- 为调用setScores()方法配置Map集合作为参数值 -->
            <map>
                <!-- 每个entry都配置一个key-value对 -->
                <entry key="数学" value="87" />
                <entry key="英语" value="89" />
                <entry key="语文" value="82" />
            </map>
        </property>
        <property name="phaseAxes">
            <!-- 为调用setPhaseAxes()方法配置Map集合作为参数 -->
            <map>
                <!-- 每个entry都配置一个key-value对 -->
                <entry key="原始社会" value-ref="stoneAxe" />
                <entry key="农业社会" value-ref="steelAxe" />
            </map>
        </property>
        <property name="health">
            <!-- 为调用prop元素都匹配一个属性项,其中key指定属性名 -->
            <props>
                <prop key="血压">正常</prop>
                <prop key="身高">175</prop>
            </props>
<!--             
            <value>
                pressure=normal
                height=175
            </value>
 -->
        </property>
        <property name="axes">
            <!-- 为调用setAxes()方法配置Set集合作为参数值 -->
            <set>
                <!-- 每个value、ref、bean...都配置一个Set元素 -->
                <value>普通的字符串</value>
                <bean class="edu.pri.lime._7_5_8.bean.impl.SteelAxe"/>         
                <ref bean="stoneAxe"/>
                <!-- 为Set集合配置一个List集合作为元素 -->
                <list>
                    <value>20</value>
                    <!-- 再次为List集合配置一个Set集合作为元素 -->
                    <set>
                        <value type="int">30</value>
                    </set>
                </list>
            </set>
        </property>
        <!-- Spring 对List集合和数组的处理是一样的,都用<list.../>元素来配置 -->
        <property name="books">
            <!-- 为调用setBooks()方法配置数组作为参数值 -->
            <list>
                <!-- 每个value、ref、bean...都可配置一个数组元素 -->
                <value>li</value>
                <value>me</value>
                <value>Oracle</value>
            </list>
        </property>
    </bean>
</beans>

        Spring对List集合和数组的处理是一样的,都用<list.../>元素来配置。

        当使用<list.../>、<set.../>、<map.../>等元素配置集合类型的参数值时,还需要配置集合元素。由于集合元素又可以是基本类型值、引用容器中的其他Bean、嵌套Bean或集合属性等,所以<list.../>、<key.../>和<set.../>元素又可接受如下子元素。

          ⊙ value : 指定集合元素是基本数据类型值或字符串类型值。

          ⊙ ref : 指定集合元素是容器中的另一个Bean实例。

          ⊙ bean : 指定集合元素是一个嵌套Bean。

          ⊙ list、set、map及props : 指定集合元素又是集合。

        <props.../>元素用于配置Properties类型的参数值,Properties类型是一种特殊的类型,其key和value都只能是字符串,故Spring配置Properties类型的参数值比较简单:每个key-value对只要分别给出key和value就足够了------而且key和value都是字符串类型,所以使用如下格式的<prop.../>元素就够了。

          ⊙ <prop key="key">value</prop> : 其中<prop.../>元素的key属性指定key的值,<prop.../>元素的内容指定value的值。

        当使用<map.../>元素配置Map参数值时,Map集合的每个元素由key、value两个部分组成,所以配置文件中的每个<entry.../>配置一组key-value对,其中<entry.../>元素支持如下4个属性:

            ⊙ key : 如果Map key是基本类型值或字符串,则可使用该属性来指定Map key。

            ⊙ key-ref : 如果Map key是容器中的另一个Bean实例,则可使用该属性指定容器中其他Bean的id。

            ⊙ value : 如果Map value 是节本类型值或字符串,则可使用该属性来指定Map value。

            ⊙ value-ref : 如果Map value 是容器中的另一个Bean实例,则可使用该属性指定容器中其他Bean的id。

        Spring 还提供了一个简化语法来支持Properties形参的setter方法:但这种配置语法中属性名、属性值都只能是英文、数字,不可出现中文。

        <property name="health">
            <!-- 为调用prop元素都匹配一个属性项,其中key指定属性名 -->
            <!-- <props>
                <prop key="血压">正常</prop>
                <prop key="身高">175</prop>
            </props> -->
            <value>
                pressure=normal
                height=175
            </value>
        </property>

        从Spring 2.0 开始,Spring IoC容器支持集合的合并,子Bean中集合属性值可以从其父Bean的集合属性继承和覆盖而来。即子Bean的集合属性的最终值是父Bean、子Bean合并后的最终结果,而且子Bean集合的元素可以覆盖父Bean集合中对应的元素。

<beans>
    <!-- 将父Bean定义成抽象Bean -->
    <bean id="parent" abstract="true" class="edu.pri.lime._7_5_8.bean.impl.ComplexObject">
        <!-- 定义Properties类型的集合属性 -->
        <property name="adminEmails">
            <props>
                <prop key="administrator">administrator@crazyit.org</prop>
                <prop key="support">support@crazyit.org</prop>
            </props>
        </property>
    </bean>
    <!-- 使用parent属性指定该Bean继承了parent Bean -->
    <bean id="child" parent="parent">
        <property name="adminEmails">
            <!-- 指定该集合属性支持合并 -->
            <props merge="true">
                <prop key="sales">sales@crazyit.org</prop>
                <prop key="support">master@crazyit.org</prop>
            </props>
        </property>
    </bean>
</beans>

        由于泛型的支持,Java可以使用泛型指定集合元素的类型,Spring可通过反射来获取集合元素的类型,这样Spring的类型转换器就会起作用了。

        Class : Test

package edu.pri.lime._7_5_8.bean.impl;

import java.util.Map;

public class Test {
// 程序使用了泛型限制了Map的key是String,value是double,Spring可根据泛型信息把配置文件的集合参数值转换成响应的数据类型
    private Map<String,Double> prices;

    public void setPrices(Map<String, Double> prices) {
        this.prices = prices;
    }
    
}

        XML : 

  <bean id="test" class="">
        <property name="prices">
            <map>
                <!-- 程序使用了泛型限制了Map的key是String,value是double,Spring可根据泛型信息把配置文件的集合参数值转换成响应的数据类型 -->
                <entry key="li" value="23.1"/>
                <entry key="me" value="32.1"/>
            </map>
        </property>
    </bean>

        Spring 会自动将每个entry中的key值转换成String类型,并将value指定的值转换成Double类型。

      7.5.9 组合属性

        Spring 还支持组合属性的方式。例如,使用配置文件为形如foo.bar.name的属性设置参数值。为Bean的组合属性设置参数值时,除最后一个属性之外,其他属性值都不允许为null。

        Class : ExampleBean

package edu.pri.lime._7_5_9.bean;

public class ExampleBean {

//    定义一个Person类型的成员变量。使用组合属性设置参数值时person不能为空
    private Person person = new Person();

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }
    
}

        Class : Person

package edu.pri.lime._7_5_9.bean;

public class Person {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

        XML : 

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <bean id="exampleBean" class="edu.pri.lime._7_5_9.bean.ExampleBean">
        <!-- 驱动Spring调用exampleBean的getPerson().setName()方法,以lime为参数 -->
        <property name="person.name" value="lime" />
    </bean>
</beans>

        Class : TestBean

package edu.pri.lime._7_5_9.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import edu.pri.lime._7_5_9.bean.ExampleBean;

public class TestBean {

    public static void main(String[] args){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_5_9.xml");
        ExampleBean exampleBean = ctx.getBean("exampleBean",ExampleBean.class);
        System.out.println(exampleBean.getPerson().getName());
    }
}

      7.5.10 Spring的Bean和JavaBean

        Spring容器对Bean没有特殊要求,甚至不需要该Bean像标准的JavaBean------必须为每个属性提供对应的getter和setter方法。

        Spring中的Bean是Java实例、Java组件;传统Java应用中的JavaBean通常作为DTO(数据传输对象),用来封装值对象,在各层之间传递数据。

        传统的JavaBean也可作为Spring的Bean,从而接受Spring管理。

        eg:将数据源配置成容器中的Bean,该数据源Bean即可用于获取数据库连接。

        XML :

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <!-- 定义数据源Bean,使用C3P0数据源实现 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定连接数据库的驱动 -->
        <property name="dirverClass" value="com.mysql.jdbc.Driver" />
        <!-- 指定连接数据库的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://localhost/spring" />
        <!-- 指定连接数据库的用户名 -->
        <property name="user" value="root" />
        <!-- 指定连接数据库的密码 -->
        <property name="password" value="system" />
        <!-- 指定连接数据库连接池的最大连接数 -->
        <property name="maxPoolSize" value="200" />
        <!-- 指定连接数据库连接池的最小连接数 -->
        <property name="minPoolSize" value="2" />
        <!-- 指定连接数据库连接池的初始连接数 -->
        <property name="initialPoolSize" value="2" />
        <!-- 指定连接数据库连接池的连接的最大空闲时间 -->
        <property name="maxIdleTime" value="200" />
    </bean>
</beans>

        Class : BeanTest

package edu.pri.lime._7_5_10.main;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanTest {

    public static void main(String[] args) throws SQLException{
        ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_5_10.xml");
        DataSource ds = ctx.getBean("dataSource",DataSource.class);
//        通过DataSource来获取数据库连接
        Connection conn = ds.getConnection();
//        通过数据库连接获取PerparedStatement
        PreparedStatement pstmt = conn.prepareStatement("insert into news_inf value(null,?,?)");
        pstmt.setString(1, "lime Cry");
        pstmt.setString(1, "lime Happy");
//        执行SQL语句
        pstmt.executeUpdate();
//        清理资源,回收数据库连接资源
        if(pstmt != null){
            pstmt.close();
        }
        if(conn != null){
            conn.close();
        }
    }
}

        虽然Spring对Bean没有特殊要求,但依然建议Spring中的Bean应满足如下几个原则:

          ⊙ 尽量为每个Bean实现类提供无参数的构造器。

          ⊙ 接受构造注入的Bean,则应提供对应的、带参数的构造器。

          ⊙ 接受设置注入的Bean,则应提供对应的setter方法,并不要求提供对应的getter方法。

        传统的JavaBean和Spring中的Bean之间的区别:

          ⊙ 用处不同 : 传统的JavaBean更多是作为值对象传递参数;Spring的Bean用处几乎无所不包,任何应用组件都被称为Bean。

          ⊙ 写法不同 : 传统的JavaBean作为值对象,要求每个属性都提供getter和setter方法;但Spring的Bean只需为接受设置注入的属性提供setter方法即可。

          ⊙ 生命周期不同 : 传统的javaBean作为值对象传递,不接受任何容器管理其生命周期;Spring中的Bean有Spring管理其声明周期行为。

原文地址:https://www.cnblogs.com/ClassNotFoundException/p/6241349.html