🥰🥰六千字长文带你体验Spring6中11种IOC控制反转实现方式
建议
在阅读本文之前,作者建议先阅读这篇文章以打牢Spring6基础,文章链接:juejin.cn/post/720818…
接下来我们要学习一种新容器:IoC容器,即控制反转,我们在spring
里面通过IoC容器
管理java
对象的实例化和初始化,我们将由IoC容器管理的java对象
称为Spring Bean
,这一点在和我们学javase
中使用new
来创建对象没有任何区别。(只是为了区分,在spring
里面叫IoC
罢了😉)
接下来我们要讲解一下什么是依赖注入,常见的依赖注入包括如下两种方式
set
注入- 构造注入
我们在com.atguigu.spring6
包下新建一个Person
类,在User类中注入Person
的属性,名为person
。
public class User {
private String name ;
private Person person ;
...
}
这里是初步实现注入的思想,后续我们会在spring
源码部分进行详细讲解
为了对xml
以及maven
进行统一管理,我们将spring-first
工程里面的maven
坐标统一迁移到父工程Spring6
中去
📌最后不要忘记刷新maven
紧接着我们在Spring6
中创建一个子工程,名为spring6-ioc-xml
,继承父工程,JDK
版本选择17,依旧是maven
版本。
初步配置见下图,不再赘述(bean.xml
未进行配置)
接着我们将log4j2.xml
从spring-first
工程中复制到spring6-ioc-xml
下,并放入相对应的位置(如果不知道在哪里的请回看spring-first
的创建过程☹)
我们在com.atguigu.spring6.iocxml
包下新建一个class
文件名为TestUser
作为测试类进行测试。
然后我们对bean.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>">
<!--user对象创建-->
<bean id="user" class="com.atguigu.spring6.iocxml.User"></bean>
</beans>
首先我们通过id
来获取bean
,TestUser
类配置内容如下
运行结果如下
第二种方式:我们可以根据类型获取bean
User user2 = context.getBean(User.class);
System.out.println("根据类型获取bean:" + user2);
输出的结果与第一种方式无异
第三种方式:根据id和类型获取bean
User user3 = context.getBean("user", User.class);
System.out.println("根据id和类型获取bean:" + user3);
输出的结果与第一、二种方式无异
📌当我们根据类型获取bean
的时候,要求我们的IOC
容器中指定类型的bean
只能有一个
当我们在bean.xml
中写入如下配置
<!--user对象创建-->
<bean id="user" class="com.atguigu.spring6.iocxml.User"></bean>
<bean id="user1" class="com.atguigu.spring6.iocxml.User"></bean>
我们在TestUser
类中启动相关测试方法的时候,会发现报如下错误
Exception in thread "main" org.springframework.beans.factory.
NoUniqueBeanDefinitionException:
No qualifying bean of type 'com.atguigu.spring6.iocxml.User' available:
expected single matching bean but found 2: user,user1
这句话很明显的告诉我们:没有为com.atguigu.spring6.iocxml.User
匹配到一个合适的对象,Spring6
期望我们提供一个单实例的对象,但是我们却提供了两个,一个叫user
,一个叫user1
,因此报错。
接下来我们通过接口的方式去实现类获取的过程,首先我们在com.atguigu.spring6.iocxml
包下新建一个package
名为bean
,分别建立UserDao
接口以及UserDaoImpl
实现类,内容如下
package com.atguigu.spring6.iocxml.bean;
public interface UserDao {
public void run();
}
package com.atguigu.spring6.iocxml.bean;
public class UserDaoImpl implements UserDao{
@Override
public void run() {
System.out.println("run.......");
}
}
接着我们去配置一下bean.xml
文件,我们同样是通过id
和class
属性去获取我们所要得到的实现类,以验证IOC
反转控制的思想
<!--一个接口实现类获取过程-->
<bean id="UserDaoImpl" class="com.atguigu.spring6.iocxml.bean.UserDaoImpl"></bean>
最后我们在package
包下新建一个测试类名为TestUserDao
,写入下方代码
package com.atguigu.spring6.iocxml.bean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUserDao {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 根据类型获取接口对应bean
UserDao userDao = context.getBean(UserDao.class);
System.out.println(userDao);
userDao.run();
}
}
最终我们打印输出的结果在控制台有所显示:
接下来我们实现一个接口对应多个实现类的业务场景。我们在
bean
下新建一个类叫PersonDaoImpl
,并将它继承于UserDao
接口。
package com.atguigu.spring6.iocxml.bean;
public class PersonDaoImpl implements UserDao{
@Override
public void run() {
System.out.println("person run.....");
}
}
然后我们去bean.xml
中进行配置接口所对应的bean
标签
<!--一个接口实现多个类的过程-->
<bean id="PersonDaoImpl" class="com.atguigu.spring6.iocxml.bean.PersonDaoImpl"></bean>
最后我们再对TestUserDao
进行测试,报错信息如下
Exception in thread "main" org.springframework.beans.factory.
NoUniqueBeanDefinitionException:
No qualifying bean of type 'com.atguigu.spring6.iocxml.bean.UserDao' available:
expected single matching bean but found 2: UserDaoImpl,PersonDaoImpl
很明显,报错原因是因为bean
不唯一。
接下来我们通过set
方法注入。首先我们在com.atguigu.spring6.iocxml
下新建一个package
名为di
,新建一个类名为Book
,内容如下
package com.atguigu.spring6.iocxml.di;
public class Book {
private String bname ;
private String author ;
public String getBname() {
return bname;
}
public void setBname(String bname) {
this.bname = bname;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public static void main(String[] args) {
// set方法注入
Book book = new Book();
book.setBname("java");
book.setAuthor("尚硅谷");
}
}
根据上方代码块我们可以得知,我们通过setter
的途径去对Book
对象注入相关属性,这是我们最常见的set
注入,接下来我们另一种set
注入方法。
首先先对Book
类添加相关构造器
public Book() {
}
public Book(String bname, String author) {
this.bname = bname;
this.author = author;
}
通过New
对象的方式进行注入
// 通过构造器注入
Book book1 = new Book("c++","尚硅谷");
接下来介绍Spring6
中特有的set
注入方法。我们在resources
下新建一个xml
名为bean-di
,具体配置内容见下方代码块
<!--1.set方法注入-->
<bean id="book" class="com.atguigu.spring6.iocxml.di.Book">
<property name="bname" value="前端开发"></property>
<property name="author" value="尚硅谷"></property>
</bean>
在这个xml
中,我们引入一个新的标签名为property
,其中name
属性的值与你在Book
中写入的变量有关,value
的值即为上文set
方法注入的属性拥有同等效力。
然后我们在Book
类中写入toString()
方法,用于打印输出相关信息。接着我们在di
中写入测试类,相关测试信息与之前无异,读者仿照前文自行写入测试类即可,测试结果如下
接下来我们尝试一下构造器注入
首先我们去xml
中进行配置
<!--2.构造器注入-->
<bean id="bookCon" class="com.atguigu.spring6.iocxml.di.Book">
<constructor-arg name="bname" value="java开发"></constructor-arg>
<constructor-arg name="author" value="尚硅谷"></constructor-arg>
</bean>
在上方代码块中,我们引入了一个新标签名为constructor-arg
,该bean
标签有两个属性,与property
一致。然后我们在Book的含参构造器中写入如下输出语句
System.out.println("有参数构造执行了.....");
接着我们在TestBook
中写入测试类,原理与上文一致,读者自行编写测试类即可,测试结果如下
📌我们在xml
中构造bean
标签中constructor-arg
的时候,它还有一个属性叫作index
,读者可自行摸索并写出测试方法,测试结果与上图一致。
接下来我们要对一个特殊值:null进行处理。首先我们在Book
中新建变量others
使其私有化,属性为String
,为它生成set
方法以及在Book
内重新生成toString()
。接着我们在xml
文件中手写property
,内容见下
<property name="others"><null/></property>
这一方面很好的处理value
值为null
的情况。
有时候我们会在xml
文件中写入特殊符号,比如<
和>
,此时我们就需要特殊转义符号来进行处理。
<property name="others" value="<"></property>
最后一种情况,有时候我们想在xml
文件中注入纯文本,此时我们需要用到CDATA节
,具体使用方法见下
<property name="others">
<value><![CDATA[a < b]]></value>
</property>
要想为对象类型属性赋值,我们首先得做一下准备工作。首先我们要在com.atguigu.spring6.iocxml
包下新建一个package
名为ditest
,分别新建类名为Dept
和Emp
两个文件,分别写入如下内容
package com.atguigu.spring6.iocxml.ditest;
public class Dept {
private String dname;
public void info(){
System.out.println("部门名称:" + dname);
}
}
package com.atguigu.spring6.iocxml.ditest;
public class Emp {
// 员工属于某个部门
private Dept dept;
private String ename;
private Integer age;
public void work(){
System.out.println("emp is working....." + age);
}
}
接下来我们分别在两个类中注入get
和set
方法,并且在Emp
中将work
方法改写为
public void work(){
System.out.println("emp is working....." + age);
dept.info();
}
接着我们新建一个xml
名为bean-ditest
,在xml
中,我们先对Dept
进行普通的属性注入,id
值均为类名小写,property
值见下
<property name="dname" value="安保部"></property>
和上述操作一样,我们同样为Emp
注入相关属性
<property name="ename" value="lucy"></property>
<property name="age" value="18"></property>
接下来我们要注入对象类型的属性,写法如下
<property name="dept" ref="dept"></property>
ref
代表引入的意思,表示引入我们的对象,这个对象与Dept
进行属性注入的时候的id
值拥有同等效力。我们在ditest
下新建一个测试类名为TestEmp
,读者对Emp
自行编写测试方法并且调用work
方法,结果如下
接下来我们使用内部bean
的方式进行注入,首先编写我们的xml
文件,内容如下
<!--第二种方式:内部bean做注入-->
<bean id="dept2" class="com.atguigu.spring6.iocxml.ditest.Dept">
<property name="dname" value="财务部"></property>
</bean>
<bean id="emp2" class="com.atguigu.spring6.iocxml.ditest.Emp">
<!--普通属性注入-->
<property name="ename" value="mary"></property>
<property name="age" value="20"></property>
<!--使用内部bean的方式进行注入-->
<property name="dept">
<bean id="dept2" class="com.atguigu.spring6.iocxml.ditest.Dept">
<property name="dname" value="财务部"></property>
</bean>
</property>
</bean>
在xml
中我们可以看到,我们使用内部bean
进行了注入,具体操作是在property
中注入bean
,这也是为什么叫内部bean
的原因,换句话说,现在我们是在emp2
中注入内部bean
,相比以前单独的两个模块,能更直观、方便的看到注入关系。最后读者对Emp
自行编写测试方法并且调用work
方法,结果如下
接下来我们要介绍第三种方法:级联赋值。首先我们对xml文件进行修改,内容见下
<!--第三种方式:级联赋值-->
<bean id="dept3" class="com.atguigu.spring6.iocxml.ditest.Dept">
<property name="dname" value="技术研发部"></property>
</bean>
<bean id="emp3" class="com.atguigu.spring6.iocxml.ditest.Emp">
<property name="ename" value="tom"></property>
<property name="age" value="30"></property>
<property name="dept" ref="dept3"></property>
<property name="dept.dname" value="测试部"></property>
</bean>
我们重点可以放在最后一行代码上,我们通过类调属性的方式对dname
的值进行修改,根据就近原则,控制台打印输出的结果也应该是跟测试部有关,然后读者对Emp
自行编写测试方法并且调用work
方法,结果如下
接着我们尝试着去注入数组类型的相关属性,首先我们新建一个xml
名为bean-diarray
,相关配置见下
<!--注入数组类型的属性-->
<bean id="dept" class="com.atguigu.spring6.iocxml.ditest.Dept">
<property name="dname" value="技术部"></property>
</bean>
<bean id="emp" class="com.atguigu.spring6.iocxml.ditest.Emp">
<!--普通属性-->
<property name="ename" value="lucy"></property>
<property name="age" value="20"></property>
<!--注入对象类型的属性-->
<property name="dept" ref="dept"></property>
<!--注入数组类型的属性-->
<property name="loves">
<array>
<value>Java</value>
<value>Vue</value>
<value>MySQL</value>
</array>
</property>
</bean>
我们重点关注<array>
相关标签的代码,因为我们考虑到人的爱好不止一个,所以Spring6
为我们提供了一个内置的array
标签,在标签内我们可以写入多个值。接着我们在Emp
中对work
方法进行改写,内容如下
public void work(){
System.out.println("emp is working....." + age);
dept.info();
System.out.println(Arrays.toString(loves));
}
最后读者对Emp
自行编写测试方法并且调用work
方法,结果如下
下面我们介绍一下list
集合的注入,首先我们在Dept
中写入一个集合,并且对info
进行改写,内容如下
private List<Emp> empList;
public void info(){
System.out.println("部门名称:" + dname);
for (Emp emp:empList){
System.out.println(emp.getEname());
}
}
接着我们新建xml
名为bean-dilist
,内容如下
<bean id="empone" class="com.atguigu.spring6.iocxml.ditest.Emp">
<property name="ename" value="lucy"></property>
<property name="age" value="19"></property>
</bean>
<bean id="emptwo" class="com.atguigu.spring6.iocxml.ditest.Emp">
<property name="ename" value="mary"></property>
<property name="age" value="30"></property>
</bean>
<bean id="dept" class="com.atguigu.spring6.iocxml.ditest.Dept">
<property name="dname" value="技术部"></property>
<property name="empList">
<list>
<ref bean="empone"></ref>
<ref bean="emptwo"></ref>
</list>
</property>
</bean>
我们重点关注<list>
,在list
集合中,我们引入相关ref
,表示对属性的注入。最后读者对Dept
自行编写测试方法并且调用info
方法,结果如下
现在我们试着进行
map
集合的注入,步骤如下。首先我们在com.atguigu.spring6.iocxml
下新建一个package
名为dimap
,在dimap
中新建两个文件,分别为Student
和Teacher
,分别创建如下属性
public class Student {
private Map<String,Teacher> teacherMap;
private String sid ;
private String sname ;
......
}
public class Teacher {
private String teacherId;
private String teacherName;
......
}
我们分别对这两个类注入set
、get
、toString
方法,接下来我们编写xml
文件,名为bean-dimap
,在xml
文件中,我们主要有以下三个步骤
- 创建两个对象
- 注入普通类型的属性
- 在学生的
bean
中注入map
集合类型的属性
前两个读者自行理解并对xml
进行相关操作,我们重点讲解第三个如何实现。我们对teacherMap
这个map
进行如下操作
<property name="teacherMap">
<map>
<entry>
<key>
<value>10010</value>
</key>
<ref bean="teacher"></ref>
</entry>
</map>
</property>
在上方代码块中,我们通过引入entry
标签对map
进行操作,同样是通过value
以及ref
属性对map
注入相关值,由于我们的map
是写入Teacher
类中,所以我们ref
引入的值必须是teacher
。
接着我们在Student
类中对run
方法进行改写,内容如下
public void run(){
System.out.println("学生的编号:"+sid+"学生的名称:"+sname);
System.out.println(teacherMap);
}
读者可自行在xml
中写入两个map
注入属性,并在dimap
中新建测试类TestStu
,读者自行完成即可。运行结果如下
接下来我们尝试着引用集合类型的bean
,首先我们在dimap
中新建类为Lesson
,在类中我们写入一个属性名为lessonName
,String
类型且私有化,并且构造出它的get
、set
、toString
方法。
接下来我们新建一个xml
名为bean-diref
,我们要完成的步骤如下
- 创建三个对象
- 注入普通类型属性
- 使用
util:
类型 定义 - 在学生
bean
里面引入util:
类型定义bean,完成list map
类型属性注入。然后我们修改头文件,修改内容如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="<http://www.springframework.org/schema/beans>"
xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"
xmlns:util="<http://www.springframework.org/schema/util>"
xsi:schemaLocation="<http://www.springframework.org/schema/util>
<http://www.springframework.org/schema/beans/spring-util.xsd>
<http://www.springframework.org/schema/beans>
<http://www.springframework.org/schema/beans/spring-beans.xsd>">
接下来我们就可以引入util
标签进行引用集合类型的注入。首先我们将id
为student
的property
属性补充完整,然后引入util
标签,内容见下
<util:list id="lessonList">
<ref bean="lessonone"></ref>
<ref bean="lessontwo"></ref>
</util:list>
<util:map id="teacherMap">
<entry>
<key>
<value>10010</value>
</key>
<ref bean="teacherone"></ref>
</entry>
<entry>
<key>
<value>10086</value>
</key>
<ref bean="teachertwo"></ref>
</entry>
</util:map>
按照上述步骤,读者可自行编写测试类,不出意外会报错(故意留的😋😋),我们需要在xml
修改相关配置,改动地方见下图
运行成功截图见下
接下来我们介绍p标签命名注入
,首先我们在beans
头标签中写入
xmlns:p="<http://www.springframework.org/schema/p>"
然后我们写入如下标签
<!--p命名空间注入-->
<bean id="studentp" class="com.atguigu.spring6.iocxml.dimap.Student"
p:sid="100" p:sname="mary" p:lessonList-ref="lessonList" p:teacherMap-ref="teacherMap">
</bean>
我们通过引入外部属性文件
的方式对数据进行注入处理,首先我们在pom.xml
中引入相关依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- <https://mvnrepository.com/artifact/com.alibaba/druid> -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
接着我们在resources
下新建一个properties
名为jdbc
,写入内容如下
jdbc.user=root
jdbc.password=20020930pch
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
📌数据库中的user
和password
都需要根据读者数据库的用户名和密码来决定,本文只是作者的数据库账户名和密码
📌数据库使用的是8.0及以上的版本
我们新建一个xml
文件名为bean-jdbc
,并且使用context
命名空间引入,内容如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="<http://www.springframework.org/schema/beans>"
xmlns:context="<http://www.springframework.org/schema/context>"
xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"
xsi:schemaLocation="<http://www.springframework.org/schema/context>
<http://www.springframework.org/schema/context/spring-context.xsd>
<http://www.springframework.org/schema/beans>
<http://www.springframework.org/schema/beans/spring-beans.xsd>">
接下来我们在com.atguigu.spring6.iocxml
中新建一个package
名为jdbc
,在里面新建一个测试类名为TestJdbc
,内容如下
package com.atguigu.spring6.iocxml.jdbc;
import com.alibaba.druid.pool.DruidDataSource;
import org.junit.jupiter.api.Test;
public class TestJdbc {
@Test
public void demo1(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("20020930pch");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
}
}
然后我们完成数据库相关信息的注入,使用$
引用properties
文件中的相关属性
<!--完成数据库信息注入-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
最后我们写入一个测试类,方法如下
@Test
public void demo2(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean-jdbc.xml");
DruidDataSource dataSource = context.getBean(DruidDataSource.class);
System.out.println(dataSource.getUrl());
}
接下来我们测试bean
的作用域。我们先在com.atguigu.spring6.iocxml
中新建一个package
名为scope
,在包下我们新建Orders
测试类,然后在resources
中新建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>">
<!--通过scope属性配置单实例/多实例-->
<bean id="orders" class="com.atguigu.spring6.iocxml.scope.Orders" scope="singleton">
</bean>
</beans>
在xml
文件中我们可以看到在bean
标签内多了一个属性叫作scope
,读者在这里可以把它理解为作用域标签,Spring6
在默认情况下是单实例,如果想设置多实例,在scope
里面修改即可。最后读者自行编写测试类进行测试,并调用sout
方法输出orders
。
接下来我们介绍一下bean
的生命周期。首先我们在com.atguigu.spring6.iocxml
中新建一个package
名为life
,在life
中新建一个User
类,内容如下
package com.atguigu.spring6.iocxml.life;
public class User {
private String name ;
// 无参数的构造
public User(){
System.out.println("1 bean对象创建,调用无参构造");
}
// 初始化的方法
public void initMethod(){
System.out.println("4 bean对象初始化 调用指定的初始化的方法");
}
// 销毁的方法
public void destoryMethod(){
System.out.println("7 bean对象销毁 调用指定的销毁的方法");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("2 给bean对象设置属性值");
this.name = name;
}
}
接着我们去xml
文件中进行配置,新建一个xml
名为bean-life
,在文件中我们写入如下内容
<?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="user" class="com.atguigu.spring6.iocxml.life.User"
scope="singleton" init-method="initMethod" destroy-method="destoryMethod">
<property name="name" value="lucy"></property></bean>
</beans>
最终我们在life下新建一个测试类名为TestUser
,用来测试bean
的生命周期,内容见下图
package com.atguigu.spring6.iocxml.life;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-life.xml");
User user = context.getBean("user", User.class);
System.out.println("6 bean对象创建完成了,可以使用了");
System.out.println(user);
// context.close(); // 销毁
}
}
测试完毕之后我们写入如下方法进行测试,目的是为了调用context.close();
package com.atguigu.spring6.iocxml.life;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean-life.xml");
User user = context.getBean("user", User.class);
System.out.println("6 bean对象创建完成了,可以使用了");
System.out.println(user);
context.close(); // 销毁
}
}
如此下来,bean
的基本生命周期就算走了一遍,读者可自行阅读代码加深印象。
细心的读者可以发现,生命周期的结果显示并不完整,接下来我们把完整的生命周期展示一遍。首先我们在life
下新建一个类名为MyBeanPost
,我们在这个类中接入BeanPostProcessor
接口,在接口中我们能看到两个方法,我们将这两个方法在MyBeanPost
中进行重写,声明为public
,并且分别加上bean
生命周期中的3和5,最终进行测试,结果如下
接下来我们基于XML
管理Bean-FactoryBean
,首先我们新建一个package
名为factorybean
,然后我们分别创建两个对象,名为FactoryBean
和User
(创建User
是因为有泛型),我们在FactoryBean
中写入如下内容
package com.atguigu.spring6.iocxml.factorybean;
import org.springframework.beans.factory.FactoryBean;
public class MyFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
接着我们新建一个XML
名为bean-factorybean
,我们在里面注入id
为user
的属性,并编写测试类进行测试。
📌在编写测试类的时候,读者需要注意使用强转,因为我们的方法在FactoryBean
是通过User
创建的。
接下来我们尝试基于XML
管理Bean
实现自动装配。首先我们在com.atguigu.spring6.iocxml
下新建一个package
名为auto
,在其包下新建三个package
分别名为controller
、dao
、service
。然后我们在dao
层下新建一个interface
名为UserDao
,配置内容如下
package com.atguigu.spring6.iocxml.auto.dao;
public interface UserDao {
public void addUserDao();
}
接着我们创建一个实现类名为UserDaoImpl
并且继承UserDao
,实现方法的重写,内容如下
@Override
public void addUserDao() {
System.out.println("userDao方法执行了...");
}
以此类推,请读者自行在service
层下写入相关信息。接着在UserController
中写入如下信息
package com.atguigu.spring6.iocxml.auto.controller;
import com.atguigu.spring6.iocxml.auto.service.UserService;
import com.atguigu.spring6.iocxml.auto.service.UserServiceImpl;
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void addUser(){
System.out.println("controller方法执行了...");
// 调用service里面的方法
userService.addUserService();
}
}
完成上述步骤之后,我们在resources
中写入XML
名为bean-auto
,读者自行对三个id
属性进行相关配置,其中在userController
和userService
中写入如下标签以实现根据类型实现自动装配
autowire="byType"
最终读者自行编写与userController
相关的测试类并进行测试。
以上方法是根据类型实现自动装配,在Spring6
中,我们还可以根据名称实现自动装配,即将上述标签替换成
autowire="byName"
运行结果与上述是一致的,区别在于:当我们使用byName
注入属性的时候,我们id的名字必须与测试类中getBean
中的名字一致,否则报错显示userDao
为null
,而类型注入可以忽略这一点。
转载自:https://juejin.cn/post/7208869642906271802