Spring IOC—基于XML配置和管理Bean 万字详解(通俗易懂)
目录
一、前言
- 第二节内容,up打算和大家分享一下Spring IOC——基于XML方式对Bean的配置和管理。(PS: 若对XML文件未曾了解,可以去快速阅读一下up的“ ”一文。)
- 注意事项——①代码中的注释也很重要;②不要眼高手低,自己跟着过一遍才有收获;③点击文章的侧边栏目录或者文章开头的目录可以进行跳转。
- 良工不示人以朴,所有文章都会适时补充完善。大家如果有问题都可以在评论区进行交流或者私信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类代码如下 :
然后,在beans.xml配置文件中,配置一个Student类对象,beans.xml代码如下 :
接着,在测试类中调用getBean(Class<T> aClass)方法,以StudentBeanByXML类为测试类,通过JUnit框架进行单元测试,StudentBeanByXML类代码如下 :
运行结果 :
三、通过指定构造器为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对象,代码如下 :
接着,在StudentBeanByXML类中新定义一个方法,分别获取到id = stu03, id = stu04, id = stu05的对象,insertPropertiesByConstructor()方法代码如下 :
运行结果 :
可以看到,打印出的属性值与我们配置的一致,说明带参构造成功初始化该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对象,代码如下 :
接着,在StudentBeanByXML测试类中新定义一个方法,获取到id = stu06的对象,injectPropertiesByP()方法代码如下 :
运行结果 :
五、通过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方法)
FurnishingServiceImpl类代码如下 : (定义了一个FurnishingDAOImpl类型的属性,我们将在beans.xml文件中对其进行初始化;此外,定义addFurnishing()方法,实现Service层调用DAO层)
在beans.xml中分别配置这两个类的对象,代码如下 : (注意看注释)
最后,仍是在StudentBeanByXML测试类中新定义一个单元测试方法,测试FurnishingServiceImpl的FurnishingDAOImpl属性是否被初始化。
injectPropertiesByRef()方法代码如下 :
运行结果 :
可以看到,FurnishingDAOImpl类的无参构造成功被调用,说明它被成功初始化。
其实,除了使用ref属性外,还可以直接通过配置内部bean来完成对属性的初始化,如下所示 :
这种方法的意思是,对引用类型属性的初始化不再使用Spring容器中已经配置好的对象,而是自己重新配置一个bean。经过测试,配置内部bean的方法也可以成功注入,大家有兴趣可以自己去试试。
六、对Bean注入属性的内容延伸
1.准备工作 :
上文中我们已经演示过多种为bean注入属性的方式,比如“通过指定构造器注入”,“通过p命名空间注入”,“通过ref引用实现Bean的相互引用”等。现在,让我们来讨论一下,如果bean对象的属性是数组或者集合类型,我们又该怎样去注入呢?
为了实现“注入数组 or 集合类型的属性”,我们先来创建一个类去维护这些属性,School类代码如下 :
接着,我们在beans.xml中配置一个School类型的Bean对象,代码如下 :
2.注入List类型的属性 :
在刚刚配置的id = school01的bean对象中,通过property标签为List类型的属性初始化,代码如下 :
注意,除了"通过ref元素配置"的形式外,也可以在List元素中直接配置内部Bean。仍然是在StudentBeanByXML测试类中,我们新定义一个方法测试List属性是否注入成功,injectList()方法代码如下 :
运行结果 :
3.注入Set类型的属性 :
在“准备工作”中配置的id = school01的bean对象中,通过property标签为Set类型的属性初始化,代码如下 :
可以看到,Set类型属性的配置和List类型属性的配置非常类似,只不过这里是放在set元素里了。仍然是在StudentBeanByXML测试类中,我们新定义一个方法测试Set属性是否注入成功,injectSet()方法代码如下 :
运行结果 :
4.注入Map类型的属性 :
在“准备工作”中配置的id = school01的bean对象中,通过property标签为Map类型的属性初始化,代码如下 :
仍然是在StudentBeanByXML测试类中,我们新定义一个方法测试Map属性是否注入成功,injectMap()方法代码如下 :
运行结果 :
5.注入数组类型的属性 :
在“准备工作”中配置的id = school01的bean对象中,通过property标签为String[]类型的属性初始化,代码如下 :
仍然是在StudentBeanByXML测试类中,我们新定义一个方法测试String[]属性是否注入成功,injectArray()方法代码如下 :
运行结果 :
6.注入Properties类型的属性 :
在“准备工作”中配置的id = school01的bean对象中,通过property标签为Properties类型的属性初始化,代码如下 :
仍然是在StudentBeanByXML测试类中,我们新定义一个方法测试Properties属性是否注入成功,injectProperties()方法代码如下 :
运行结果 :
7.List属性注入之通过util命名空间注入 :
方才我们已经见过了如何通过property元素注入List类型的属性,其实是通过list子元素来实现的。
那么,假设现在给出一个需求:已知成华大道到二仙桥的路上开着两家书店,它们都卖《生死疲劳》《镜花缘》《湘行散记》《明朝那些事儿》《三体》这几本书,让你在beans.xml中配置这俩个书店对象,你能吗?
你可能会说:哟瞧你这话说的,这不是张飞吃豆芽——小菜一碟么?看我一波张飞穿针——粗中有细,给你整得明明白白,服服帖帖。
于是你就开整了,先新定义一个BookStore的JavaBean类,代码如下 :
再去beans.xml文件中配置一波,代码如下 :
你还不尽兴, 继续去测试类中定义了一个单元测试的方法,设法输出bookList属性进行检验,testMySB()方法代码如下 :
运行结果 :
对此,我只能说:“你™是真🐂B呀”。是的,坦白说你做的针不戳儿。但是呢,假如现在成华大道到二仙桥的路上又开了5家书店,阁下又如何应对呢?
你可能会想:那我再配5个Bean不就完事儿了么,这up🐖怎么磨磨唧唧的,阴阳怪气,你到底想说啥你说呗,整这么绕一大圈子。对此,我想说:“你再配5个Bean也确实能成事儿,但我说你这么配就慢了,我们不仅要配得对,而且要配得快。
于是便要引出util命名空间了。直接上代码 : (当你使用<util:list>时,IDEA会自动帮你引入util命名空间)
可以看到,其实就是将大家都有的📕放到了<util:list></util:list>元素中,然后在每个BookStore类型的Bean对象中,利用ref引用到<util:list>中。经测试,输出结果是一样的,这里就不再放图了。
8.级联属性注入 :
所谓“级联属性注入”,其实指的是Spring的IOC容器可以直接为“属性的属性”赋值,即当类中维护的某个属性又维护有自己的属性时,我们希望在配置该类Bean对象时将对象属性和对象属性的属性都注入。
需求 : 定义一个员工类,维护员工id,员工姓名,员工的部门名称三个属性,其中,员工的部门名称通过部门类的deptName属性表示。要求设法实现“级联属性注入”。
首先,我们需要定义员工类和部门类,如下 :
Employee类代码如下 :
Department类代码如下:
接着,在beans.xml文件中配置Employee对象和Department对象。代码如下 :
最后,在测试类StudentBeanByXML中新定义一个单元测试方法,输出配置的Employee对象,查看级联属性注入是否成功。testCascade()方法代码如下 :
运行结果 :
可以看到,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类代码如下 :
然后在beans.xml文件中完成配置,代码如下 : (注意看up配置的id,表明最终返回的其实是一个Student类对象)
最后,仍然是在测试类StudentBeanByXML中,定义一个单元测试方法,获取到Student对象,
getBeanByStaticFactory()方法代码如下 : (注意此处getBean方法得到的是Student类型的对象)
运行结果 :
八、通过实例工厂获取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类代码如下 :
然后在beans.xml文件中完成配置,代码如下 :
最后,仍然是在测试类StudentBeanByXML中,定义一个单元测试方法,获取到Student对象,
getBeanByInstanceFactory()方法代码如下 :
运行结果 :
九、通过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类代码如下 :
接着,在beans.xml中配置bean对象,代码如下 :
最后,在StudentBeanByXML测试类中定义一个单元测试方法,测试是否配置成功。getBeanByFactoryBean()方法代码如下 :
运行结果 :
十、关于Bean配置的更多内容和细节
由于“Spring IOC—基于XML配置和管理Bean”内容较多,而up写到这里时编辑器已经很卡了😂。故打算将Bean配置信息重用,Bean生命周期,以及Bean后置处理器等内容单独放一篇文章中。
链接如下 :
待更新---🕊🕊2023/12/9 已更新(点击下方链接跳转)
十一、总结
- 🆗,以上就是Spring系列博文第二小节的全部内容了。
- 总的来看,Spring 基于XML配置和管理Bean内容很多,我们可以通过多种方式获取Bean或者为Bean注入属性,足以感受到Spring配置和管理Bean的灵活性。再来简单回顾一下上文的总述,如下图所示 :
- 下一节内容——Spring IOC——基于注解配置和管理Bean。感谢阅读!
System.out.println("END-------------------------------------------------");
- 点赞
- 收藏
- 关注作者
评论(0)