Spring IOC—基于XML配置和管理Bean 万字详解(通俗易懂)

目录

一、前言

二、通过类型来获取Bean

        0.总述(重要) : 

        1.基本介绍 : 

        2.应用实例 : 

三、通过指定构造器为Bean注入属性

        1.基本介绍 : 

        2.应用实例 : 

四、通过p命名空间为Bean注入属性

        1.基本介绍 : 

        2.应用实例 : 

五、通过ref引用实现Bean的相互引用

        1.基本介绍 : 

        2.应用实例 : 

六、对Bean注入属性的内容延伸

        1.准备工作 : 

        2.注入List类型的属性 : 

        3.注入Set类型的属性 : 

        4.注入Map类型的属性 : 

        5.注入数组类型的属性 : 

        6.注入Properties类型的属性 : 

        7.List属性注入之通过util命名空间注入 : 

        8.级联属性注入 : 

七、通过静态工厂获取Bean

        1.基本介绍 : 

        2.应用实例 : 

八、通过实例工厂获取Bean

        1.基本介绍 : 

        2.应用实例 : 

九、通过FactoryBean获取Bean

        1.基本介绍 : 

        2.应用实例 : 

十、关于Bean配置的更多内容和细节

十一、总结


一、前言

  • 第二节内容,up打算和大家分享一下Spring IOC——基于XML方式对Bean的配置和管理。(PS: 若对XML文件未曾了解,可以去快速阅读一下up的“速通XML”一文。)
  • 注意事项——代码中的注释也很重要;不要眼高手低,自己跟着过一遍才有收获;点击文章的侧边栏目录或者文章开头的目录可以进行跳转。
  • 良工不示人以朴,所有文章都会适时补充完善。大家如果有问题都可以在评论区进行交流或者私信up。感谢阅读!

二、通过类型来获取Bean

        0.总述(重要) : 

        (1) Spring对Bean的管理主要包括两方面内容——创建bean对象为bean注入属性

        (2) Spring对Bean的配置方式也主要是两种——基于XML文件配置的方式基于注解配置的方式

        (3) 上一小节中,我们已经演示过“通过id属性来获取bean对象”的方式(基于beans.xml配置文件),因此这一小节,我们直接演示其他方式(这一小节均是基于XML文件配置的方式)。

        1.基本介绍 : 

        (1) 通过类型来获取bean对象,其实是调用了getBean(Class<T> aClass)方法,该方法与我们上一小节中演示的“通过id获取bean对象”的方法构成重载,如下图所示 : 

        (2) 通过类型来获取bean时,要求ioc容器中同一类型的bean对象只能有一个,否则会抛出异常NoUniqueBeanDefinitionException(不唯一Bean定义异常,如下图所示 : 

        (3) “通过类型获取Bean”方式的适用场景——在一个线程中只需要一个对象实例(单例)的情况,eg : XxxAction / Servlet / Controller / XxxService。

        (4) PS : 在容器配置文件(eg : beans.xml)中给bean对象的属性赋值,底层是通过setter方法完成的,因此我们定义的JavaBean(比如之前的Student)类中,必须提供相应的setter方法,否则报错。

        2.应用实例 : 

                需求 : 在beans.xml配置文件中配置一个bean对象,并通过Class类型的方式来获取到该bean对象。
                首先,我们需要创建一个JavaBean类,以Student类为例,Student类代码如下 : 

package com.cyan.spring.bean;/*** @author : Cyan_RA9* @version : 21.0*/
public class Student {private String name;private int age;private int score;//PS : 无参构造器必须给出,因为底层要通过反射来创建对象。public Student() {}public Student(String name, int age, int score) {this.name = name;this.age = age;this.score = score;}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 int getScore() {return score;}public void setScore(int score) {this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
}

                然后,在beans.xml配置文件中,配置一个Student类对象,beans.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"><!--(1) 在根元素beans中,通过<bean></bean>子元素来配置JavaBean对象。每配置一个bean,相当于配置了一个Java对象。(2) bean子元素需要配置两个属性———class 和 id。其中,class表示所要实例化类的正名(全类名);id表示该对象在Spring容器中的标识,通过id可以获取到对象。(3) property子元素用于配置该对象的成员变量(对象的属性),其中,name表示属性名称,value表示属性的值。(4) XML内容回顾———若一个标签没有标签体,以<age></age>为例,可以简写为<age/>。--><!-- 注意———此处我们没有为bean标签配置id属性,默认的分配id为:全类名#0 --><bean class="com.cyan.spring.bean.Student"><property name="name" value="Rain"></property><property name="age" value="19"></property><property name="score" value="439"></property></bean>
</beans>

                接着,在测试类中调用getBean(Class<T> aClass)方法,以StudentBeanByXML类为测试类,通过JUnit框架进行单元测试,StudentBeanByXML类代码如下 : 

package com.cyan.spring.test;import com.cyan.spring.bean.Student;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author : Cyan_RA9* @version : 21.0*/
public class StudentBeanByXML {//1.通过类型来获取Bean@Testpublic void getBeanByClass() {//(1) 获取Spring容器对象//IOC : Inversion Of ControlApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");//(2) 通过JavaBean的类型来获取beanStudent bean = ioc.getBean(Student.class);//(3) 打印出获取到的bean对象的信息System.out.println(bean);}
}

                运行结果 : 


三、通过指定构造器为Bean注入属性

        1.基本介绍 : 

        “通过指定构造器为Bean注入属性”,即在beans.xml配置文件中通过<constructor-arg/>标签来指定一个JavaBean中的带参构造,利用该带参构造来完成对Bean的属性的初始化,其配置方式本质仍然是“IOC——基于XML文件配置Bean”。如下图所示 : 

        上图中演示的为通过index索引来确定参数,还可以通过type类型或者name属性名来确定参数,本质都是根据形参来唯一确定一个带参构造

        2.应用实例 : 

                需求 : 在beans.xml文件中新配置一个Bean对象,并通过<constructor-arg/>标签指定Student类的一个带参构造来初始化该Bean的属性。在StudentBeanByXML类中新定义一个单元测试方法,通过id属性获取到该Bean对象,并检测属性注入是否成功。
                仍然使用Student类作为JavaBean类,首先我们要在beans.xml文件中配置Bean对象,up为了演示一下index, type, name三种形式的<constructor-arg/>标签,此处配置了三个Bean对象,代码如下 : 

    <bean class="com.cyan.spring.bean.Student" id="stu03"><constructor-arg value="Cyan" index="0"></constructor-arg><constructor-arg value="21" index="1"></constructor-arg><constructor-arg value="453" index="2"></constructor-arg></bean><bean class="com.cyan.spring.bean.Student" id="stu04"><constructor-arg value="Eisen" type="java.lang.String"></constructor-arg><constructor-arg value="21" type="int"></constructor-arg><constructor-arg value="442" type="int"></constructor-arg></bean><bean class="com.cyan.spring.bean.Student" id="stu05"><constructor-arg value="Five" name="name"></constructor-arg><constructor-arg value="20" name="age"></constructor-arg><constructor-arg value="460" name="score"></constructor-arg></bean>

                接着,在StudentBeanByXML类中新定义一个方法,分别获取到id = stu03, id = stu04, id = stu05的对象,insertPropertiesByConstructor()方法代码如下 : 

    //2.通过指定构造器为Bean注入属性//回顾————//对Bean的管理包括两方面 (1) 创建bean对象;(2) 为bean对象注入属性。@Testpublic void insertPropertiesByConstructor() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Student stu03 = ioc.getBean("stu03", Student.class);Student stu04 = ioc.getBean("stu04", Student.class);Student stu05 = ioc.getBean("stu05", Student.class);System.out.println("stu03 = " + stu03);System.out.println("stu04 = " + stu04);System.out.println("stu05 = " + stu05);}

                运行结果 : 

                可以看到,打印出的属性值与我们配置的一致,说明带参构造成功初始化该bean对象。


四、通过p命名空间为Bean注入属性

        1.基本介绍 : 

        前文中用<bean></bean>标签配置Bean对象时,我们用到了class属性(JavaBean的全类名),id属性(对象在容器中的标识)。现在我们可以用"p:property_name = property_value"(注意冒号)的形式,直接在bean标签内部为Bean注入属性,但直接使用会报错,如下图所示 : 

        报错提示 : "Namespace 'p' is not bound"(命名空间p未绑定)。 

        解决方法 : 将鼠标悬停在报错处,按下Alt + Enter,选择"Create namespace declaration"(创建命名空间声明),如下图所示 :

        创建命名空间声明后, 可以看到beans.xml根元素中已经自动加入了p命名空间的声明,如下图所示 : 

        2.应用实例 : 

                需求 : 在beans.xml文件中新配置一个Bean对象,并通过p命名空间为该对象注入属性。在StudentBeanByXML类中新定义一个单元测试方法,打印该对象信息,检测属性注入是否成功。
                仍然使用Student类作为JavaBean类,首先我们要在beans.xml文件中配置Bean对象,代码如下 : 

    <bean class="com.cyan.spring.bean.Student" id="stu06" p:name="Peter" p:age="33" p:score="1024"></bean>

                接着,在StudentBeanByXML测试类中新定义一个方法,获取到id = stu06的对象,injectPropertiesByP()方法代码如下 : 

    //3.通过p命名空间为Bean注入属性@Testpublic void injectPropertiesByP() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Student stu06 = ioc.getBean("stu06", Student.class);System.out.println("stu06 = " + stu06);}

                运行结果 : 


五、通过ref引用实现Bean的相互引用

        1.基本介绍 : 

        (1) 在JavaWeb中(比方说我们之前的Web项目——尘歌壶摆设购买系统),我们利用“分层设计”的思想,会分别建立DAO层,Service层,Web层;并根据“Web层调用Service层,Service层调用DAO层”的思想,在XxxServlet类中new一个XxxService对象,在XxxService类中new一个XxxDao对象。如下图所示 : 

        (2) 而有了Spring的加持后,我们可以通过ref(引用)来实现IOC容器中bean对象的相互引用,其本质是通过ref引用来为一个对象的引用类型的属性进行初始化,即属于“Bean管理——为bean注入属性”的范畴

        (3) ref在使用时,是作为property标签的一个属性(我们之前已经多次用到了property标签),格式如下 : 

        <property name="property_name" ref="otherBeans id"></property>(更多说明见beans.xml中的注释)

        2.应用实例 : 

                需求 : 参照之前JavaWeb中“分层设计”的思想,在Service层中调用DAO层,但是不直接new出实例,而是在beans.xml文件中通过ref进行配置,试着根据输出语句,测试注入属性是否成功。
                首先,我们需要创建用于测试的Service层和DAO层的类,以我们之前的尘歌壶摆设购买系统为参考,如下图所示 : 

                FurnishingDAOImpl类代码如下 : (定义了一个用于添加摆设的addFurnishing方法)

package com.cyan.spring.dao;/*** @author : Cyan_RA9* @version : 21.0*/
public class FurnishingDAOImpl {public FurnishingDAOImpl() {System.out.println("FurnishingDAOImpl的无参构造被调用~");}public void addFurnishing() {System.out.println("FurnishingDAOImpl的addFurnishing方法被调用~");}
}

                FurnishingServiceImpl类代码如下 : (定义了一个FurnishingDAOImpl类型的属性,我们将在beans.xml文件中对其进行初始化;此外,定义addFurnishing()方法,实现Service层调用DAO层)

package com.cyan.spring.service;import com.cyan.spring.dao.FurnishingDAOImpl;/*** @author : Cyan_RA9* @version : 21.0*/
public class FurnishingServiceImpl {//Service层调用DAO层(但通过beans.xml文件进行配置)private FurnishingDAOImpl furnishingDAO;public FurnishingServiceImpl() {}//About DAO's setter,getterpublic FurnishingDAOImpl getFurnishingDAO() {return furnishingDAO;}public void setFurnishingDAO(FurnishingDAOImpl furnishingDAO) {this.furnishingDAO = furnishingDAO;}//Add Furnishingpublic void addFurnishing() {System.out.println("FurnishingServiceImpl的addFurnishing()方法被调用~");furnishingDAO.addFurnishing();}
}

                在beans.xml中配置这两个对象,代码如下 : (注意看注释

    <!--(1) 配置一个FurnishingDAOImpl对象;(2) 由于该类暂时没有设置属性,所以此处不需要注入属性(下面furnishingService01同理)--><bean class="com.cyan.spring.dao.FurnishingDAOImpl" id="furnishingDAO01"/><!--(1) 配置一个FurnishingServiceImpl对象;(2) 仍然是通过property子元素来为id = furnishingService01的对象注入属性,注意,此处的"ref" 表示————id = furnishingService01的对象的furnishingDAO属性,引用了上面配置的id = furnishingDAO01的对象PS : ref体现了Spring的依赖注入(即一个对象的属性,引用了另一个对象)--><bean class="com.cyan.spring.service.FurnishingServiceImpl" id="furnishingService01"><property name="furnishingDAO" ref="furnishingDAO01"></property></bean>

                最后,仍是在StudentBeanByXML测试类中新定义一个方法,测试FurnishingServiceImpl的FurnishingDAOImpl属性是否被初始化。
                injectPropertiesByRef()方法代码如下 : 

    //4.通过ref引用实现Bean的相互引用@Testpublic void injectPropertiesByRef() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");FurnishingServiceImpl furnishingService01 = ioc.getBean("furnishingService01", FurnishingServiceImpl.class);furnishingService01.addFurnishing();}

                运行结果 : 

                可以看到,FurnishingDAOImpl类的无参构造成功被调用,说明它被成功初始化。
                其实,除了使用ref属性外,还可以直接通过配置内部bean来完成对属性的初始化,如下所示 : 

    <bean class="com.cyan.spring.service.FurnishingServiceImpl" id="furnishingService02"><property name="furnishingDAO"><bean class="com.cyan.spring.dao.FurnishingDAOImpl"/></property></bean>

                这种方法的意思是,对引用类型属性的初始化不再使用Spring容器中已经配置好的对象,而是自己重新配置一个bean。经过测试,配置内部bean的方法也可以成功注入,大家有兴趣可以自己去试试。


六、对Bean注入属性的内容延伸

        1.准备工作 : 

                上文中我们已经演示过多种为bean注入属性的方式,比如“通过指定构造器注入”,“通过p命名空间注入”,“通过ref引用实现Bean的相互引用”等。现在,让我们来讨论一下,如果bean对象的属性是数组或者集合类型,我们又该怎样去注入呢?

                为了实现“注入数组 or 集合类型的属性”,我们先来创建一个类去维护这些属性,School类代码如下 : 

package com.cyan.spring.bean;import java.util.*;/*** @author : Cyan_RA9* @version : 21.0*/
public class School {private List<Student> studentList;private Set<Student> studentSet;private Map<String, Student> studentMap;private String[] studentNames;private Properties pros;public School() {}public School(List<Student> studentList, Set<Student> studentSet, Map<String, Student> studentMap, String[] studentNames, Properties pros) {this.studentList = studentList;this.studentSet = studentSet;this.studentMap = studentMap;this.studentNames = studentNames;this.pros = pros;}public List<Student> getStudentList() {return studentList;}public void setStudentList(List<Student> studentList) {this.studentList = studentList;}public Set<Student> getStudentSet() {return studentSet;}public void setStudentSet(Set<Student> studentSet) {this.studentSet = studentSet;}public Map<String, Student> getStudentMap() {return studentMap;}public void setStudentMap(Map<String, Student> studentMap) {this.studentMap = studentMap;}public String[] getStudentNames() {return studentNames;}public void setStudentNames(String[] studentNames) {this.studentNames = studentNames;}public Properties getPros() {return pros;}public void setPros(Properties pros) {this.pros = pros;}@Overridepublic String toString() {return "School{" +"studentList=" + studentList +", studentSet=" + studentSet +", studentMap=" + studentMap +", studentNames=" + Arrays.toString(studentNames) +", pros=" + pros +'}';}
}

                接着,我们在beans.xml中配置一个School类型的Bean对象,代码如下 : 

    <bean class="com.cyan.spring.bean.School" id="school01"><!-- 暂未给属性赋值 --></bean>

        2.注入List类型的属性 : 

                在刚刚配置的id = school01的bean对象中,通过property标签为List类型的属性初始化,代码如下 : 

        <property name="studentList"><list><ref bean="stu03"></ref><ref bean="stu04"></ref></list></property>

                注入,除了"通过ref元素配置"的形式外,也可以在List元素中直接配置内部Bean。仍然是在StudentBeanByXML测试类中,我们新定义一个方法测试List属性是否注入成功,injectList()方法代码如下 : 

    //5.为Bean注入集合 or 数组类型的属性//5.1 注入List类型的属性@Testpublic void injectList() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");School school01 = ioc.getBean("school01", School.class);System.out.println("School中的studentList属性如下 : ");System.out.println(school01.getStudentList());}

                运行结果 : 

        3.注入Set类型的属性 : 

                在“准备工作”中配置的id = school01的bean对象中,通过property标签为Set类型的属性初始化,代码如下 : 

        <property name="studentSet"><set><ref bean="stu03"></ref><ref bean="stu06"></ref></set></property>

                可以看到,Set类型属性的配置和List类型属性的配置非常类似,只不过前者是放在set元素里了。仍然是在StudentBeanByXML测试类中,我们新定义一个方法测试Set属性是否注入成功,injectSet()方法代码如下 : 

        //5.2 注入Set类型的属性@Testpublic void injectSet() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");School school01 = ioc.getBean("school01", School.class);System.out.println("School中的studentSet属性如下 : ");System.out.println(school01.getStudentSet());}

                运行结果 : 

        4.注入Map类型的属性 : 

                在“准备工作”中配置的id = school01的bean对象中,通过property标签为Map类型的属性初始化,代码如下 : 

        <property name="studentMap"><map><!-- entry表示一个键值对  --><entry><!-- key元素用于配置key,注意此处的value是指key的值,字面意思 --><key><value>keyStu05</value></key><!-- 紧跟key元素其后的ref元素才是真正的当前键值对的value! --><ref bean="stu05"></ref></entry><entry><key><value>keyStu06</value></key><ref bean="stu06"></ref></entry></map></property>

                仍然是在StudentBeanByXML测试类中,我们新定义一个方法测试Map属性是否注入成功,injectMap()方法代码如下 : 

        //5.3 注入Map类型的属性@Testpublic void injectMap() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");School school01 = ioc.getBean("school01", School.class);System.out.println("School中的studentMap属性如下 : ");System.out.println(school01.getStudentMap());}

                运行结果 : 

        5.注入数组类型的属性 : 

                在“准备工作”中配置的id = school01的bean对象中,通过property标签为String[]类型的属性初始化,代码如下 : 

        <property name="studentNames"><array><!-- 由于School类中维护的数组为String类型,所以此处直接以value元素配置 --><value>Cyan</value><value>Rain</value><value>Five</value><value>Ice</value></array></property>

                仍然是在StudentBeanByXML测试类中,我们新定义一个方法测试String[]属性是否注入成功,injectArray()方法代码如下 : 

        //5.4 注入数组类型的属性@Testpublic void injectArray() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");School school01 = ioc.getBean("school01", School.class);System.out.println("School中的studentNames属性如下 : ");System.out.println(Arrays.toString(school01.getStudentNames()));}

                运行结果 : 

        6.注入Properties类型的属性 : 

                在“准备工作”中配置的id = school01的bean对象中,通过property标签为Properties类型的属性初始化,代码如下 : 

        <property name="pros"><props><prop key="username">Cyan_RA9</prop><prop key="password">55555233</prop><prop key="ip">127.0.0.1</prop></props></property>

                仍然是在StudentBeanByXML测试类中,我们新定义一个方法测试Properties属性是否注入成功,injectProperties()方法代码如下 : 

        //5.5 注入Properties类型的属性@Testpublic void injectProperties() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");School school01 = ioc.getBean("school01", School.class);System.out.println("School中的pros属性如下 : ");System.out.println(school01.getPros());}

                运行结果 : 

        7.List属性注入之通过util命名空间注入 : 

                方才我们已经见过了如何通过property元素注入List类型的属性,其实是通过list子元素来实现的。
                那么,假设现在给出一个需求:已知成华大道到二仙桥的路上开着两家书店,它们都卖《生死疲劳》《镜花缘》《湘行散记》《明朝那些事儿》《三体》这几本书,让你在beans.xml中配置这俩个书店对象,你能吗?
                你可能会说:哟瞧你这话说的,这不是张飞吃豆芽——小菜一碟么?看我一波张飞穿针——粗中有细,给你整得明明白白,服服帖帖。

                于是你就开整了,先新定义一个BookStore的JavaBean类,代码如下 : 

package com.cyan.spring.bean;import java.util.List;public class BookStore {private List<String> bookList;public BookStore() {}public BookStore(List<String> bookList) {this.bookList = bookList;}public List<String> getBookList() {return bookList;}public void setBookList(List<String> bookList) {this.bookList = bookList;}
}

                再去beans.xml文件中配置一波,代码如下 : 

    <bean class="com.cyan.spring.bean.BookStore" id="bookStore01"><property name="bookList"><list><value>生死疲劳</value><value>镜花缘</value><value>湘行散记</value><value>明朝那些事儿</value><value>三体</value></list></property></bean><bean class="com.cyan.spring.bean.BookStore" id="bookStore02"><property name="bookList"><list><value>生死疲劳</value><value>镜花缘</value><value>湘行散记</value><value>明朝那些事儿</value><value>三体</value></list></property></bean>

                你还不尽兴, 继续去测试类中定义了一个单元测试的方法,设法输出bookList属性进行检验,testMySB()方法代码如下 : 

    @Testpublic void testMySB() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");BookStore bookStore01 = ioc.getBean("bookStore01", BookStore.class);BookStore bookStore02 = ioc.getBean("bookStore02", BookStore.class);System.out.println("bookStore01's bookList = " + bookStore01.getBookList());System.out.println("bookStore02's bookList = " + bookStore02.getBookList());}

                运行结果 : 

                对此,我只能说:“你™是真🐂B呀”。是的,坦白说你做的针不戳儿。但是呢,假如现在成华大道到二仙桥的路上又开了5家书店,阁下又如何应对呢?
                你可能会想:那我再配5个Bean不就完事儿了么,这up🐖怎么磨磨唧唧的,阴阳怪气,你到底想说啥你说呗,整这么绕一大圈子。对此,我想说:“你再配5个Bean也确实能成事儿,但我说你这么配就慢了,我们不仅要配得对,而且要配得快。

                于是便要引出util命名空间了。直接上代码 : (当你使用<util:list>时,IDEA会自动帮你引入util命名空间)

    <util:list id="commonBookList"><value>生死疲劳</value><value>镜花缘</value><value>湘行散记</value><value>明朝那些事儿</value><value>三体</value></util:list><bean class="com.cyan.spring.bean.BookStore" id="bookStore01"><property name="bookList" ref="commonBookList"></property></bean><bean class="com.cyan.spring.bean.BookStore" id="bookStore02"><property name="bookList" ref="commonBookList"></property></bean>

                可以看到,其实就是将大家都有的📕放到了<util:list></util:list>元素中,然后在每个BookStore类型的Bean对象中,利用ref引用到<util:list>中。经测试,输出结果是一样的,这里就不再放图了。

        8.级联属性注入 : 

                所谓“级联属性注入”,其实指的是Spring的IOC容器可以直接为“属性的属性”赋值,即当类中的某个属性有自己的属性时,我们希望在配置该类Bean对象时将对象属性和对象属性的属性都注入。

                需求 : 定义一个员工类,维护员工id,员工姓名,员工的部门名称三个属性,其中,员工的部门名称通过部门类的deptName属性表示。要求设法实现“级联属性注入”。
                首先,我们需要定义员工类和部门类,如下 : 
                Employee类代码如下 : 

package com.cyan.spring.bean;public class Employee {private Integer id;private String name;private Department department;public Employee() {}public Employee(Integer id, String name, Department department) {this.id = id;this.name = name;this.department = department;}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 Department getDepartment() {return department;}public void setDepartment(Department department) {this.department = department;}@Overridepublic String toString() {return "Employee{" +"id=" + id +", name='" + name + '\'' +", department=" + department +'}';}
}

                Department类代码如下: 

package com.cyan.spring.bean;public class Department {private String deptName;public Department() {}public Department(String deptName) {this.deptName = deptName;}public String getDeptName() {return deptName;}public void setDeptName(String deptName) {this.deptName = deptName;}@Overridepublic String toString() {return "Department{" +"deptName='" + deptName + '\'' +'}';}
}

                接着,在beans.xml文件中配置Employee对象和Department对象。代码如下 : 

    <!-- 配置Employee类对象 --><bean class="com.cyan.spring.bean.Employee" id="employee01"><property name="id" value="1"></property><property name="name" value="Cyan"></property><property name="department" ref="department01"></property><!-- [级联属性注入] --><!-- 其实是通过"对象名.属性名"的形式,对属性的属性进行注入操作 --><property name="department.deptName" value="Back-End"></property><!-- 此处,底层实际调用了setDeptName方法 --></bean><!-- 配置Department类对象 --><bean class="com.cyan.spring.bean.Department" id="department01"/>

                最后,在测试类StudentBeanByXML中新定义一个单元测试方法,输出配置的Employee对象,查看级联属性注入是否成功。testCascade()方法代码如下 : 

    //PS : 测试级联属性赋值@Testpublic void testCascade() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Employee employee01 = ioc.getBean("employee01", Employee.class);System.out.println("employee01 = \n" + employee01);}

                运行结果 : 

                可以看到,employee对象的属性department的属性deptName被成功初始化,说明级联属性注入成功。


七、通过静态工厂获取Bean

        1.基本介绍 : 

        “通过静态工厂获取Bean”,本质上还是“基于XML方式配置Bean”;只不过我们并不直接在配置Bean时就为Bean对象注入属性,而是事先在静态工厂类的static代码块中完成了初始化(用静态的Map容器来存储Student类对象),并提供了一个静态方法用于获取已经初始化好的Student对象,然后在beans.xml配置文件中,我们只需要给定一个key,并指定调用静态工厂提供的用于获取Student对象的方法,Spring容器便可以根据该key获取到对应的Student类对象。

        注意 : 

        (1) “通过静态工厂获取Bean”,在配置bean时,class不再是Student类的全路径,而是静态工厂类的全路径.

        (2) 除了id和class外,还需要一个属性factory-method,表示指定一个静态工厂类的用于返回Bean对象的方法.

        (3) 至于bean元素内部,则需要使用<construcotr-arg value="key"/>标签,说明要获取的对象在静态工厂类中对应的key

        2.应用实例 : 

                上面说了一堆,只是看肯定多少觉得一头雾水,下面我们来个实例感受一下。
                首先,up定义一个自己的静态工厂类,CyanStaticFactory类代码如下 : 

package com.cyan.spring.factory;import com.cyan.spring.bean.Student;import java.util.HashMap;
import java.util.Map;public class CyanStaticFactory {//维护一个静态Map集合,用于保存Student对象private static Map<String, Student> studentMap;//在静态代码块中初始化Student对象static {studentMap = new HashMap<>();studentMap.put("student01", new Student("Cyan", 21, 450));studentMap.put("student02", new Student("Rain", 19, 460));}//提供一个获取Student对象的静态方法public static Student getStudent(String key) {return studentMap.get(key);}
}

                然后在beans.xml文件中完成配置,代码如下 : (注意看up配置的id,表明最终返回的其实是一个Student类对象)

    <bean class="com.cyan.spring.factory.CyanStaticFactory" id="stu07"factory-method="getStudent"><constructor-arg value="student02"/></bean>

                最后,仍然是在测试类StudentBeanByXML中,定义一个单元测试方法,获取到Student对象,
                getBeanByStaticFactory()方法代码如下 : (注意此处getBean方法得到的是Student类型的对象)

    //6.通过静态工厂获取Bean@Testpublic void getBeanByStaticFactory() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Student stu07 = ioc.getBean("stu07", Student.class);System.out.println(stu07);}

                运行结果 : 


八、通过实例工厂获取Bean

        1.基本介绍 : 

        “通过实例工厂获取Bean”,和通过静态工厂获取Bean类似,只不过见名知意,我们在自定义实例工厂类中通过非静态Map容器来保存Student类对象,并在非静态代码块中对Map容器进行初始化,并提供一个用于获取Student对象的非静态方法。然后在beans.xml文件中,除了指定一个用于获取Bean对象的方法,以及给出key外,必须先指定一个实例工厂对象

        回顾一下代码块——

        静态代码块随着类的加载而被隐式地调用,最多只能执行一次而对于非静态代码块,每实例化一次包含该非静态代码块的类,都会执行一次该类中的非静态代码块

        结合代码块的内容回顾,我们可以猜到必须先实例化“实例工厂对象”,以执行其非静态代码块中的内容,完成对非静态Map集合的初始化;然后才能获取到其保存的学生对象

        注意 : 

        (1) “通过实例工厂获取Bean”,在配置bean时,需要同时配置实例工厂对象和学生对象.

        (2) 属性factory-method,表示指定一个实例工厂类的用于返回Bean对象的方法;属性factory-bean,表示指定使用一个特定的实例工厂对象返回Bean

        (3) bean元素内部,仍需要使用<construcotr-arg value="key"/>标签,说明要获取的对象在实例工厂类中对应的key

        2.应用实例 : 

                首先,up定义一个自己的实例工厂类,CyanInstanceFactory类代码如下 : 

package com.cyan.spring.factory;import com.cyan.spring.bean.Student;import java.util.HashMap;
import java.util.Map;public class CyanInstanceFactory {private Map<String, Student> studentMap;{studentMap = new HashMap<>();studentMap.put("student03", new Student("Eisen", 22, 437));studentMap.put("student04", new Student("Five", 20, 429));}public Student getStudent(String key) {return studentMap.get(key);}
}

                然后在beans.xml文件中完成配置,代码如下 :

    <!-- 配置实例工厂对象 --><bean class="com.cyan.spring.factory.CyanInstanceFactory" id="cyanInstanceFactory01"/><!-- 配置学生对象 --><bean id="stu08" factory-bean="cyanInstanceFactory01" factory-method="getStudent"><constructor-arg value="student03"/></bean>

                最后,仍然是在测试类StudentBeanByXML中,定义一个单元测试方法,获取到Student对象,
                getBeanByInstanceFactory()方法代码如下 :

    //7.通过实例工厂获取Bean@Testpublic void getBeanByInstanceFactory() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Student stu08 = ioc.getBean("stu08", Student.class);System.out.println(stu08);}

                运行结果 : 


九、通过FactoryBean获取Bean

        1.基本介绍 : 

        “通过FactoryBean获取Bean”,又和上文中通过实例工厂获取Bean类似,都是维护了一个非静态Map容器来保存Bean对象,并在非静态代码块中对Map容器进行初始化,此外,还需要单独维护一个String类型的key属性。并且,我们需要去实现FactoryBean<>接口,并重写接口中的方法

        注意 : 

        (1) “通过FactoryBean获取Bean”,在配置bean时,class为FactoryBean的全类名.

        (2) 通过property子元素为key属性初始化

        2.应用实例 : 

                首先,我们需要定义一个自己的FactoryBean类并实现FactoryBean接口,CyanFactoryBean类代码如下 : 

package com.cyan.spring.factory;import com.cyan.spring.bean.Student;
import org.springframework.beans.factory.FactoryBean;import java.util.HashMap;
import java.util.Map;public class CyanFactoryBean implements FactoryBean<Student> {private String key;private Map<String, Student> studentMap;{studentMap = new HashMap<>();studentMap.put("student05", new Student("Irving", 32, 427));studentMap.put("student06", new Student("Rose", 23, 431));}public void setKey(String key) {this.key = key;}@Overridepublic Student getObject() throws Exception {return studentMap.get(key);}@Overridepublic Class<?> getObjectType() {return Student.class;}@Overridepublic boolean isSingleton() {return FactoryBean.super.isSingleton();}
}

                接着,在beans.xml中配置bean对象,代码如下 : 

    <!-- 配置Student对象,通过FactoryBean获取 --><bean class="com.cyan.spring.factory.CyanFactoryBean" id="stu09"><property name="key" value="student05"/></bean>

                最后,在StudentBeanByXML测试类中定义一个单元测试方法,测试是否配置成功。getBeanByFactoryBean()方法代码如下 : 

    //8.通过实例工厂获取Bean@Testpublic void getBeanByFactoryBean() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Student stu09 = ioc.getBean("stu09", Student.class);System.out.println(stu09);}

                运行结果 : 


十、关于Bean配置的更多内容和细节

        由于“Spring IOC—基于XML配置和管理Bean”内容较多,而up写到这里时编辑器已经很卡了😂。故打算将Bean配置信息重用,Bean生命周期,以及Bean后置处理器等内容单独放一篇文章中。

        链接如下 : 

        待更新---🕊🕊


十一、总结

  •  🆗,以上就是Spring系列博文第二小节的全部内容了。
  • 总的来看,Spring 基于XML配置和管理Bean内容很多,我们可以通过多种方式获取Bean或者为Bean注入属性,足以感受到Spring配置和管理Bean的灵活性。再来简单回顾一下上文的总述,如下图所示 :

  • 下一节内容——Spring IOC——基于注解配置和管理Bean感谢阅读!

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/194076.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

吸烟(抽烟)检测和识别2:Pytorch实现吸烟(抽烟)检测和识别(含吸烟(抽烟)数据集和训练代码)

吸烟(抽烟)检测和识别2&#xff1a;Pytorch实现吸烟(抽烟)检测和识别(含吸烟(抽烟)数据集和训练代码) 目录 吸烟(抽烟)检测和识别2&#xff1a;Pytorch实现吸烟(抽烟)检测和识别(含吸烟(抽烟)数据集和训练代码) 1.吸烟(抽烟)检测和识别 2.吸烟(抽烟)数据集 &#xff08;1&am…

c++中函数的引用

函数中的引用 引用可以作为函数的形参 不能返回局部变量的引用 #include<iostream> #include<stdlib.h> using namespace std; //形参是引用 void swap(int *x, int *y)//*x *y表示对x y取地址 { int tmp *x; *x *y; *y tmp; } void test01() { …

用两个栈实现队列

目录 一、栈的基本结构及其接口 二、我的队列结构定义 三、我的队列创建及其初始化 四、我的队列入队 五、我的队列出队 六、我的队列取队头元素 七、我的队列判空 八、我的队列销毁 一、栈的基本结构及其接口 //栈的结构定义 typedef int STDataType;typedef struct St…

OpenGL ES入门教程(三)之为平面桌子添加混合色

OpenGL ES入门教程&#xff08;三&#xff09;之为平面桌子添加渐变色 前言零、OpenGL ES实现混合色的原理一、修改绘制的桌子结构1. 三角形扇介绍2. 基于三角形扇结构绘制平面桌子 二、为每个顶点添加颜色属性三、修改着色器1. 顶点着色器2. 片段这色器 四、绘制具有混合颜色的…

7.24 SpringBoot项目实战【审核评论】

文章目录 前言一、编写控制器二、编写服务层三、Postman测试前言 我们在 上文 7.23 已经实现了 评论 功能,本文我们继续SpringBoot项目实战 审核评论 功能。逻辑如下: 一是判断管理员权限,关于角色权限校验 在 7.5 和 7.6 分别基于 拦截器Interceptor 和 切面AOP 都实现过…

Qt OpenCV 学习(二):两个简单图片识别案例

1. 寻找匹配物体 1.1 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <opencv2/opencv.hpp>#include <QImage> #include <QString> #include <QPixmap>QT_BEGIN_NAMESPACE namespace Ui { class Main…

TimeGPT:时序预测领域终于迎来了第一个大模型

时间序列预测领域在最近的几年有着快速的发展&#xff0c;比如N-BEATS、N-HiTS、PatchTST和TimesNet。 大型语言模型(llm)最近在ChatGPT等应用程序中变得非常流行&#xff0c;因为它们可以适应各种各样的任务&#xff0c;而无需进一步的训练。 这就引出了一个问题:时间序列的…

速达软件全系产品存在任意文件上传漏洞 附POC

@[toc] 速达软件全系产品存在任意文件上传漏洞 附POC 免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。…

基于Java SSM框架+Vue实现药品保健品购物网站项目【项目源码+论文说明】

基于java的SSM框架Vue实现药品保健品购物网站演示 摘要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 ssm药源购物网站&#xff0c;主要的模块包括两个用户&#xff0c;管理员权限&#xff1a;用…

关于我离破500粉丝感受

嘿嘿快破500粉丝啦&#xff0c;加油喔&#xff0c;感谢支持 首先&#xff0c;恭喜我在CSDN上的粉丝数量即将突破500大关&#xff01;这说明你在这个平台上的内容受到了很多人的关注和认可。 1. 保持高质量的内容输出&#xff1a;粉丝数量的增长与你在CSDN上发布的内容质量密切…

【数据库】基于封锁的数据库调度器,以及等待锁处理的优先级策略

封锁调度器的体系结构 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会…

分享一个国内可用的免费AI-GPT网站

背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具&#xff0c;近期的热度直接沸腾&#x1f30b;。 我们也忍不住做了一个基于ChatGPT的网站&#xff0c;可以免登陆&#xff01;&#xff01;国内可直接对话AI&#xff0c;也有各种提供工作效率的工具供大家使用。 可以这…

27、数据存储秒表(定时器扫描按键数码管)

数据存储 main.c #include <REGX52.H> #include "LCD1602.h" #include "Key.h" #include "AT24C02.h" #include "Delay.h"unsigned char KeyNum; unsigned int Num;void main() {LCD_Init();LCD_ShowNum(1,1,Num,5);while(1){…

Vue中下载不同文件的几种方式

当在Vue中需要实现文件下载功能时&#xff0c;我们可以有多种方式来完成。下面将介绍五种常用的方法。 1. 使用window.open方法下载文件 <template><div><button click"downloadFile(file1.pdf)">下载文件1</button><button click"…

Spring Cloud Alibaba简介

1、简介 Spring Cloud阿里(https://sca.aliyun.com/en-us/)为分布式应用开发提供一站式解决方案。它包含开发分布式应用程序所需的所有组件&#xff0c;使您可以轻松地使用Spring Cloud开发应用程序。 有了Spring Cloud阿里&#xff0c;你只需要添加一些注释和少量的配置&#…

vue2框架简易版响应式设计(观察者模式)

对于vue.js中的属性值我们要格外关注&#xff1a; $attrs 获取当前传递的参数 $listeners 获取当前组件的自定义事件 $children 获取当前组件所有子组件 $parent 获取当前组件所有父组件 $options 获取当前vue实例参数信息 $refs 获取ref所有的引用节点 设计原则&#x…

day4 节点两两交换

ListNode* swapPairs(ListNode* head) { ListNode* dummyHead new ListNode(0); // 设置一个虚拟头结点 dummyHead->next head; // 将虚拟头结点指向head&#xff0c;这样方便后面做删除操作 ListNode* cur dummyHead; while(cur->next ! nullptr && cur->…

编译原理头歌实验:实验4《算符优先分析法设计与实现》(C语言版)

任务描述 本关任务&#xff1a;加深对语法分析器工作过程的理解&#xff1b;加强对算符优先分析法实现语法分析程序的掌握&#xff1b;能够采用一种编程语言实现简单的语法分析程序&#xff1b;能够使用自己编写的分析程序对简单的程序段进行语法翻译。 相关知识 为了完成本…

【大连民族大学C语言CG题库练习题】——猪的安家问题

【问题描述】 Andy和Mary养了很多猪。他们想要给猪安家。但是Andy没有足够的猪圈&#xff0c;很多猪只能够在一个猪圈安家。举个例子&#xff0c;假如有16头猪&#xff0c;Andy建了3个猪圈&#xff0c;为了保证公平&#xff0c;剩下1头猪就没有地方安家了。Mary生气了&#xf…

(一)WtBtRunner回测大体流程

WtBtRunner是回测的主程序&#xff0c;启动改程序时&#xff0c;会进行配置文件和dll的加载 解析参数&#xff0c;由cppcli::Option完成参数解析初始化日志打印 std::string filename;if (lParam->exists())filename lParam->get<std::string>();elsefilename …