Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)

2023-12-13 07:38:13

目录

一、前言

二、Bean配置信息重用

? ? ? ? 1.简介 :?

? ? ? ? 2.实例 :?

三、关于Bean的创建顺序

? ? ? ? 1.简介 :?

? ? ? ? 2.实例 :?

四、关于Bean的单例和多例

? ? ? ? 1.简介 :?

? ? ? ? 2.实例 :?

五、关于Bean的生命周期

? ? ? ? 1.简介 :?

? ? ? ? 2.实例 :?

六、Bean配置后置处理器

? ? ? ? 1.简介 :?

? ? ? ? 2.实例 :?

七、通过.properties文件为Bean注入属性

? ? ? ? 1.简介 :?

? ? ? ? 2.实例 :?

八、关于Bean的自动装配

? ? ? ? 1.简介 :?

? ? ? ? 2.实例 :?

九、关于Spring-EL表达式

? ? ? ? 1.简介 :?

? ? ? ? 2.实例 :?

十、总结?


一、前言

  • 本篇博文是对“Spring IOC——基于XML方式对Bean的配置和管理”一文内容补充(十、关于Bean配置的更多内容和细节)。
  • 注意事项——代码中的注释也很重要;不要眼高手低,自己跟着过一遍才有收获;点击文章的侧边栏目录或者文章开头的目录可以进行跳转。
  • 良工不示人以朴,所有文章都会适时补充完善。大家如果有问题都可以在评论区进行交流或者私信up。感谢阅读!

二、Bean配置信息重用

? ? ? ? 1.简介 :?

? ? ? ? 所谓“Bean配置信息重用”,指的是在通过XML配置Bean时,若你想配置一个Bean与之前的某个Bean属性值相同,可以使用 parent="bean_id" 实现,表示当前bean对象的属性值从“id = bean_id”的bean对象中来

? ? ? ? 2.实例 :?

? ? ? ? ? ? ? ? 首先,我们在beans.xml中配置一个对象,用以充当parent,代码如下 :?

    <!-- (1) Bean配置信息重用 -->
    <bean class="com.cyan.spring.bean.Student" id="stu10">
        <property name="name" value="Cyan"></property>
        <property name="age" value="21"></property>
        <property name="score" value="455"></property>
    </bean>

? ? ? ? ? ? ? ? 然后,另新配置一个Bean,并使用 parent="stu10" 重用上面这个Bean的属性值,代码如下 :

    <bean class="com.cyan.spring.bean.Student" id="stu11" parent="stu10"/>

? ? ? ? ? ? ? ? 接着,仍是在测试类StudentBeanByXML中,新定义一个单元测试方法,测试Bean配置信息重用是否成功。
? ? ? ? ? ? ? ? beanConfigReuse()方法代码如下 :?

    //(1) 测试 Bean配置信息重用
    @Test
    public void beanConfigReuse() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

        Student stu10 = ioc.getBean("stu10", Student.class);
        Student stu11 = ioc.getBean("stu11", Student.class);
        
        System.out.println("stu10 = " + stu10);
        System.out.println("stu11 = " + stu11);
    }

? ? ? ? ? ? ? ? 运行结果 :?

? ? ? ? ? ? ? ? 此外,我们还可以直接通过?abstract="true"?定义一个抽象Bean,抽象Bean本身不能被实例化,也就无法获取;但是可以通过parent属性配置一个同抽象类属性值相同的Bean对象,代码如下 :?

    <!--配置抽象bean-->
    <bean class="com.cyan.spring.bean.Student" id="stu12" abstract="true">
        <property name="name" value="Ice"></property>
        <property name="age" value="22"></property>
        <property name="score" value="462"></property>
    </bean>
    <bean class="com.cyan.spring.bean.Student" id="stu13" parent="stu12"/>

? ? ? ? ? ? ? ? 在测试类中获取bean并打印bean的信息,代码如下 : (仍在beanConfigReuse()方法中)

    //(1) 测试 Bean配置信息重用
    @Test
    public void beanConfigReuse() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

        Student stu10 = ioc.getBean("stu10", Student.class);
        Student stu11 = ioc.getBean("stu11", Student.class);

        System.out.println("stu10 = " + stu10);
        System.out.println("stu11 = " + stu11);

        //-----------------------------------------------------
        /*
        //Student stu12 = ioc.getBean("stu12", Student.class);
            直接使用getBean方法获取抽象Bean会报错————
            org.springframework.beans.factory.BeanIsAbstractException:
         */
        Student stu13 = ioc.getBean("stu13", Student.class);

        //System.out.println("stu12 : " + stu12);
        System.out.println("stu13 : " + stu13);
    }

? ? ? ? ? ? ? ? 运行结果 :?


三、关于Bean的创建顺序

? ? ? ? 1.简介 :?

? ? ? ? Spring的IOC容器,默认是按照Bean配置时的顺序创建Bean对象

? ? ? ? 而如果我们在前面配置的Bean对象中用到了 depends-on="bean_id"?即前面配置的Bean对象依赖了后面配置的Bean对象, IOC容器就会先去创建依赖的Bean对象,即使它是在后面配置的。

? ? ? ? 2.实例 :?

? ? ? ? ? ? ? ? 首先,我们来建两个JavaBean类用于演示,在它们的无参构造中打印出提示信息,以便测试。
? ? ? ? ? ? ? ? Commodity类代码如下 :?

package com.cyan.spring.bean;

public class Commodity {
    private Integer id;
    private String name;
    private Double price;

    public Commodity() {
        System.out.println("Commodity类的无参构造被调用~");
    }
    public Commodity(Integer id, String name, Double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public Integer getId() {
        return id;
    }
    public void setId(Integer 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;
    }

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

? ? ? ? ? ? ? ? Member类代码如下 :?

package com.cyan.spring.bean;

public class Member {
    private Integer id;
    private String name;

    public Member() {
        System.out.println("Member类的无参构造被调用~");
    }
    public Member(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return this.id;
    }
    public void setId(Integer id) {
        this.id = id;
    }

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

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

? ? ? ? ? ? ? ? 然后,我们先在beans.xml中按照Member --> Commodity的顺序配置两个Bean对象,即我们认为Member对象应该在Commodity对象之前被创建。代码如下 :?

    <!-- (2) Bean创建顺序 -->
    <bean class="com.cyan.spring.bean.Member" id="member01">
        <property name="id" value="1"></property>
        <property name="name" value="Cyan"></property>
    </bean>
    <bean class="com.cyan.spring.bean.Commodity" id="commodity01">
        <property name="id" value="1"></property>
        <property name="name" value="Mate90"></property>
        <property name="price" value="9999"></property>
    </bean>

? ? ? ? ? ? ? ? 接着,在测试类StudentBeanByXML中新定义一个单元测试方法,beanCreateOrder()方法代码如下 :?

    //(2) 测试 Bean创建顺序
    @Test
    public void beanCreateOrder() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

        Member member01 = ioc.getBean("member01", Member.class);
        Commodity commodity01 = ioc.getBean("commodity01", Commodity.class);

        System.out.println("member01 = " + member01);
        System.out.println("commodity01 = " + commodity01);
    }

? ? ? ? ? ? ? ? 运行结果 :?

????????????????可以看到,Bean对象的确是依据配置时的顺序被创建的。
????????????????然后,我们利用 depends-on?额外配置两个Bean,要求先配置Commodity对象,且Commodity对象依赖于Member对象代码如下 : (将刚才配置的两个Bean对象注释掉)

    <bean class="com.cyan.spring.bean.Commodity" id="commodity02" depends-on="member02">
        <property name="id" value="2"></property>
        <property name="name" value="P90"></property>
        <property name="price" value="8999"></property>
    </bean>
    <bean class="com.cyan.spring.bean.Member" id="member02">
        <property name="id" value="2"></property>
        <property name="name" value="Five"></property>
    </bean>

? ? ? ? ? ? ? ? 再次在beanCreateOrder()方法中获取Bean,代码如下 :?

    //(2) 测试 Bean创建顺序
    @Test
    public void beanCreateOrder() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

        Commodity commodity02 = ioc.getBean("commodity02", Commodity.class);
        Member member02 = ioc.getBean("member02", Member.class);

        System.out.println("commodity02 = " + commodity02);
        System.out.println("member02 = " + member02);
    }

? ? ? ? ? ? ? ? 运行结果 :?

? ? ? ? ? ? ? ? 可以看到,明明我们在beans.xml中先配置的是Commodity对象,但由于depends-on属性导致Commodity对象依赖于Member对象,因此最终是Member对象首先被创建。

? ? ? ? ? ? ? ? PS:补充一点,对于ref引用——ref属性并不会影响Bean配置的顺序,因为IOC容器对Bean对象的创建是以一个整体来执行的,先完成Bean对象的创建,再进行ref引用的关联。。


四、关于Bean的单例和多例

? ? ? ? 1.简介 :?

? ? ? ? (1)Spring的IOC容器中,默认是按照单例形式来创建对象的,即配置一个Bean对象后,IOC容器只会创建一个该Bean的实例,后续使用的是同一个实例。若希望IOC容器按照多例形式来创建Bean对象,可以通过在配置Bean时加入 scope = "prototype"[/?pro?t?ta?p/,原型] 属性来指定。

? ? ? ? (2) 配置的Bean默认scope="singleton"(即使你没有手动写出),若该Bean没有进行任何其他配置,默认会在容器启动时创建该Bean实例,并放入到单例池beanFactory-->singletonObjects中若配置Bean时指定了scope = "prototype",则Bean实例被创建的时机会被推迟,该Bean只有在getBean(...)时才会被创建。(可通过无参构造打印提示信息进行验证)

? ? ? ? (3) 实际上,Bean实例的创建时机,还取决于Bean的lazy-init (懒加载)属性;默认lazy-init="false",即默认Bean实例都是在容器启动时就创建(以空间换时间)。对于?scope = "prototype"?的情况,不管?lazy-init="true 还是 lazy-init="false",Bean实例都是只有在getBean(...)时才会被创建。而对于?scope="singleton" 的情况,若在单例形式的同时,希望Bean是在getBean(...)被调用时创建,则可以指定懒加载lazy-init="true"。

? ? ? ? 2.实例 :?

? ? ? ? ? ? ? ? 以我们刚才创建的Member类为JavaBean,首先我们注释掉Member类中重写的toString()方法,其目的是可以打印出对象的哈希码值,如下图所示 :?

? ? ? ? ? ? ? ? 然后,我们在beans.xml中配置一个Member对象(将之前配置的Member对象注释掉,防止无参构造打印出的提示信息造成干扰),代码如下 :?

    <!-- (3) Bean单例多例 -->
        <!-- 即使不手动写出,默认scope = singleton -->
    <bean class="com.cyan.spring.bean.Member" id="member03">
        <property name="id" value="3"></property>
        <property name="name" value="Cyan"></property>
    </bean>

? ? ? ? ? ? ? ? 接着,我们在StudentBeanByXML测试类中另定义一个单元测试方法,在测试方法中多次获取 id=member03 的Bean对象,打印对象并比较它们的哈希码值(不同对象的哈希码值一般不同),beanScope()方法代码如下 :?

    //(3) 测试 Bean单例多例
    @Test
    public void beanScope() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

        Member member03 = ioc.getBean("member03", Member.class);
        Member member03_EX = ioc.getBean("member03", Member.class);
        Member member03_Pro = ioc.getBean("member03", Member.class);

        System.out.println("member03 = " + member03);
        System.out.println("member03_EX = " + member03_EX);
        System.out.println("member03_Pro = " + member03_Pro);
    }

? ? ? ? ? ? ? ? 运行结果 :?

? ? ? ? ? ? ? ? 由对象的哈希码值我们可以得知,尽管我们获取了三次Bean对象,打印出的却始终是同一个对象,即是单例的。

? ? ? ? ? ? ? ? 接下来,我们在方才配置的Member对象上添加?scope = "prototype" 属性,如下图所示 :?

? ? ? ? ? ? ? ? 再次运行beanScope()测试方法,结果如下 :?

? ? ? ? ? ? ? ? 可以看到,三次获取到的Bean对象是三个不同的对象,且由Member类无参构造打印出的提示信息我们亦可得知,配置的Member类型的Bean实例被创建了三次。

? ? ? ? ? ? ? ? 注意,假如我们此时将getBean(...)的代码注释掉,如下图所示 :?

? ? ? ? ? ? ? ? 此时,若再次运行beanScope()方法,会发现没有Bean被创建(没有无参构造打印出的提示信息),如下图所示 :?

? ? ? ? ? ? ? ? 原因我们上面也说了——对于?scope = "prototype"(多例形式)?的情况,不管?lazy-init="true 还是 lazy-init="false",Bean实例都是只有在getBean(...)时才会被创建。现在我们把getBean(...)方法注释掉了,当然也就没有Bean实例被创建了。

? ? ? ? ? ? ? ? 那么,如果我们在getBean(...)方法被注释掉的基础上,又将多例改为单例呢?如下图所示 :?

? ? ? ? ? ? ? ? 这时候我们再运行beanScope()方法,会发现有无参构造打印的提示信息了,如下 :?

? ? ? ? ? ? ? ? 原因我们上文也提到了——配置的Bean,默认scope="singleton"(即使你没有手动写出),若该Bean没有进行任何其他配置,默认会在容器启动时创建该Bean实例,并放入到单例池beanFactory-->singletonObjects中
? ? ? ? ? ? ? ? 当然,如果这时候我们希望在单例形式下实现懒加载,可以在?scope="singleton"?的基础上,配置懒加载lazy-init="true",这样即使此时scope="singleton",也不会有无参构造打印出提示信息了(因为getBean被注释了,不会有Bean实例被创建),如下图所示 :?

? ? ? ? ? ? ? ? 这时候我们再运行beanScope()测试方法,会发现再一次“一无所获”,如下图所示 :?


五、关于Bean的生命周期

? ? ? ? 1.简介 :?

? ? ? ? Spring中,Bean的生命周期是指Bean从创建到初始化再到销毁的过程,这个过程由IOC容器管理,主要包括以下几个过程——

? ? ? ? JVM完成创建Bean对象,执行构造器

? ? ? ? 执行相关setter方法(Bean的属性注入)。

? ? ? ? 调用初始化Bean的方法。(需要在beans.xml中配置Bean时,通过 init-method="method_name" 属性来指定,初始化bean的方法在setter方法后执行,具体执行时机由IOC容器来控制

? ? ? ? 使用Bean对象。

? ? ? ? 调用销毁Bean的方法(需要在beans.xml中配置Bean时,通过 destroy-method="method_name" 属性来指定,销毁bean的方法在IOC容器关闭的时候被执行。)。

? ? ? ? 2.实例 :?

? ? ? ? ? ? ? ? 首先,我们来定义一个用于演示的JavaBean,在该JavaBean中自定义初始化Bean的方法和销毁Bean的方法
? ? ? ? ? ? ? ? Task类代码如下 :?

package com.cyan.spring.bean;

public class Task {
    private String taskName;

    public Task() {
        System.out.println("Task 类的无参构造器被执行~");
    }
    public Task(String taskName) {
        this.taskName = taskName;
    }

    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        System.out.println("(IOC容器通过setter进行Bean的属性注入)The taskName you set is : " + taskName);
        this.taskName = taskName;
    }

    //初始化Bean的方法
    public void init() {
        System.out.println(this.getTaskName() + " is initiated~");
    }

    //销毁Bean的方法
    public void destroy() {
        System.out.println(this.getTaskName() + " is destroyed~");
    }
}

? ? ? ? ? ? ? ? 然后,在beans.xml文件中配置一个Task类的Bean对象,代码如下 :?

    <!-- (4) Bean生命周期 -->
    <bean class="com.cyan.spring.bean.Task" id="task01"
          init-method="init" destroy-method="destroy">
        <property name="taskName" value="不动鸣神,泡影断灭"></property>
    </bean>

? ? ? ? ? ? ? ? 接着,仍然是在StudentBeanByXML测试类中定义一个单元测试方法,输出获取到的Bean对象,查看无参构造,setter方法,以及自定义的初始化Bean和销毁Bean的方法有没有打印出提示信息。beanLifeCycle()方法代码如下 :?

    //(4) 测试 Bean生命周期
    @Test
    public void beanLifeCycle() {
        //回顾———接口的多态
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

        Object task01 = ioc.getBean("task01");

        System.out.println("task01 = " + ((Task)task01));

        //若想使用close方法,需要强制向下转型(此处的接口的向下转型)
        ((ConfigurableApplicationContext) ioc).close();
    }

? ? ? ? ? ? ? ? 运行结果 :?


六、Bean配置后置处理器

? ? ? ? 1.简介 :?

? ? ? ? (1) 在Spring的IOC容器中,可以配置bean的后置处理器,后置处理器的本质其实是一个实现了BeanPostProcessor接口的Java类

? ? ? ? (2) 后置处理器通常会实现BeanPostProcessor接口中的两个方法,这两个方法允许该后置处理器在Bean的初始化方法(即配置Bean时init-method属性所指定的初始化方法)调用前调用后进行相应的业务处理(即程序员可以在这两个方法中处理自己的代码)。其实,我们可以直接“见名知意”,这两个方法分别为postProcessBeforeInitialization(...) postprocessAfterInitialization(...)

? ? ? ? (3) 当我们在XML文件中配置了后置处理器,后置处理器对象会作用于当前IOC容器中所有创建的Bean对象(体现了AOP[面向切面编程]);“对IOC容器中所有创建的Bean对象的统一处理”可以应用于日志处理、权限校验、安全验证,事务管理等业务场景

? ? ? ? 2.实例 :?

? ? ? ? ? ? ? ? 就以上文中定义的Task类为JavaBean类(我们之前在该类中业已定义了初始化Bean的方法和销毁Bean的方法),我们还需要定义一个后置处理器,CyanBeanPostProcessor类代码如下 :?

package com.cyan.spring.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class CyanBeanPostProcessor implements BeanPostProcessor {
    /**
     * postProcessBeforeInitialization(...)方法在Bean的init方法执行前被调用
     * @param bean : IOC容器中创建的Bean对象
     * @param beanName : 传入的该Bean对象的id属性值
     * @return : 通常返回业务处理后的bean对象。
     * @throws BeansException : 执行过程中若出现异常,抛出BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization方法被调用" + ", bean=" + bean + " beanName=" + beanName);
        return bean;
    }

    /**
     * postProcessAfterInitialization(...)方法在Bean的init方法执行后被调用(注意调用顺序)
     * @param bean : IOC容器中创建的Bean对象
     * @param beanName : 传入的该Bean对象的id属性值
     * @return : 通常返回业务处理后的bean对象。
     * @throws BeansException : 执行过程中若出现异常,抛出BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization方法被调用" + ", bean=" + bean + " beanName=" + beanName);
        return bean;
    }
}

? ? ? ? ? ? ? ? 为了更好的演示Bean的后置处理器,up在src目录下新建了一个beans_EX.xml配置文件。在beans_EX.xml配置文件中,我们需要配置一个Task类的bean对象,同时要配置后置处理器类型的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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置Task类Bean对象 -->
    <bean class="com.cyan.spring.bean.Task" id="task01"
          init-method="init" destroy-method="destroy">
        <property name="taskName" value="高墙谍影"></property>
    </bean>

    <!-- 配置后置处理器对象 -->
    <bean class="com.cyan.spring.bean.CyanBeanPostProcessor" id="beanPostProcessor01"/>
</beans>

? ? ? ? ? ? ? ? 最后,仍然是在StudentBeanByXML测试类中新定义一个单元测试方法,测试后置处理器实现的两个方法是否被调用。
? ? ? ? ? ? ? ? beanPostProcessor()方法代码如下 :

    //(5) 测试 Bean后置处理器
    @Test
    public void beanPostProcessor() {
        //注意此处获取IOC容器时,传入的实参变成了beans_EX.xml
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans_EX.xml");

        Object task01 = ioc.getBean("task01");

        System.out.println("task01 = " + ((Task)task01));

        ((ConfigurableApplicationContext) ioc).close();
    }

? ? ? ? ? ? ? ? 运行结果 :?

? ? ? ? ? ? ? ? 可以看到,仍然满足上文中Bean的生命周期的五个过程;并且在此基础上,初始化Bean的方法执行前调用了postProcessBeforeInitialization(...)方法,而初始化Bean的方法执行后调用了postProcessAfterInitialization(...)方法。


七、通过.properties文件为Bean注入属性

? ? ? ? 1.简介 :?

????????在XML中通过命名空间<context:property-placeholder location="classpath:__.properties"/>指定一个属性文件,在该属性文件中为Bean的属性赋值。其中,location属性表示文件所在的位置(注意格式)。

? ? ? ? 在配置Bean时,以"${key}"的形式引用属性文件中定义的属性值。

? ? ? ? 2.实例 :?

? ? ? ? ? ? ? ? 首先我们需要在src目录下建立一个自定义的properties文件。up以cyan.properties为例,如下所示 :?

? ? ? ? ? ? ? ? 以上文“关于Bean的创建顺序”中定义的Commodity类为JavaBean类,在cyan.properties属性文件中为Bean属性赋值:

id=1
name=Camera
price=12999

?????????????????继续回到我们的beans.xml中进行配置,将上文“Bean的生命周期”中配置的Task类Bean对象注释掉,防止打印出的信息造成干扰。在beans.xml中配置一个Commodity类型的Bean对象,并指定相应的属性文件,代码如下 :?

    <!-- (6) 通过属性文件为Bean注入属性 -->
    <!-- 配置一个Commodity对象 -->
    <bean class="com.cyan.spring.bean.Commodity" id="commodity01">
        <property name="id" value="${id}"></property>
        <property name="name" value="${name}"></property>
        <property name="price" value="${price}"></property>
    </bean>
    <!-- 指定一个属性文件为该Commodity对象的属性赋值 -->
    <context:property-placeholder location="classpath:cyan.properties"/>

? ? ? ? ? ? ? ? 在测试类中新定义一个单元测试方法,测试为Bean输入属性是否成功。
????????????????injectBeanByProperties()方法代码如下 :?

    //(6) 测试 通过属性文件为Bean注入属性
    @Test
    public void injectBeanByProperties() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

        Commodity commodity01 = ioc.getBean("commodity01", Commodity.class);

        System.out.println("commodity01 = " + commodity01);
    }

? ? ? ? ? ? ? ? 运行结果 :?


八、关于Bean的自动装配

? ? ? ? 1.简介 :?

? ? ? ? 前面我们在讲“通过ref引用实现Bean的相互引用”时提到,在Spring中,可以通过ref引用实现“Web层调用Service层,Service层调用DAO层”的分层设计思想,如下图所示? :(回顾)

????????而“Bean的自动装配”,其实就是以另一种更快捷的方式来达到这个效果。

? ? ? ? Bean的自动装配,需要在配置Bean时通过autowire属性来实现。

? ? ? ? Δautowire属性又主要有两种常用的属性值——byType 和 byName

? ? ? ? byType,指通过类型给Bean对象的属性完成赋值,即根据属性的类型在IOC容器中匹配有没有相同的类型的Bean对象(要求当前IOC容器中有且至多有一个对应类型的Bean对象),若匹配成功则自动装配

? ? ? ? byName,指通过setter方法的名称给Bean对象的属性完成赋值,即根据setXxx方法的xxx (字面意思),去IOC容器中寻找"id=xxx"的Bean对象,若匹配成功则自动装配

? ? ? ? 2.实例 :?

? ? ? ? ? ? ? ? 先来演示autowire="byType"的情况。
? ? ? ? ? ? ? ? 基于"Web层调用Service层,Service层调用DAO层"的思想,我们先来定义三个演示类OrderServlet, OrderServiceImpl, OrderDAOImpl
? ? ? ? ? ? ? ? OrderDAOImpl类代码如下 :?

package com.cyan.spring.dao;

public class OrderDAOImpl {
    public void saveOder() {
        System.out.println("保存了订单...");
    }
}

? ? ? ? ? ? ? ? OrderServiceImpl类代码如下 :?

package com.cyan.spring.service;

import com.cyan.spring.dao.OrderDAOImpl;

public class OrderServiceImpl {
    private OrderDAOImpl orderDAO;

    public OrderDAOImpl getOrderDAO() {
        return orderDAO;
    }

    public void setOrderDAO(OrderDAOImpl orderDAO) {
        this.orderDAO = orderDAO;
    }
}

? ? ? ? ? ? ? ? OrderServlet类代码如下 :?

package com.cyan.spring.web;

import com.cyan.spring.service.OrderServiceImpl;

public class OrderServlet {
    private OrderServiceImpl orderService;

    public OrderServiceImpl getOrderService() {
        return orderService;
    }

    public void setOrderService(OrderServiceImpl orderService) {
        this.orderService = orderService;
    }
}

????????????????接着,在beans.xml中分别配置OrderDAOImpl,OrderServiceImpl,OrderServlet类的Bean对象,代码如下 :?

    <!-- (7) Bean自动装配 -->
    <!-- OrderDAOImpl类中没有定义属性,因此不需要配置autowire -->
    <bean class="com.cyan.spring.dao.OrderDAOImpl" id="orderDAO01"/>
    <bean autowire="byType" class="com.cyan.spring.service.OrderServiceImpl" id="orderService01"/>
    <bean autowire="byType" class="com.cyan.spring.web.OrderServlet" id="orderServlet01"/>

? ? ? ? ? ? ? ? 最后,在测试类中定义单元测试方法,beanAutoAssemble()方法代码如下 :?

    //(7) 测试 Bean的自动装配
    @Test
    public void beanAutoAssemble() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

        OrderServlet orderServlet01 = ioc.getBean("orderServlet01", OrderServlet.class);

        System.out.println("orderServlet01" + orderServlet01);
        System.out.println("orderServiceImpl01" + orderServlet01.getOrderService());
        System.out.println("orderDAOImpl" + orderServlet01.getOrderService().getOrderDAO());
    }

? ? ? ? ? ? ? ? 运行结果 :?

????????????????再来演示autowire="byName"的情况。
? ? ? ? ? ? ? ? 假设我们直接将原先配置的OrderServlet和OrderServiceImpl的autowire改为byName,如下图所示 :?

? ? ? ? ? ? ? ? 由于setOrderService方法和setOrderDAO对应的xxx分别为"orderService"和"orderDAO",因此会匹配IOC容器中 id=orderService 和 id=orderDAO的Bean对象,而我们此时只有id=orderDAO01 和 id=orderService01的Bean对象,因此匹配失败,可以猜到测试方法会报错空指针异常(不能正常调用getOrderDAO()方法),我们尝试运行,结果如下 :?

? ? ? ? ? ? ? ? 果然如此,此时,解决方法有两个——修改配置的Bean对象的id为setter方法对应的xxx修改setter方法的名称,使之与已配置的Bean的id匹配。这里up以解决方法①为例,如下图所示 ;?

? ? ? ? ? ? ? ? 这时,我们再次运行beanAutoAssemble()方法,结果如下 :?

? ? ? ? ? ? ? ? 果然如此。


九、关于Spring-EL表达式

? ? ? ? 1.简介 :?

? ? ? ? (1) Spring Expression Language,简称SpEL,指Spring表达式语言,支持运行时查询并操作对象,类似于EL表达式,SpEL根据JavaBean的getXxx()、setXxx()方法定义的属性访问对象。

? ? ? ? (2) SpEL使用#{...}作为定界符,所有在{}中的字符都将被认为是SpEL表达式。

? ? ? ? 2.实例 :?

? ? ? ? ? ? ? ? 首先建立一个新的JavaBean,以Cat类为例,Cat类代码如下:?

package com.cyan.spring.bean;

public class Cat {
    //fields
    private String name;
    private int age;
    private String food;
    private String habit;

    //constructor
    public Cat() {
    }
    public Cat(String name, int age, String food, String habit) {
        this.name = name;
        this.age = age;
        this.food = food;
        this.habit = habit;
    }

    //getter, setter
    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 String getFood() {
        return food;
    }
    public void setFood(String food) {
        this.food = food;
    }

    public String getHabit() {
        return habit;
    }
    public void setHabit(String habit) {
        this.habit = habit;
    }

    //非静态方法
    public String eat(String food) {
        return food;
    }
    //静态方法
    public static String like(String habit) {
        return habit;
    }

    //重写toString()方法
    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", food='" + food + '\'' +
                ", habit='" + habit + '\'' +
                '}';
    }
}

? ? ? ? ? ? ? ? 在beans.xml中通过SpEL配置Cat类Bean对象,代码如下 :?

    <!-- (8) SpEL表达式 -->
    <bean class="com.cyan.spring.bean.Cat" id="cat01">
        <property name="name" value="lit"></property>
        <property name="age" value="2"></property>
        <property name="food" value="meat"></property>
        <property name="habit" value="sleep"></property>
    </bean>
    <bean class="com.cyan.spring.bean.Cat" id="cat02">
        <property name="name" value="#{'cro'}"></property>
        <!-- 使用SpEL可以引用其他Bean的属性 -->
        <property name="age" value="#{cat01.age - 1}"></property>
        <!-- 使用SpEL可以调用非静态方法,并以其返回值赋值 -->
        <property name="food" value="#{cat01.eat('fish')}"></property>
        <!-- 使用SpEL可以调用静态方法,并以其返回值赋值 -->
        <property name="habit" value="#{T(com.cyan.spring.bean.Cat).like('playing')}"></property>
    </bean>

? ? ? ? ? ? ? ? 最后,仍然是在测试类中定义一个单元测试方法,测试id=cat02的Bean对象的属性是否注入成功。
? ? ? ? ? ? ? ? testSpringEL()方法代码如下 :?

    //(8) 测试 Spring-EL表达式
    @Test
    public void testSpringEL() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

        Cat cat02 = ioc.getBean("cat02", Cat.class);

        System.out.println("cat02 = " + cat02);
    }

? ? ? ? ? ? ? ? 运行结果 :?


十、总结?

????????🆗,以上就是“关于Bean配置的更多内容和细节”的全部内容了,虽然本篇博文是只是对

Spring IOC——基于XML方式对Bean的配置和管理”一文内容补充,但由于“Spring——基于XML配置和管理Bean”本身内容较多,所以这篇补充性质的文章篇幅也不短,希望大家勤动手,自己写一些,感谢阅读!

? ? ? ? System.out.println("END-----------------------------);?

文章来源:https://blog.csdn.net/TYRA9/article/details/134790897
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。