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

简介: Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!

目录

一、前言

二、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>

image.gif

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

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

image.gif

               接着,仍是在测试类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);
    }

image.gif

               运行结果 :

image.gif 编辑

               此外,我们还可以直接通过 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"/>

image.gif

               在测试类中获取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);
    }

image.gif

               运行结果 :

image.gif 编辑


三、关于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 +
                '}';
    }
}

image.gif

               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 + '\'' +
                '}';
    }
}

image.gif

               然后,我们先在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>

image.gif

               接着,在测试类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);
    }

image.gif

               运行结果 :

image.gif 编辑

               可以看到,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>

image.gif

               再次在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);
    }

image.gif

              运行结果 :

image.gif 编辑

               可以看到,明明我们在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()方法,其目的是可以打印出对象的哈希码值,如下图所示 :

image.gif 编辑

               然后,我们在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>

image.gif

               接着,我们在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);
    }

image.gif

               运行结果 :

image.gif 编辑

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

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

image.gif 编辑

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

image.gif 编辑

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

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

image.gif 编辑

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

image.gif 编辑

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

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

image.gif 编辑

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

image.gif 编辑

               原因我们上文也提到了——配置的Bean,默认scope="singleton"(即使你没有手动写出),若该Bean没有进行任何其他配置,默认会在容器启动时创建该Bean实例,并放入到单例池beanFactory-->singletonObjects中

               当然,如果这时候我们希望在单例形式下实现懒加载,可以在 scope="singleton" 的基础上,配置懒加载lazy-init="true",这样即使此时scope="singleton",也不会有无参构造打印出提示信息了(因为getBean被注释了,不会有Bean实例被创建),如下图所示 :

image.gif 编辑

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

image.gif 编辑


五、关于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~");
    }
}

image.gif

               然后,在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>

image.gif

               接着,仍然是在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();
    }

image.gif

               运行结果 :

image.gif 编辑


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

image.gif

               为了更好的演示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>

image.gif

               最后,仍然是在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();
    }

image.gif

               运行结果 :

image.gif 编辑

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


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

       1.简介 :

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

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

      2.实例 :

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

image.gif 编辑

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

id=1
name=Camera
price=12999

image.gif

                继续回到我们的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"/>

image.gif

               在测试类中新定义一个单元测试方法,测试为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);
    }

image.gif

              运行结果 :

image.gif 编辑


八、关于Bean的自动装配

       1.简介 :

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

image.gif 编辑

       而“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("保存了订单...");
    }
}

image.gif

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

image.gif

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

image.gif

               接着,在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"/>

image.gif

               最后,在测试类中定义单元测试方法,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());
    }

image.gif

               运行结果 :

image.gif 编辑

               再来演示autowire="byName"的情况。

               假设我们直接将原先配置的OrderServlet和OrderServiceImpl的autowire改为byName,如下图所示 :

image.gif 编辑

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

image.gif 编辑

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

image.gif 编辑

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

image.gif 编辑

               果然如此。


九、关于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 + '\'' +
                '}';
    }
}

image.gif

               在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>

image.gif

               最后,仍然是在测试类中定义一个单元测试方法,测试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);
    }

image.gif

               运行结果 :

image.gif 编辑


十、总结

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

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

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

目录
相关文章
|
5天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
20 0
|
29天前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
36 0
|
22天前
|
Java API Spring
在 Spring 配置文件中配置 Filter 的步骤
【10月更文挑战第21天】在 Spring 配置文件中配置 Filter 是实现请求过滤的重要手段。通过合理的配置,可以灵活地对请求进行处理,满足各种应用需求。还可以根据具体的项目要求和实际情况,进一步深入研究和优化 Filter 的配置,以提高应用的性能和安全性。
|
14天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
27 1
|
3月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
5月前
|
XML Java 数据格式
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
47 1
|
5月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
62 0
|
3月前
|
XML Java 数据格式
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)
这篇文章详细介绍了Spring框架中IOC容器的Bean管理,特别是基于XML配置方式的实现。文章涵盖了Bean的定义、属性注入、使用set方法和构造函数注入,以及如何注入不同类型的属性,包括null值、特殊字符和外部bean。此外,还探讨了内部bean的概念及其与外部bean的比较,并提供了相应的示例代码和测试结果。
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)
|
3月前
|
XML Java 数据格式
Spring5入门到实战------5、IOC容器-Bean管理(三)
这篇文章深入探讨了Spring5框架中IOC容器的高级Bean管理,包括FactoryBean的使用、Bean作用域的设置、Bean生命周期的详细解释以及Bean后置处理器的实现和应用。
Spring5入门到实战------5、IOC容器-Bean管理(三)
|
3月前
|
XML Java 数据格式
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
这篇文章是Spring5框架的实战教程,主题是IOC容器中Bean的集合属性注入,通过XML配置方式。文章详细讲解了如何在Spring中注入数组、List、Map和Set类型的集合属性,并提供了相应的XML配置示例和Java类定义。此外,还介绍了如何在集合中注入对象类型值,以及如何使用Spring的util命名空间来实现集合的复用。最后,通过测试代码和结果展示了注入效果。
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)