QA 由浅入深 Spring Framework 5.0(二)

Chapter 02 - Spring IoC 容器 Bean 花式注册与获取

花式一:通过Bean的类型获取Bean

配置文件beans.xml保持不变,在ContainerTest中新增方法

// 通过bean类型获取bean
@Test
public void testGetBeanByClass(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml");

    Person stark = context.getBean(Person.class);
    System.out.println(stark);
}
复制代码

image.png 配置文件beans.xml新增加一个bean标签的配置

<bean id="parker" class="com.citi.entity.Person">
    <property name="lastName" value="Parker" />
    <property name="age" value="18" />
    <property name="gender" value="male" />
    <property name="email" value="[email protected]" />
</bean>
复制代码

再次执行testGetBeanByClass(),会出现如下报错:
No qualifying bean of type 'com.citi.entity.Person' available: expected single matching bean but found 2: stark,parker 这是因为配置文件中有连个bean配置,获取的时候没有指定获取哪个bean,所以会报错,修改testGetBeanByClass()

// 通过bean类型获取bean
@Test
public void testGetBeanByClass(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml");

    Person stark = context.getBean("stark",Person.class);
    System.out.println(stark);
}
复制代码

输出结果如下

image.png

花式二:通过Constructor获取Bean

Person实体类中新增有参数构造方法

public Person(String lastName, Integer age, String gender, String email) {
    System.out.println("有参构造方法被调用");
    this.lastName = lastName;
    this.age = age;
    this.gender = gender;
    this.email = email;
}
复制代码

beans.xml中注册所有bean标签配置,并增加如下bean标签

<bean id="parker" class="com.citi.entity.Person">
    <constructor-arg name="lastName" value="Parker" />
    <constructor-arg name="age" value="18" />
    <constructor-arg name="gender" value="male" />
    <constructor-arg name="email" value="[email protected]" />
</bean>
复制代码

ContainerTest中获取bean

@Test
public void testGetBeanByConstructor(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml");

    Person parker = context.getBean("parker",Person.class);
    System.out.println(parker);
}
复制代码

执行testGetBeanByConstructor,控制台打印结果如下

image.png 通过Constructor获取Bean时bean.xml第二种注册方式

<bean id="parker" class="com.citi.entity.Person">
    <constructor-arg value="Parker" />
    <constructor-arg value="18" />
    <constructor-arg value="male" />
    <constructor-arg value="[email protected]" />
</bean>
复制代码

name标签可以省略,但是这个顺序一定要与构造方法中属性的顺序一致,也可以添加index属性来让value和类的属性保持一致,index从0开始

<bean id="parker" class="com.citi.entity.Person">
    <constructor-arg value="Parker" index="0"/>
    <constructor-arg value="18" index="1"/>
    <constructor-arg value="male" index="2"/>
    <constructor-arg value="[email protected]" index="3"/>
</bean>
复制代码

有参构造方法重载的情况下,可以使用type执行属性值的类型

花式三:为各种属性正确赋值

新增一个实体类Car,Book

public class Car {

   private String carName;
   private Integer price;
   private String color;
   // 此处省略getter/setter/toString方法
}
复制代码
public class Book {
    private String bookName;
    private String author;
    // 此处省略getter/setter/toString方法
}
复制代码

给Person实体类增加属性

public class Person {

    private String lastName;
    private Integer age;
    private String gender;
    private String email;

    // 为各种属性正确赋值
    private Car car;
    private List<Book> bookList;
    private Map<String,Object> map;
    private Properties properties;

    //无参构造方法
    public Person() {
        System.out.println("无参构造方法被调用");
    }

    public Person(String lastName, Integer age, String gender, String email) {
        System.out.println("有参构造方法被调用");
        this.lastName = lastName;
        this.age = age;
        this.gender = gender;
        this.email = email;
    }
    // 此处省略getter/setter/toString方法
}      
复制代码

resources目录下新建一个assgin_value.xml的bean配置文件

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

test目录下新建一个测试类AssignValueTest

public class AssignValueTest {
}
复制代码

不给属性赋值,基本数据类型就是默认值,引用数据类型是null 在assing_value.xml增加bean标签

<!--不赋值,默认为Null-->
<bean id="stark" class="com.citi.entity.Person">
</bean>
复制代码

AssignValueTest测试类中增加测试代码

@Test
public void testAssignNull(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Person stark = context.getBean(Person.class);
    System.out.println(stark);
}
复制代码

执行testAssignNull()测试方法,属性都为null

image.png 如果要为一个属性赋值null,以lastName为例,修改xml配置文件

<!--不赋值,默认为Null-->
<bean id="stark" class="com.citi.entity.Person">
    <property name="lastName">
       <null/>
    </property>
</bean>
复制代码

修改测试类,增加判断lastName是否为null

@Test
public void testAssignNull(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Person stark = context.getBean(Person.class);
    System.out.println(stark.getLastName() == null);
    System.out.println(stark);
}
复制代码

控制台输出为true,说明赋值null成功,对属性赋值null要使用标签 image.png

为应用的外部的值 在xml配置文件中增加Car实体类的配置,增加一个Person实体了的bean标签配置,给Car属性赋值

<bean id="JAGUAR" class="com.citi.entity.Car">
    <property name="carName" value="JAGUAR"></property>
    <property name="color" value="英国绿"></property>
    <property name="price" value="580000"></property>
</bean>

<!--引用外部的值-->
<bean id="stark01" class="com.citi.entity.Person">
    <property name="car" ref="JAGUAR"></property>
</bean>
复制代码

增加测试方法testRefOutsideValue()

@Test
public void testRefOutsideValue(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Car car = context.getBean(Car.class);
    Person stark01 = context.getBean("stark01",Person.class);
    System.out.println(stark01.getCar() == car);
    System.out.println(stark01);
    System.out.println(car);
}
复制代码

执行该方法,xml配置中使用了ref标签引用容器中存在的Car配置,所以容器中的Car和Person中的Car是同一个

image.png

引用内部的Value 增加Bean xml配置

<!--引用内部的值-->
<bean id="stark02" class="com.citi.entity.Person">
    <property name="car">
        <bean class="com.citi.entity.Car">
            <property name="carName" value="Model 3"></property>
            <property name="price" value="265652"></property>
            <property name="color" value="black"></property>
        </bean>
    </property>
</bean>
复制代码

增加测试方法

@Test
public void testInsideValue(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Car car = context.getBean(Car.class);
    Person stark02 = context.getBean("stark02",Person.class);
    System.out.println(stark02.getCar() == car);
    System.out.println(stark02);
    System.out.println(car);
}
复制代码

容器中的Car仍然为JAGUAR,而Person中定义了Car为Model 3,两者不是同一个,输出false,使用内部的Value,要在propert标签内再定一个Bean标签 image.png

List属性赋值 增加xml配置,定义一个Book的bean标签供bookList引用,列表引用要是用ref标签

<bean id="PythonBook" class="com.citi.entity.Book">
    <property name="bookName" value="Python21天跑路"></property>
    <property name="author" value="佚名"></property>
</bean>

<!--List属性赋值-->
<bean id="stark03" class="com.citi.entity.Person">
    <property name="bookList">
        <list>
            <bean id="book01" class="com.citi.entity.Book">
                <property name="bookName" value="Java从入门到跑路"></property>
                <property name="author" value="佚名"></property>
            </bean>
            <!--引用外部-->
            <ref bean="PythonBook"></ref>
        </list>
    </property>
</bean>
复制代码

增加测试方法

@Test
public void testAssign2List(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Book pythonBook = context.getBean(Book.class);
    Person stark03 = context.getBean("stark03",Person.class);
    List<Book> bookList = stark03.getBookList();
    for (Book book : bookList) {
        System.out.println(book);
    }
    System.out.println(stark03);
    System.out.println(pythonBook);
}
复制代码

执行测试,控制台输出

image.png 尝试获取Person bookList属性中的另一本书,测试方法中增加代码

Book book01 = context.getBean("book01",Book.class);
System.out.println(book01);
复制代码

执行测试方法报错,说明bean标签内部的bean是无法直接通过容器获取的,只有最外层定义的bean可以被容器直接获取

image.png

Map属性赋值 增加xml配置,给map属性赋值需要使用entry标签,通过key和value标签定义map结构中的key和map,也可用过key-ref和value-ref引用外部的key和value,map中可以在通过entry再嵌套map

<!--Map属性赋值-->
<bean id="stark04" class="com.citi.entity.Person">
    <property name="map">
        <map>
            <!--key,value,一个entry代表一个简直对-->
            <entry key="name" value="Peter"></entry>
            <entry key="book" value-ref="PythonBook"></entry>
            <entry key="car">
                <bean class="com.citi.entity.Car">
                    <property name="carName" value="Model Y"></property>
                </bean>
            </entry>
            <entry key="mapKey">
                <map>
                    <entry key="key01" value="value01"></entry>
                </map>
            </entry>
        </map>
    </property>
</bean>
复制代码

新增测试方法

@Test
public void testAssign2Map(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Person stark04 = context.getBean("stark04",Person.class);
    Map<String, Object> map = stark04.getMap();
    for (String key : map.keySet()) {
        System.out.println("key:" + key);
    }

    for (Object value : map.values()) {
        System.out.println("value:" + value);
    }
    System.out.println(stark04);
}
复制代码

执行测试方法,控制台成功打印出map数据结构 image.png

Properties属性赋值 增加xml配置,为Properties属性赋值需要使用props标签,prop标签表示properties配置文件中的每一行key-value

<!--Properties属性赋值-->
<bean id="stark05" class="com.citi.entity.Person">
    <property name="properties">
        <props>
            <prop key="username">root</prop>
            <prop key="password">12345</prop>
        </props>
    </property>
</bean>
复制代码

增加测试方法

@Test
public void testAssign2Properties(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Person stark05 = context.getBean("stark05",Person.class);

    System.out.println(stark05);
}
复制代码

执行测试方法,控制台成功输出properties内容

image.png

namespace名称空间 名称空间是用来防止标签重复的,通过增加前缀(名称空间)区分名字相同的标签,如

<car>
    <name>F-TYPE</name>
    <price>580000</price>
    <owner>
        <name>Stark</name>
        <age>40</age>
    </owner>
</car>
复制代码

car下面的name和owner下面的name出现了重复,此时需要通过增加前缀即命名空间来区分,将xml格式数据修改为如下形式

<car>
    <c:name>F-TYPE</c:name>
    <price>580000</price>
    <owner>
        <o:name>Stark</o:name>
        <age>40</age>
    </owner>
</car>
复制代码

Spring配置文件中本身就有默认的命名空间,即表头中的

xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd"
复制代码

它表示默认的命名空间,不需要添加前缀

使用自定义的名称空间需要导入,放在表头

xmlns:p = "http://www.springframework.org/schema/p"
复制代码

使用时会有很多提示

image.png 在xml中增加使用前缀的配置

<bean id="stark06" class="com.citi.entity.Person" p:lastName="stark06">
    
</bean>
复制代码

增加测试方法

@Test
public void testAssignByNamespace(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Person stark06 = context.getBean("stark06",Person.class);

    System.out.println(stark06);
}
复制代码

控制台成功打印出lastName为stark06 image.png 利用util名称空间为Map属性赋值

首先导入名称空间,修改表头为

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p = "http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.3.xsd">
</beans>        
复制代码

接着利用util名称空间创建一个map,id为mapSample,供id为stark07的bean标签引用

<util:map id="mapSample">
    <!--key,value,一个entry代表一个简直对-->
    <entry key="name" value="Thor"></entry>
    <entry key="book" value-ref="PythonBook"></entry>
    <entry key="car">
        <bean class="com.citi.entity.Car">
            <property name="carName" value="Model Y"></property>
        </bean>
    </entry>
</util:map>

<!--引用util名称空间为Map属性赋值-->
<bean id="stark07" class="com.citi.entity.Person">
    <property name="map" ref="mapSample">
    </property>
</bean>
复制代码

增加测试方法

@Test
public void testAssignMapByUtilNamespace(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Person stark07 = context.getBean("stark07",Person.class);

    System.out.println(stark07);
}
复制代码

控制台日志显示,已成功赋值 image.png

利用util名称空间为List属性赋值 xml中增加util:list配置,并增加一个bean标签引用util list标签

<util:list id="listSample">
    <bean id="book02" class="com.citi.entity.Book">
        <property name="bookName" value="Java从入门到跑路"></property>
        <property name="author" value="佚名"></property>
    </bean>
    <!--引用外部-->
    <ref bean="PythonBook"></ref>
</util:list>

<!--引用util名称空间为List属性赋值-->
<bean id="stark08" class="com.citi.entity.Person">
    <property name="bookList" ref="listSample">
    </property>
</bean>
复制代码

增加测试方法

@Test
public void testAssignListByUtilNamespace(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Person stark08 = context.getBean("stark08",Person.class);

    System.out.println(stark08);
}
复制代码

控制台显示list已被成功赋值

image.png 级联属性赋值 Person有Car属性,Car属性又有carName属性,从Person端来看,carName就是级联属性,即属性的属性 增加xml配置

<!--级联属性赋值-->
<bean id="stark09" class="com.citi.entity.Person">
    <property name="car" ref="JAGUAR"></property>
    <!--修改引用的Car的价格-->
    <property name="car.price" value="400000"></property>
</bean>
复制代码

增加测试代码

@Test
public void testAssignCascadeProperty(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:assign_value.xml");

    Person stark09 = context.getBean("stark09",Person.class);
    Car car = context.getBean(Car.class);

    System.out.println(stark09);
    System.out.println(car);
}
复制代码

控制台日志显示价格已经被成功修改,由原来的580000变为400000,并且容器中的原来的car的价格也被修改 image.png

猜你喜欢

转载自juejin.im/post/7048497631948963876
QA