zoukankan      html  css  js  c++  java
  • Spring(四) Spring容器(2)

    6.Spring容器中的Bean

    6.1 Bean定义和Bean别名

    对于开发者来说,开发者使用Spring做2件事:(1)开发Bean (2)配置Bean。

    <beans.../>标签的属性:

    default-lazy-init

    default-merge

    default-autowire

    default-autowire-condidates

    default-init-method

    default-destroy-method

    <beans.../>的定义对每一个bean都有效,<bean.../>对单个bean有效,如果这两者冲突,<bean.../>会覆盖<beans.../>的定义。<bean.../>也可以采用name属性,用于指定Bean的别名,通过访问Bean别名也可以访问Bean实例。

    指定别名有2种方式:

    (1)通过name属性指定:可以指定多个,使用逗号、冒号或者空格分隔多个别名;

    (2)通过alias元素指定:一个元素是name,一个元素是alias。并且这个标签,不是在<bean.../>里,而是跟<bean.../>并列的。

    新建一个测试类,AliasTest.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package com.pmpa.ch03;
     
    public class AliasTest {
         
        private String name;
        private int age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Override
        public String toString() {
            return "AliasTest [name=" + name + ", age=" + age + "]";
        }
         
         
     
    }

    配置文件:

    1
    2
    3
    4
    5
    <bean id="aliastest" class="com.pmpa.ch03.AliasTest" name="#221,iuffs,googke">
        <property name="name" value="honda" ></property>
        <property name="age" value="13" ></property>
    </bean>
    <alias name="googke" alias="Sun"></alias>

    测试类BeanTest.java

    1
    2
    AliasTest at = ctx.getBean("Sun", AliasTest.class);
    System.out.println(at);

    运行结果:

    1
    AliasTest [name=honda, age=13]

    可以看到,通过别名“Sun”,Spring也找到了这个Bean,并正常输出。

    6.2 容器中Bean的作用域

    Bean包含5种作用域:

    singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例

    prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例

    request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效

    session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效

    globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效

    6.2.1 singleton和prototype作用域测试实例:

    如果不指定Bean的作用域,默认是singleton。prototype作用域的Bean的创建、销毁的系统代价比较大,应尽量避免使用。Spring配置文件的<bean.../>标签通过scope属性来配置bean的作用域。

    ScopeTest.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.pmpa.ch03;
     
    public class ScopeTest {
        private String idNo;
     
        public String getIdNo() {
            return idNo;
        }
     
        public void setIdNo(String idNo) {
            this.idNo = idNo;
        }
     
        @Override
        public String toString() {
            return "ScopeTest [idNo=" + idNo + "]";
        }
         
    }

    配置文件 beans03.xml

    1
    2
    3
    4
    5
    6
    <bean id="scope1" class="com.pmpa.ch03.ScopeTest" scope="prototype">
        <property name="idNo" value="152301ABC"></property>
    </bean>
    <bean id="scope2" class="com.pmpa.ch03.ScopeTest" scope="singleton">
        <property name="idNo" value="110332CDE"></property>
    </bean>

    测试类BeanTest.java:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //prototype作用域Bean的测试:
    ScopeTest st_prototype_1 = ctx.getBean("scope1", ScopeTest.class);
    ScopeTest st_prototype_2 = ctx.getBean("scope1", ScopeTest.class);
    System.out.println("prototype类型的对象1和2是否是同一对象:" + (st_prototype_1 == st_prototype_2));
             
    //sigleton作用域Bean的测试:
    ScopeTest st_singleton_1 = ctx.getBean("scope2", ScopeTest.class);
    ScopeTest st_singleton_2 = ctx.getBean("scope2", ScopeTest.class);
    System.out.println("sigleton类型的对象1和2是否是同一对象:" + (st_singleton_1 == st_singleton_2));

    运行结果:

    1
    2
    prototype类型的对象1和2是否是同一对象:false
    sigleton类型的对象1和2是否是同一对象:true

    对于singleton作用域的Bean,每次请求该id的bean,都将返回同一个共享实例

    6.2.2 request、session、globalsession作用域测试实例(待补充):

    6.3 配置依赖

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

    ApplicationContext可以在容器初始化阶段检验出配置错误

    Spring允许通过如下元素为setter方法、构造器参数指定参数值。

    value

    ref

    bean

    list、set、map、props

    以上4种情况分别代表Bean类的4种类型的成员变量

    6.3.1 value:

    value元素用于指定基本类型及其包装、字符串类型的参数。前边讲述的内容中,包含很多value赋值的情形,这里不再赘述。

    6.3.2 ref:

    如果需要为Bean设置的属性值是容器中的另一个Bean实例,则应该使用<ref.../>元素(其实,常用的是ref属性,前边事例都是这样。)这种术语也叫作“注入合作者Bean”。ref实例在前边例子中也很多,不再赘述。

    自动装配注入合作者Bean

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

    自动装配通过<beans.../>的default-autowrite属性指定,也可以通过<bean.../>元素的autowire属性指定。

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

    auto-wire属性的可用值:

    no:不使用自动装配,这是默认配置。

    byName:根据setter方法名进行自动装配。

    byType:根据setter方法的形参类型来自动装配。如果找到多个和形参类型匹配的Bean,则抛出异常。

    constructor:与byType类似,区别是用于自动匹配构造器的参数。

    autodetect:Spring容器根据Bean内部结构,自行决定使用constructor或byType策略。

    AutoWireTest.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package com.pmpa.ch03;
     
    public class AutoWireTest {
         
        private String uName;
        private Car car;
        private House house;
        public String getuName() {
            return uName;
        }
        public void setuName(String uName) {
            this.uName = uName;
        }
        public Car getCar() {
            return car;
        }
        public void setCar(Car car) {
            this.car = car;
        }
        public House getHouse() {
            return house;
        }
        public void setHouse(House house) {
            this.house = house;
        }
         
        public void testAutoWire()
        {
            System.out.println("begin testing autowire " + uName);
            car.carTest();
            house.testHouse();
        }
         
    }

    在外部,创建2个类,分别是Car和House,这2个类很简单。

    配置文件beans03.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <bean id="benz" class="com.pmpa.ch03.Car">
        <property name="brand" value="benz"></property>
        <property name="speed" value="270"></property>
    </bean>
     
    <bean id="biguiyuan" class="com.pmpa.ch03.House">
        <property name="address" value="GuoMao"></property>
        <property name="price" value="78900000"></property>
    </bean>
     
    <bean id="autowiretest" class="com.pmpa.ch03.AutoWireTest" autowire="byType">
        <property name="uName" value="自动装配测试1"></property>
    </bean>

    这时候,创建的2个bean实例benz和biguiyuan的类型分别是Car和House,所以如果autowiretest实例的autowire属性设置为byType,则可以自动装配这2个属性。

    测试类BeanTest.java

    1
    2
    AutoWireTest awt = ctx.getBean("autowiretest", AutoWireTest.class);
    awt.testAutoWire();

    运行结果:

    1
    2
    3
    begin testing autowire 自动装配测试1
    The car benz can reach the speed of 270.0
    The house GuoMao can reach the price of 7.89E7

    通过byType自动装配成功。如果想通过byName完成自动装配,则需要修改配置文件,修改Car和House类型的2个bean实例的id(修改成什么Id,需要根据类AutoWireTest的2个setter方法来确定,应该是car和house),修改如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <bean id="car" class="com.pmpa.ch03.Car">
        <property name="brand" value="benz"></property>
        <property name="speed" value="270"></property>
    </bean>
     
    <bean id="house" class="com.pmpa.ch03.House">
        <property name="address" value="GuoMao"></property>
        <property name="price" value="78900000"></property>
    </bean>
     
    <bean id="autowiretest" class="com.pmpa.ch03.AutoWireTest" autowire="byName">
        <property name="uName" value="自动装配测试1"></property>
    </bean>

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

    6.3.3 注入嵌套Bean:

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

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

    注意如下的配置文件:

    1
    2
    3
    4
    5
    <bean id="chinese02" class="com.pmpa.ch02.Chinese">
        <property name = "axe" >
            <bean class="com.pmpa.ch02.SteelAxe"></bean>
        </property>
    </bean>

    使用嵌套Bean与使用ref引用容器中另一个Bean在本质上是一样的。 之前的写法是<property name="..." ref="..."/>,现在改成<property name="..."><bean class="..."/></property>。注意这种写法中,不能指定bean id属性。

    6.3.4 注入集合值:

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

    下面定义一个包含集合属性的java类,Student.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    package com.pmpa.ch03;
     
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    import java.util.Set;
     
    public class Student {
         
        private String name;
        private List<String> schools;
        private Map scores;
        private Map<String,Car> carPhases;
        private Properties examinations;
        private Set infos;
        private String[] careers;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        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, Car> getCarPhases() {
            return carPhases;
        }
        public void setCarPhases(Map<String, Car> carPhases) {
            this.carPhases = carPhases;
        }
        public Properties getExaminations() {
            return examinations;
        }
        public void setExaminations(Properties examinations) {
            this.examinations = examinations;
        }
        public Set getInfos() {
            return infos;
        }
        public void setInfos(Set infos) {
            this.infos = infos;
        }
        public String[] getCareers() {
            return careers;
        }
        public void setCareers(String[] careers) {
            this.careers = careers;
        }
        @Override
        public String toString() {
            return "Student [name=" + name + ", schools=" + schools + ", scores=" + scores + ", carPhases=" + carPhases
                    ", examinations=" + examinations + ", infos=" + infos + ", careers=" + Arrays.toString(careers) + "]";
        }
         
    }

    分别为<property.../>元素增加<list.../>、<set.../>、<map.../>、<props.../>子元素来配置这些集合类型的参数值。

    配置文件定义bean03.xml:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    <bean id="toyota" class="com.pmpa.ch03.Car">
        <property name="brand" value="toyota"></property>
        <property name="speed" value="180"></property>
    </bean>
    <bean id="bmw" class="com.pmpa.ch03.Car">
        <property name="brand" value="bmw"></property>
        <property name="speed" value="270"></property>
    </bean>
    <bean id="bently" class="com.pmpa.ch03.Car">
        <property name="brand" value="bently"></property>
        <property name="speed" value="460"></property>
    </bean>
     
    <bean id="student" class="com.pmpa.ch03.Student">
        <property name="name" value="Tom White"></property>
        <!-- 配置List集合类型 -->
        <property name="schools">
            <list>
                <!-- 下面每个value标签,定义一个list的元素,根据list泛型的类型,也可以使用ref、bean等等标签 -->
                <value>衡水一中</value>
                <value>北京工业大学</value>
                <value>斯坦福大学</value>
            </list>
        </property>
        <!-- 配置Map集合类型 -->
        <property name="scores">
            <map>
                <!-- 下面每个entry标签,定义一个map的元素,也就是key-value对 -->
                <entry key="数学" value="78"></entry>
                <entry key="语文" value="132"></entry>
                <entry key="英语" value="104"></entry>
            </map>
        </property>  
        <!-- 配置Map集合类型,Map<String,Car> -->
        <property name="carPhases">
            <map>
                <!-- 下面每个entry标签,定义一个map的元素,也就是key-value对 -->
                <entry key="打工阶段" value-ref="toyota"></entry>
                <entry key="创业阶段" value-ref="bmw"></entry>
                <entry key="成功阶段" value-ref="bently"></entry>
            </map>
        </property>  
        <!-- 配置Properties集合属性 -->
        <property name="examinations">
            <props>
                <!-- 每个prop元素都配置一个属性项,其中key指定属性名 -->
                <prop key="血常规">正常</prop>
                <prop key="尿常规">脂肪肝</prop>
            </props>
        </property>  
        <!-- 配置Set集合属性 -->
        <property name="infos">
            <set>
                <!-- 每个value、ref、bean都配置一个Set元素 -->
                <value>简单的字符串赋值</value>
                <bean class="com.pmpa.ch03.House"></bean>
                <ref bean="car"></ref>
                <!-- Set里可以继续配置一个List -->
                <list>
                    <value>Set中的List</value>
                    <set><value type="java.lang.String">List中的Set</value></set>
                </list>
            </set>
        </property>
        <!-- 配置数组集合类型 -->
        <property name="careers">
            <list>
                <!-- 每个value标签是一个数组元素 -->
                <value>IBM</value>
                <value>Google</value>
                <value>Oracle</value>
            </list>
        </property>
    </bean>

    测试类BeanTest.java

    1
    2
    3
    System.out.println("-----------------------测试集合注入------------------------");
    Student student = ctx.getBean("student", Student.class);
    System.out.println(student);

    运行结果:

    1
    2
    -----------------------测试集合注入------------------------
    Student [name=Tom White, schools=[衡水一中, 北京工业大学, 斯坦福大学], scores={数学=78, 语文=132, 英语=104}, carPhases={打工阶段=Car [brand=toyota, speed=180.0], 创业阶段=Car [brand=bmw, speed=270.0], 成功阶段=Car [brand=bently, speed=460.0]}, examinations={血常规=正常, 尿常规=脂肪肝}, infos=[简单的字符串赋值, com.pmpa.ch03.House@6d7b4f4c, Car [brand=benz, speed=270.0], [Set中的List, [List中的Set]]], careers=[IBM, Google, Oracle]]

    说明:

    1. Spring对List元素和数组的处理是一致的,都使用<list.../>元素配置。

    2. <list.../>、<set.../>、<map.../>、<props.../>又可以接受value;ref;bean;list、set、map、props元素。

    3. Properties类型,其key和value只能是字符串。

    4. <entry.../>元素支持4个属性:key、key-ref(key为容器中的其他bean实例)、value、value-ref(value为容器中的其他bean实例,上边例子中有说明)。

    5. Spring容器支持集合的合并,子Bean中的结合属性值可以从其父Bean的集合属性继承和覆盖而来。

    6.3.4  组合属性:

    使用car.brand 等类似格式为对象成员变量的属性赋值。为Bean的组合属性设置参数时,除最后一个属性外,其他属性值都不允许为null。

    CombinationTest.java:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package com.pmpa.ch03;
    public class CombinationTest {
     private String name;
     private Car car = new Car();
     private House house = new House();
     public String getName() {
      return name;
     }
     public void setName(String name) {
      this.name = name;
     }
     public Car getCar() {
      return car;
     }
     public void setCar(Car car) {
      this.car = car;
     }
     public House getHouse() {
      return house;
     }
     public void setHouse(House house) {
      this.house = house;
     }
     @Override
     public String toString() {
      return "CombinationTest [name=" + name + ", car=" + car + ", house=" + house + "]";
     }
    }

    配置文件:

    1
    2
    3
    4
    5
    6
    <bean id="combinationtest" class="com.pmpa.ch03.CombinationTest">
     <property name="name" value="combination"/>
     <property name="car.brand" value="honda"></property>
     <property name="car.speed" value="193" ></property>
     <property name="house.address" value="XiDan" ></property>
    </bean>

    运行结果:

    1
    2
    -----------------------测试组合属性------------------------
    CombinationTest [name=combination, car=Car [brand=honda, speed=193.0], house=House [address=XiDan, price=0.0]]

    使用组合属性指定参数值时,除了最后一个属性外,其他属性都不能为Null,否则将引发NullPointerException异常,如果感觉自学困难的话,可以去参加java培训来提高。例如,上面配置文件为car.brand指定参数值,则CombinationTest的car就一定不能为null,所以该类在设置private属性时,要直接new一个对象。是这样:private Car car = new Car();而不能是这样:private Car car;
    这种赋值方法,底层spring执行时,需要先调用getter方法,再调用setter方法,

    1
    combinationtest.getCar().setBrand("honda");

    也就是说组合属性只有最后一个属性才调用setter方法,其余都调用getter方法——这也是为什么前面属性都不能为null的缘由。

  • 相关阅读:
    vue项目webpack配置terser-webpack-plugin 去掉项目中多余的debugger
    difference between count(1) and count(*)
    为什么PostgreSQL WAL归档很慢
    mysql_reset_connection()
    Oracle使用audit跟踪登录失败的连接信息
    .NET Standard 版本
    ASP.NET Web API版本
    我是如何用go-zero 实现一个中台系统的
    JAVA中文件写入的6种方法
    MySql 常用语句
  • 原文地址:https://www.cnblogs.com/plan123/p/5732734.html
Copyright © 2011-2022 走看看