• 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] -->
  • 相关阅读:
    mysql锁表与解锁
    问题汇总
    安装一台Centos7桌面版的跳板机
    Centos7二进制部署k8s-v1.20.2 ipvs版本(部署mysql、nacos)
    Centos7二进制部署k8s-v1.20.2 ipvs版本(Prometheus监控k8s)
    CV2 安装异常
    PostgreSQL VACUUM 没有效果(无法清理死元组)的原因
    SQL Server 进程运行状态解析
    mysql 几种启动和关闭mysql服务的方法和区别
    MySQL学习(九)小结(转载)
  • 原文地址:https://www.cnblogs.com/2015110615L/p/5567343.html
Copyright © 2020-2023  润新知