Spring Bean的配置

二、Bean的配置

配置Bean就是告诉Spring的IOC容器将要去管理的对象

1. 配置bean的方式

1.1 传统的XML配置方式

Person.java

public class Person {
    private String name;
    private int age;
    private double money;

    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;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", money=" + money +
                '}';
    }
}

applicationContext.xml

<?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
    http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
    bean标签:让spring创建一个对象并放置在IOC容器内
    属性:
        id : 标签该bean对象的唯一标识符,bean的名称,不能重复的
        class : 需要创建对象的类型的全限定名,spring通过反射机制创建该类的对象(要求:该类必须拥有无参构造方法)
    -->
    <bean id="person1" class="com.newcapec.bean.Person"/>
    <bean id="person2" class="com.newcapec.bean.Person">
</beans>

属性解析:

  • id : bean的名称
    在IOC容器中必须是唯一的
    若id没有指定,Spring自动将类全限定性类名作为bean的名字
  • class : java类的全限定名称

BeanTest.java

public class BeanTest {
    @Test
    public void test(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        Person p1 = (Person) ac.getBean("person1");
        System.out.println(p1);

        Person p2 = (Person) ac.getBean("person2");
        System.out.println(p2);
        System.out.println(p1 == p2);
    }
}

1.2 基于Java注解的配置

请关注后面的章节

1.3 基于类的Java Config

请关注后面的章节

2. 实例化Bean的方式

2.1 通过构造方法实例化Bean

Spring IoC容器即能使用默认空构造方法也能使用有参数构造,通过反射机制来实例化Bean对象。

2.2 通过工厂实例化Bean

请关注后面的章节

2.3 FactoryBean实例化Bean

请关注后面的章节

3. Spring容器

Spring提供了两种类型的IOC容器实现

3.1 BeanFactory

BeanFactory是Spring框架的基础设施,面向Spring本身,底层父接口

3.2 ApplicationContext

提供了更多的高级特性,它主要面向于Spring框架的使用者,是BeanFactory的子接口

3.3 ApplicationContext的主要实现类

  • ClassPathXmlApplicationContext:从类路径上加载配置文件
  • FileSystemXmlApplicationContext:从文件系统中加载配置文件
  • WebApplicationContext:专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作

3.4 从容器中获取Bean

  • getBean(String name)方法,通过Bean的id从容器中获取Bean对象
  • getBean(Class requiredType)方法,通过Bean的Class类型从容器中获取Bean对象
  • getBean(String name, Class requiredType)方法,通过Bean的id和Class类型从容器中获取Bean对象

注意:当IOC容器中存放有多个此类型的对象时,不能通过Class类型来获取Bean对象,推荐使用bean的id来获取

BeanTest.java

public class BeanTest {
    @Test
    public void testGetBean(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //1.参数为字符串类型
        Person p1 = (Person) ac.getBean("person1");
        System.out.println(p1);

        //2.参数为Class类型
       Person person = ac.getBean(Person.class);
       System.out.println(person);

        //3.参数为字符串类型+Class类型
        Person p2 = ac.getBean("person2", Person.class);
        System.out.println(p2);
        System.out.println(p1 == p2);
    }
}

4. 依赖注入

4.1 基于属性注入

  • 通过setter方法注入Bean的属性值或依赖的对象
  • 属性注入使用<property>标签,使用name属性指定Bean的属性名称,value属性或<value>子标签指定属性值
  • 属性注入是实际应用中最常用的注入方式

applicationContext.xml

<?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
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 通过set方法注入属性值 -->
    <bean id="person" class="com.newcapec.bean.Person">
        <!--
        property标签:表示通过属性的set方法为属性赋值,也叫做依赖注入
        属性:
        name : 对象中的属性名称
        value : 属性值
        -->
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
        <property name="money">
            <value>3600.5</value>
        </property>
    </bean>
</beans>
public class DiTest {
    @Test
    public void testSet(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = ac.getBean("person", Person.class);
        System.out.println(person);
    }
}

4.2 基于构造方法注入

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

Car.java

public class Car {
    private String name;
    private String type;
    private double price;
    private int doors;

    public Car(String name, String type, double price, int doors) {
        this.name = name;
        this.type = type;
        this.price = price;
        this.doors = doors;
    }
    public Car(String name, String type, int doors) {
        this.name = name;
        this.type = type;
        this.doors = doors;
    }
    public Car(String name, String type, double price) {
        this.name = name;
        this.type = type;
        this.price = price;
    }
    public Car(String n, String t) {
        this.name = n;
        this.type = t;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", type='" + type + '\'' +
                ", price=" + price +
                ", doors=" + doors +
                '}';
    }
}

applicationContext.xml

<?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
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 通过构造方法注入属性值 -->
    <bean id="car1" class="com.newcapec.bean.Car">
        <!--
        constructor-arg : 表示创建该类型的对象时,使用的构造方法的参数
        属性:
            value : 构造方法的参数值
            index : 构造方法参数的索引
            type : 构造方法参数的类型
            name : 构造方法参数的名称
        -->
        <constructor-arg value="宝马"/>
        <constructor-arg value="轿车"/>
        <constructor-arg value="360000"/>
        <constructor-arg value="4"/>
    </bean>
</beans>

  • 按索引匹配构造方法参数
<bean id="car2" class="com.newcapec.bean.Car">
    <constructor-arg value="越野" index="1"/>
    <constructor-arg value="奔驰" index="0"/>
    <constructor-arg value="4" index="3"/>
    <constructor-arg value="560000" index="2"/>
</bean>
  • 按类型匹配构造方法参数
<bean id="car3" class="com.newcapec.bean.Car">
    <constructor-arg value="大众" type="java.lang.String"/>
    <constructor-arg value="商务车" type="java.lang.String"/>
    <constructor-arg value="290000" type="double"/>
</bean>
  • 按参数名称匹配构造方法参数
<bean id="car4" class="com.newcapec.bean.Car">
    <constructor-arg value="电动车" name="t"/>
    <constructor-arg value="特斯拉" name="n"/>
</bean>

如果该类中有多个构造方法,通过index、type或name对构造方法进行精确选取

public class DiTest {
    @Test
    public void testConstructor(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        Car car1 = ac.getBean("car1", Car.class);
        System.out.println(car1);

        Car car2 = ac.getBean("car2", Car.class);
        System.out.println(car2);

        Car car3 = ac.getBean("car3", Car.class);
        System.out.println(car3);

        Car car4 = ac.getBean("car4", Car.class);
        System.out.println(car4);
    }
}

5. 注入属性值

5.1 字面值

  • 可用字符串表示的值,可以通过<value>标签或value属性进行注入
  • 基本数据类型及包装类类型,String等类型都可以采取字面值注入的方式。Spring会将字符串自动转换为相应的数据类型
  • 如果字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来
<bean id="p" class="com.newcapec.bean.Person">
    <property name="name">
        <value><![CDATA[1<2]]></value>
    </property>
</bean>

5.2 引用其他的bean

组成应用程序的Bean经常需要相互协作以完成应用程序的功能。要使Bean能够相互访问,就必须在Bean配置文件中指定对Bean的引用

Customer.java

public class Customer {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

Order.java

public class Order {
    private String orderNum;
    private double price;
    /**
     * 自定义类型的属性
     */
    private Customer customer;

    public String getOrderNum() {
        return orderNum;
    }

    public void setOrderNum(String orderNum) {
        this.orderNum = orderNum;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderNum='" + orderNum + '\'' +
                ", price=" + price +
                ", customer=" + customer +
                '}';
    }
}

<property>标签中的ref属性

<bean id="customer1" class="com.newcapec.bean.Customer">
    <property name="username" value="tom"/>
    <property name="password" value="123456"/>
</bean>
<bean id="order1" class="com.newcapec.bean.Order">
    <property name="orderNum" value="20190624001"/>
    <property name="price" value="1299"/>
    <!-- 通过ref属性来注入属性值:其中ref属性的值为其他bean的name-->
    <property name="customer" ref="customer1"/>
</bean>

<property>标签中的<ref>子标签

<bean id="customer2" class="com.newcapec.bean.Customer">
    <property name="username" value="jerry"/>
    <property name="password" value="654321"/>
</bean>
<bean id="order2" class="com.newcapec.bean.Order">
    <property name="orderNum" value="20190624002"/>
    <property name="price" value="3650"/>
    <!-- 通过ref子标签来注入其他的bean对象-->
    <property name="customer">
        <ref bean="customer2"/>
    </property>
</bean>

<property>标签中的<bean>子标签,定义一个内部Bean

<bean id="order3" class="com.newcapec.bean.Order">
    <property name="orderNum" value="20190624003"/>
    <property name="price" value="99.9"/>
    <property name="customer">
        <!-- 内置bean-->
        <bean class="com.newcapec.bean.Customer">
            <property name="username" value="chris"/>
            <property name="password" value="111111"/>
        </bean>
    </property>
</bean>

注意:内部Bean不能使用在任何其他地方

@Test
public void testRef(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

    Order order1 = ac.getBean("order1", Order.class);
    System.out.println(order1);

    Order order2 = ac.getBean("order2", Order.class);
    System.out.println(order2);

    Order order3 = ac.getBean("order3", Order.class);
    System.out.println(order3);
}

5.3 集合属性

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

Course.java

public class Course {
    private int id;
    private String cname;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }

    @Override
    public String toString() {
        return "Course{" +
                "id=" + id +
                ", cname='" + cname + '\'' +
                '}';
    }
}

Student.java

public class Student {
    private int id;
    private String name;
    private List<Course> courseList;
    private Integer[] ids;
    private Set<String> stringSet;
    private Map<String, Course> courseMap;
    private Properties props;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public List<Course> getCourseList() {
        return courseList;
    }

    public void setCourseList(List<Course> courseList) {
        this.courseList = courseList;
    }

    public Integer[] getIds() {
        return ids;
    }

    public void setIds(Integer[] ids) {
        this.ids = ids;
    }

    public Set<String> getStringSet() {
        return stringSet;
    }

    public void setStringSet(Set<String> stringSet) {
        this.stringSet = stringSet;
    }

    public Map<String, Course> getCourseMap() {
        return courseMap;
    }

    public void setCourseMap(Map<String, Course> courseMap) {
        this.courseMap = courseMap;
    }

    public Properties getProps() {
        return props;
    }

    public void setProps(Properties props) {
        this.props = props;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", courseList=" + courseList +
                ", ids=" + Arrays.toString(ids) +
                ", stringSet=" + stringSet +
                ", courseMap=" + courseMap +
                ", props=" + props +
                '}';
    }
}
5.3.1 List集合

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

<bean id="course1" class="com.newcapec.bean.Course">
    <property name="id" value="10"/>
    <property name="cname" value="Java语言"/>
</bean>
<bean id="course2" class="com.newcapec.bean.Course">
    <property name="id" value="20"/>
    <property name="cname" value="Oracle数据库"/>
</bean>
<bean id="course3" class="com.newcapec.bean.Course">
    <property name="id" value="30"/>
    <property name="cname" value="Spring框架"/>
</bean>

<bean id="student" class="com.newcapec.bean.Student">
    <property name="id" value="1001"/>
    <property name="name" value="小明"/>
    <!-- list集合:list子标签,表示配置的属性类型为java.util.List-->
    <property name="courseList">
        <list>
            <ref bean="course1"/>
            <ref bean="course2"/>
            <ref bean="course3"/>
        </list>
    </property>
</bean>
@Test
public void testCollection(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = ac.getBean("student",Student.class);
    System.out.println(student);
}
5.3.2 Object[]数组

配置数组类型的属性,使用<array>标签

<bean id="student" class="com.newcapec.bean.Student">
    <property name="id" value="1001"/>
    <property name="name" value="小明"/>
    <!-- 数组:array子标签-->
    <property name="ids">
        <array>
            <value>10</value>
            <value>20</value>
            <value>30</value>
            <value>40</value>
        </array>
    </property>
</bean>
5.3.3 Set集合

配置java.util.Set类型的属性,使用<set>标签

<bean id="student" class="com.newcapec.bean.Student">
    <property name="id" value="1001"/>
    <property name="name" value="小明"/>
    <!-- set集合:set子标签-->
    <property name="stringSet">
        <set>
            <value>hello</value>
            <value>goodbye</value>
            <value>how are you</value>
        </set>
    </property>
</bean>
5.3.4 Map集合

java.util.Map通过<map>标签定义,<map>标签里可以使用多个<entry>作为子标签。每个<entry>中包含一个键和一个值。简单类型使用key和value属性来定义,Bean引用通过key-ref和value-ref属性定义

<bean id="student" class="com.newcapec.bean.Student">
    <property name="id" value="1001"/>
    <property name="name" value="小明"/>
    <!-- map集合:map子标签-->
    <property name="courseMap">
        <map>
            <!--
            entry标签:表示Map集合中的一组键值对
            key : 表示Map集合中的键为字面值
            key-ref : 表示Map集合中的键为自定义类型
            value : 表示Map集合中的值为字面值
            value-ref : 表示Map集合中的值为自定义类型
            -->
            <entry key="one" value-ref="course3"/>
            <entry key="two" value-ref="course1"/>
        </map>
    </property>
</bean>
5.3.5 Properties

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

<bean id="student" class="com.newcapec.bean.Student">
    <property name="id" value="1001"/>
    <property name="name" value="小明"/>
    <!-- Properties类型的属性 -->
    <property name="props">
        <props>
            <!-- prop标签,表示Properties集合中的一个键值对,key属性对应的键,prop开始标签与结束标签之后的区域填写值-->
            <prop key="hello">你好</prop>
            <prop key="goodbye">再见</prop>
        </props>
    </property>
</bean>
5.3.6 单例集合

单例集合: 供多个Bean使用。使用<util>标签,将集合定义在Bean的外部

注意:需要导入util命名空间和标签规范

xmlns:util="http://www.springframework.org/schema/util"

http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
<!-- 外部集合:多个集合属性可同时引用 -->
<util:list id="mylist">
    <ref bean="car1"/>
    <ref bean="car2"/>
</util:list>

5.4 p命名空间

  • 为了简化XML文件的配置,越来越多的XML文件采用属性而非子标签配置信息
  • 从Spring2.5版本开始引入了一个新的p命名空间,可以通过<bean>标签属性的方式配置Bean的属性
  • 使用p命名空间后,基于XML的配置方式将进一步简化
xmlns:p="http://www.springframework.org/schema/p"

Dept.java

public class Dept {
    private int deptno;
    private String dname;
    private String loc;

    public int getDeptno() {
        return deptno;
    }

    public void setDeptno(int deptno) {
        this.deptno = deptno;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptno=" + deptno +
                ", dname='" + dname + '\'' +
                ", loc='" + loc + '\'' +
                '}';
    }
}

Emp.java

public class Emp {
    private int empno;
    private String ename;
    /**
     * 关系属性
     */
    private Dept dept;

    public int getEmpno() {
        return empno;
    }

    public void setEmpno(int empno) {
        this.empno = empno;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "empno=" + empno +
                ", ename='" + ename + '\'' +
                ", dept=" + dept +
                '}';
    }
}

applicationContext.xml

<!-- 通过p命名空间的方式,来简化依赖注入 -->
<bean id="dept" class="com.newcapec.bean.Dept" p:deptno="10" p:dname="研发部" p:loc="郑州"/>
<bean id="emp" class="com.newcapec.bean.Emp" p:empno="8000" p:ename="张三" p:dept-ref="dept"/>

DiTest.java

@Test
public void testPNameSpace(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    Emp emp = ac.getBean("emp", Emp.class);
    System.out.println(emp);
}

5.5 autowire自动装配

SpringIOC容器可以自动装配Bean。需要做的仅仅是在<bean>标签的autowire属性里指定自动装配的模式,autowire属性的取值如下:

  • no:不进行自动装配
  • constructor:根据构造方法进行装配,不推荐使用
  • byType:根据当前bean中属性的类型与IOC容器中管理的bean的类型进行匹配
    如果类型一致则进行自动装配
    如果类型不一致不进行装配
    注意:如果IOC容器中有一个以上的bean类型与之匹配,则抛出异常
  • byName:根据bean的唯一标识符(id)和当前bean属性名称进行匹配
    如果名称相同则进行自动装配
    如果名称不同则不进行装配
  • default:根据<beans>标签中的default-autowire属性,进行配置

applicationContext.xml

<!-- 自动装配,自动注入 -->
<bean id="department" class="com.newcapec.bean.Dept" p:deptno="20" p:dname="人力部" p:loc="北京"/>
<bean id="employee" class="com.newcapec.bean.Emp" p:empno="8001" p:ename="李四" autowire="default"/>

DiTest.java

@Test
public void testAutowire(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    Emp emp = ac.getBean("employee", Emp.class);
    System.out.println(emp);
}

XML配置里的Bean自动装配的缺点

  • 在bean配置文件里设置autowire属性进行自动装配将会装配bean的所有属性。然而,若只希望装配个别属性时,autowire属性就不够灵活了
  • autowire属性要么根据类型自动装配,要么根据名称自动装配,不能两者兼而有之
  • 一般情况下,在实际的项目中很少使用自动装配功能,因为和自动装配功能所带来的好处比起来,明确清晰的配置文档更有说服力一些

6. 使用外部属性文件

加入命名空间和标签规范

xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

引入外部属性文件

<!-- 读取外部的资源文件-->
<context:property-placeholder location="classpath:db.properties"/>
public class MyDataSource {
    private String driver;
    private String url;
    private String username;
    private String password;

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "MyDataSource{" +
                "driver='" + driver + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

通过表达式访问属性文件中的数据

<bean id="myDataSource" class="com.newcapec.bean.MyDataSource">
    <property name="driver" value="${jdbc.oracle.driver}"/>
    <property name="url" value="${jdbc.oracle.url}"/>
    <property name="username" value="${jdbc.oracle.username}"/>
    <property name="password" value="${jdbc.oracle.password}"/>
</bean>

测试

public class ReadPropertiesTest {

    @Test
    public void test(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        MyDataSource myDataSource = ac.getBean("myDataSource", MyDataSource.class);
        System.out.println(myDataSource);
    }
}

7. Bean的作用域

在Spring中,可以在<bean>标签的scope属性里设置Bean的作用域,其取值如下:

  • singleton : 单例模式(默认值),IOC容器在初始化时创建bean的实例,在整个容器的生命周期中只创建一个bean实例
  • prototype : 原型的,IOC容器在初始化时不创建bean的实例,而是在每次使用时都创建一个新的bean的实例

Book.java

public class Book {
    private int id;
    private String name;
    private double price;
    private String author;

    public Book(){
        System.out.println("Book对象被创建....");
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", author='" + author + '\'' +
                '}';
    }
}

applicationContext.xml

<!-- bean的作用域-->
<bean id="book" class="com.newcapec.bean.Book" p:id="101" p:name="西游记"
      p:price="98.5" p:author="吴承恩" scope="singleton"/>

<bean id="book" class="com.newcapec.bean.Book" p:id="101" p:name="西游记"
      p:price="98.5" p:author="吴承恩" scope="prototype"/>

测试

public class ScopeTest {
    @Test
    public void test(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println("------------分割线--------------");

        Book book1 = ac.getBean("book",Book.class);
        System.out.println(book1);
        Book book2 = ac.getBean("book",Book.class);
        System.out.println(book2);

        System.out.println(book1 == book2);
    }
}

8. Bean的生命周期

  • SpringIOC容器可以管理Bean的生命周期,Spring允许在Bean生命周期的特定点执行定制的任务
  • SpringIOC容器对bean的生命周期进行管理的过程
    1. 通过构造构造或工厂方法创建bean对象
    2. 为bean的属性设置值和对其他bean的引用
    3. 调用bean的初始化方法
    4. bean可以使用了
    5. 当容器关闭时,调用bean的销毁方法
  • 在bean的声明里设置init-method和destroy-method属性,为bean指定初始化和销毁方法

Dog.java

public class Dog {
    private String name;
    private String owner;
    private int age;

    public Dog() {
        System.out.println("Dog对象被创建...");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("调用setName方法....");
        this.name = name;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        System.out.println("调用setOwner方法....");
        this.owner = owner;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        System.out.println("调用setAge方法....");
        this.age = age;
    }

    public void init(){
        System.out.println("Dog对象的初始化方法...");
    }
    public void destroy(){
        System.out.println("Dog对象的销毁方法...");
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", owner='" + owner + '\'' +
                ", age=" + age +
                '}';
    }
}

applicationContext.xml

<bean id="dog" class="com.newcapec.bean.Dog" p:name="旺财" p:owner="小明" p:age="5"  
      init-method="init" destroy-method="destroy"/>

测试

public class LifeCycleTest {
    @Test
    public void test(){
        System.out.println("-------容器初始化阶段---------");
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        System.out.println("-------对象使用阶段---------");
        Dog dog = ac.getBean("dog",Dog.class);
        System.out.println(dog);

        System.out.println("-------容器关闭阶段---------");
        //手动关闭容器
        ac.close();
    }
}

9. Bean之间的关系

9.1 继承关系

  • Spring允许继承bean的配置,被继承的bean称为父bean。继承这个父bean的bean称为子bean
  • 子bean从父bean中继承配置, 包括bean的属性配置
  • 子bean也可以覆盖从父bean继承过来的配置
  • 父bean可以作为配置模板,也可以作为bean实例。若只想把父bean作为模板,可以设置<bean>的abstract属性为true,这样Spring将不会实例化这个bean
  • 并不是<bean>元素里的所有属性都会被继承。比如: autowire,abstract等
  • 也可以忽略父bean的class属性,让子bean指定自己的类,而共享相同的属性配置。但此时abstract必须设为true

Computer.java

public class Computer {
    private String name;
    private String type;
    private double price;

    public String getName() {
        return name;
    }

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

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Computer{" +
                "name='" + name + '\'' +
                ", type='" + type + '\'' +
                ", price=" + price +
                '}';
    }
}

applicationContext.xml

<!-- bean之间的继承关系 -->
<bean id="parentComputer" class="com.newcapec.bean.Computer">
    <property name="name" value="联想"/>
    <property name="type" value="台式机"/>
    <property name="price" value="4500"/>
</bean>
<bean id="childComputer" class="com.newcapec.bean.Computer" parent="parentComputer">
    <property name="name" value="戴尔"/>
</bean>

测试

public class RelationTest {
    @Test
    public void testExtends(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        Computer childComputer = ac.getBean("childComputer", Computer.class);
        System.out.println(childComputer);
    }
}

9.2 依赖关系

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

Address.java

public class Address {
    public Address() {
        System.out.println("Address对象创建了....");
    }
}

User.java

public class User {
    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public User() {
        System.out.println("User对象创建了....");
    }

    @Override
    public String toString() {
        return "User{" +
                "address=" + address +
                '}';
    }
}

applicationContext.xml

<!-- bean之间的依赖关系 -->
<bean id="user" class="com.newcapec.bean.User" p:address-ref="address" depends-on="address"/>
<bean id="address" class="com.newcapec.bean.Address"/>

测试

public class RelationTest {
    @Test
    public void testDependsOn(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        User user = ac.getBean("user", User.class);
        System.out.println(user);
    }
}

10. 通过工厂实例化Bean

10.1 静态工厂

  • 调用静态工厂方法创建bean是将对象创建的过程封装到静态方法中。当客户端需要对象时,只需要简单地调用静态方法,而无需关心创建对象的细节
  • 要声明通过静态方法创建的bean,需要在bean的class属性里指定拥有该工厂的方法的类,同时在factory-method属性里指定工厂方法的名称。最后,使用<constrctor-arg>元素为该方法传递方法参数

Cat.java

public class Cat {
    private String name;
    private int age;

    public Cat() {
    }

    public Cat(String name, int age) {
        this.name = name;
        this.age = 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 "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

CatStaticFactory.java

/**
 * 一个用于创建cat对象的静态工厂
 */
public class CatStaticFactory {
    /**
     * 提供一个创建对象的静态方法
     */
    public static Cat getInstance(String name, int age){
        return new Cat(name, age);
    }
}

applicationContext.xml

<!-- 静态工厂-->
<bean id="cat1" class="com.newcapec.factory.CatStaticFactory" factory-method="getInstance">
    <constructor-arg value="汤姆猫"/>
    <constructor-arg value="2"/>
</bean>

测试

public class FactoryTest {
    @Test
    public void testStaticFactory(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Cat cat = ac.getBean("cat1", Cat.class);
        System.out.println(cat);
    }
}

10.2 实例工厂

  • 将对象的创建过程封装到另外一个对象实例的方法里。当客户端需要请求对象时,只需要简单的调用该实例方法而不需要关心对象的创建细节
  • 要声明通过实例工厂方法创建的bean,在<bean>的factory-bean属性里指定拥有该工厂方法的bean,并且factory-method属性里指定该工厂方法的名称。最后,使用<construtor-arg>标签为工厂方法传递方法参数

CatInstanceFactory.java

/**
 * 一个用于创建cat对象的实例工厂
 */
public class CatInstanceFactory {
    /**
     * 提供一个创建对象的非静态方法
     */
    public Cat getInstance(String name, int age){
        return new Cat(name, age);
    }
}

applicationContext.java

<!-- 实例工厂-->
<bean id="instanceFactory" class="com.newcapec.factory.CatInstanceFactory"/>
<bean id="cat2" factory-bean="instanceFactory" factory-method="getInstance">
    <constructor-arg value="波斯猫"/>
    <constructor-arg value="3"/>
</bean>

测试

public class FactoryTest {
    @Test
    public void testInstanceFactory(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Cat cat = ac.getBean("cat2", Cat.class);
        System.out.println(cat);
    }
}

11. FactoryBean实例化Bean

  • Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean
  • 工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象

CatFactoryBean.java

public class CatFactoryBean implements FactoryBean<Cat> {
    private int age;
    private String name;
    public void setAge(int age) {
        this.age = age;
    }
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取对象
     */
    @Override
    public Cat getObject() throws Exception {
        return new Cat(name, age);
    }

    /**
     * 生成对象的Class类型
     */
    @Override
    public Class<?> getObjectType() {
        return Cat.class;
    }

    /**
     * 设置该对象是否为单例模式
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}

applicationContext.xml

<!-- FactoryBean配置bean -->
<bean id="cat3" class="com.newcapec.factory.CatFactoryBean">
    <property name="name" value="加菲猫"/>
    <property name="age" value="5"/>
</bean>

测试

public class FactoryTest {
    @Test
    public void testFactoryBean(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Cat cat = ac.getBean("cat3", Cat.class);
        System.out.println(cat);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JTZ001

你的鼓励是我创作的最大动力?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值