一、IoC容器
定义与本质:IoC 是 Inversion of Control 的缩写,即 “控制反转”,它并非具体技术,而是一种设计思想
(1)控制反转
- 思想本质:控制反转旨在降低程序模块间的耦合度,提升程序的可扩展性和可维护性
- “反转”的内容:
- 实现方式:
(2)依赖注入
- 在配置文件中配置好,针对于某个类我们想要什么样的对象,把这个信息交给IoC。然后由IoC创建、返回对象
- 总结:
(3)IOC容器在Spring的实现
- Spring的 IoC容器 与 IoC 思想的关系:
- Spring 提供了实现 IoC 容器的两种方式:
- ApplicationContext 的主要实现类:
- ApplicationContext 的主要实现类介绍:
- ApplicationContext 就是IoC容器:
二、基于XML管理Bean
(1)搭建子模块spring-ioc-xml
- 我们把 spring-first 的依赖都让父工程管理(即将 spring-first 的 pom.xml 文件中的 dependencies 标签和该标签里的所有内容放到 spring6 的 pom.xml 文件中,然后刷新):
- 搭建好子模块 spring-ioc-xml 后发现:因为此时它继承了父模块的所有依赖项
- 要在模块中使用 spring ,就必须有 spring 的配置文件,所以我们在 spring-ioc-xml 的 src/main/resources中 创建 bean.xml(名字可以随便起)
- 还要引入log4j2的配置文件:
- 在 spring-ioc-xml 的 src/main/java 下新建一个类
(2)实验一:获取bean
准备操作:
- 方式一:根据id获取
- id属性指定了bean的唯一标识
- 演示:
public class TestUser { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //获取bean User user = (User)context.getBean("user"); } }
-
Loaded 1 bean definitions from class path resource [bean.xml] 意思是 加载 spring 配置文件
-
Creating shared instance of singleton bean 'user' 意思是 创建了一个单例对象
- 方式二:根据类型获取
- 此时根据id得到对象和根据类型得到对象,二者返回的是同一个对象(因为单例)
- 演示:
public class TestUser { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //1.根据id获取bean User user1 = (User)context.getBean("user"); System.out.println("根据id获取bean:" + user1); //2.根据类型获取bean User user2 = context.getBean(User.class); System.out.println("根据类型获取bean:" + user2); } }
- 方式三:根据id和类型
- 演示:
public class TestUser { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //1.根据id获取bean User user1 = (User)context.getBean("user"); System.out.println("根据id获取bean:" + user1); //2.根据类型获取bean User user2 = context.getBean(User.class); System.out.println("根据类型获取bean:" + user2); //3.根据id和类型获取bean User user3 = context.getBean("user", User.class); System.out.println("根据id和类型获取bean:" + user3); } }
- user1,user2 和 user3 的地址值都是一样的
- 演示:
- 用后两种方式时,因为已经指定了类型,所以不需要我们强转
- 注意:当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个
- 假如,我们给同一个类配置不同的id值,那么在用类型获取bean对象时就会报错
- 但是我们发现,如果我们用id获取bean对象,或者用id和类型获取bean对象,就不会报错。因为根据id就可以精准定位
- expected single matching bean but found 2: user,user1 该错误信息表示:期望获得一个单实例的bean,但是获取了两个
- 也就是说,一个bean标签对应着一个单实例的bean。此时我们对同一个类设置了两个bean标签,我们分别用它们的id来获取bean,运行后可以发现,这确实是两个不同的bean
public class TestUser { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //1.根据id获取bean User user = (User)context.getBean("user"); System.out.println("根据id获取bean:" + user); User user1 = (User)context.getBean("user1"); System.out.println("根据id获取bean:" + user1); } }
- 假如,我们给同一个类配置不同的id值,那么在用类型获取bean对象时就会报错
- 组件类通常是指那些会被 IoC 容器创建和管理对象的类
- 扩展知识:
- 结论:
(3)实验二:依赖注入之setter注入
- 类有属性,创建对象的过程中,给属性设置值
- 第一种方式:基于set方法完成
- 第二种方式:基于构造器完成
- 创建一个Book类:
package com.atguigu.spring6.iocxml.di; public class Book { private String bname; private String author; //生成set方法 public void setBname(String bname) { this.bname = bname; } public void setAuthor(String author) { this.author = author; } //生成有参数的构造器 public Book(String bname, String author) { this.bname = bname; this.author = author; } //生成无参数的构造器 public Book() { } //toString方法 @Override public String toString() { return "Book{" + "bname='" + bname + '\'' + ", author='" + author + '\'' + '}'; } public static void main(String[] args) { //set方法注入 Book book = new Book(); book.setBname("java"); book.setAuthor("尚硅谷"); //构造器注入 Book book1 = new Book("c++","尚硅谷"); } }
- 配置 bean 标签时为属性赋值(此时我们重新建了一个配置文件,名字可以随便起。Spring 相关的配置文件都要放在 src/main/resources 下面):
name 对应类的属性名、value 对应类的属性值
- 测试代码如下,因为我们此时使用的是bean-di.xml文件,所以文件名别填错
public class TestBook { @Test public void testSetter(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml"); Book book = context.getBean(Book.class); System.out.println(book); } }
- 可以看到,通过setter注入成功(所谓注入,其实就是给属性赋值)
- 基于 Setter 注入需类有 无参构造器和对应 Setter 方法,其本质就是 Spring 容器调用类的 Setter 方法完成依赖注入
(4)实验三:依赖注入之构造器注入
- 基于构造器注入前提是类有有参构造器,其本质就是利用类的有参构造器进行依赖注入
- 构造器注入,要这么写配置文件中的bean标签(可以用name也可以用index,index=0表示第一个属性,index=1表示第二个属性......)
- 为了测试效果更明显,我们在Book类的setter方法中加一条语句:
- 测试代码:
public class TestBook { @Test public void testConstructor(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml"); //因为现在Book类有两个id,所以我们不能根据类型获取bean了 Book book = (Book)context.getBean("bookCon"); System.out.println(book); } }
- 可以看到,在bean标签里使用 constructor-arg 标签,等价于调用有参构造器来初始化对象
- 总结:用
<bean>
标签会让 Spring 创建类的实例,id
类似实例名;用<property>
标签是通过类的setter
方法给对象属性赋值,先以无参构造器创建对象再赋值;用<constructor-arg>
标签则是调用类的有参构造器
(5)实验四:特殊值处理
- 字面量赋值:字面量就是数据本身。例如,int a = 10;10就是字面量
- 使用value属性给bean的属性赋值时,Spring会把给value属性的值看作字面量。比如,这个“前端开发”就是字面量
- 如果我们此时,给Book类再添加一个属性,并为这个属性设置setter方法
- 如果我们在注入时,给该属性赋null,该怎么做呢?
- xml实体:小于号在XML文档中是用来定义标签的开始的,不能随便使用。假如我们想给属性赋的值中出现了 < 该怎么办呢?例如
- 解决xml实体的方式一:转义,小于号(<)用 < 表示,大于号(>)用 > 表示。这样就不会报错了
- 解决xml实体的方式二:CDATA区。XML解析器看到 CDATA区 就知道这里是纯文本,就不会当作XML标签或属性来解析,所以在CDATA区中写什么符号都可以
- 在<![CDATA[在这里面写什么符号都可以]]>
- 测试一下,发现成功
(6)实验五:为对象类型属性赋值
方式一:引用外部bean
- 准备两个类,员工类和部门类
- 代码:
package com.atguigu.spring6.iocxml.ditest; //员工类 public class Emp { //对象类型的属性 员工属于某个部门 private Dept dept; //员工名称 private String ename; //员工年龄 private Integer age; public void setDept(Dept dept) { this.dept = dept; } public void setEname(String ename) { this.ename = ename; } public void setAge(Integer age) { this.age = age; } public void work(){ System.out.println(ename+"emp work..."+age); dept.info(); } }
package com.atguigu.spring6.iocxml.ditest; //部门类 public class Dept { private String dname; public void setDname(String dname) { this.dname = dname; } public void info(){ System.out.println("部门名称:" + dname); } }
- 我们新建一个Spring配置文件(文件名称可以随意,但是 Spring 配置文件必须放在src/main/resources 内)
<?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.创建两个类对象emp和dept 2.在emp的bean标签里,用property引入dept的bean --> <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="50"></property> <!--对象类型属性注入--> <property name="dept" ref="dept"></property> </bean> </beans>
- 为对象类型的属性注入,不要用value,而是用ref。ref后面跟的是,要注入的对象的id
方式二:内部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"> <!--第二种方式:内部bean注入--> <bean id="emp2" class="com.atguigu.spring6.iocxml.ditest.Emp"> <property name="ename" value="mary"></property> <property name="age" value="20"></property> <property name="dept"> <bean id="dept2" class="com.atguigu.spring6.iocxml.ditest.Dept"> <property name="dname" value="财务部"></property> </bean> </property> </bean> </beans>
- 总结:
方式三:级联属性赋值
- 级联赋值代码演示:
package com.atguigu.spring6.iocxml.ditest; //员工类 public class Emp { //对象类型的属性 员工属于某个部门 private Dept dept; //员工名称 private String ename; //员工年龄 private Integer age; public void setDept(Dept dept) { this.dept = dept; } public void setEname(String ename) { this.ename = ename; } public void setAge(Integer age) { this.age = age; } public Dept getDept() { return dept; } public String getEname() { return ename; } public Integer getAge() { return age; } public void work(){ System.out.println(ename+"emp work..."+age); dept.info(); } }
<?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="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> </beans>
- 在进行级联赋值时,必须为对象类型的属性提供
getter
方法。具体过程为:(1)先通过getter
方法获取该对象,(2)再利用该对象所属类中提供的setter
方法为其属性赋值
(7)实验六:为数组类型属性赋值
- 修改Emp类的代码,为员工类添加一个新属性,该属性为数组类型
package com.atguigu.spring6.iocxml.ditest; import java.util.Arrays; //员工类 public class Emp { //对象类型的属性 员工属于某个部门 private Dept dept; //员工名称 private String ename; //员工年龄 private Integer age; //爱好 private String[] loves; public void setDept(Dept dept) { this.dept = dept; } public void setEname(String ename) { this.ename = ename; } public void setAge(Integer age) { this.age = age; } public void setLoves(String[] loves) { this.loves = loves; } public Dept getDept() { return dept; } public String getEname() { return ename; } public Integer getAge() { return age; } public void work(){ System.out.println(ename+"emp work..."+age); dept.info(); System.out.println(Arrays.toString(loves)); } }
- 为了方便,我们再创建一个Spring配置文件
<?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="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>吃饭</value> <value>睡觉</value> <value>敲代码</value> </array> </property> </bean> </beans>
(8)实验七:为集合类型属性赋值
- 为 List 集合类型属性赋值:首先我们修改部门类,为它添加一个List集合类型的属性
package com.atguigu.spring6.iocxml.ditest; import java.util.List; //部门类 public class Dept { //一个部门有很多员工 private List<Emp> empList; private String dname; public String getDname() { return dname; } public void setDname(String dname) { this.dname = dname; } public List<Emp> getEmpList() { return empList; } public void setEmpList(List<Emp> empList) { this.empList = empList; } public void info() { System.out.println("部门名称:"+dname); for (Emp emp:empList) { System.out.println(emp.getEname()); } } }
然后我们新建一个Spring配置文件
<?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="empone" class="com.atguigu.spring6.iocxml.ditest.Emp"> <property name="ename" value="lucy"></property> <property name="age" value="20"></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> </beans>
- 为 Map 集合类型属性赋值(先创建两个类,Student类和Teacher类,然后配置一个Spring的xml文件):
package com.atguigu.spring6.iocxml.dimap; import java.util.Map; //学生类 public class Student { private Map<String,Teacher> teacherMap; private String sid; private String sname; public String getSid() { return sid; } public void setSid(String sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public Map<String, Teacher> getTeacherMap() { return teacherMap; } public void setTeacherMap(Map<String, Teacher> teacherMap) { this.teacherMap = teacherMap; } public void run(){ System.out.println("学生编号:" + sid + "学生名称:" + sname); System.out.println(teacherMap); } }
package com.atguigu.spring6.iocxml.dimap; //老师类 public class Teacher { private String teacherId; private String teacherName; public String getTeacherId() { return teacherId; } public void setTeacherId(String teacherId) { this.teacherId = teacherId; } public String getTeacherName() { return teacherName; } public void setTeacherName(String teacherName) { this.teacherName = teacherName; } @Override public String toString() { return "Teacher{" + "teacherId='" + teacherId + '\'' + ", teacherName='" + teacherName + '\'' + '}'; } }
<?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.创建两个对象 2.注入普通类型属性 3.在学生bean注入map集合类型属性 --> <bean id="teacherone" class="com.atguigu.spring6.iocxml.dimap.Teacher"> <!--注入普通类型属性--> <property name="teacherId" value="100"></property> <property name="teacherName" value="西门讲师"></property> </bean> <bean id="teachertwo" class="com.atguigu.spring6.iocxml.dimap.Teacher"> <!--注入普通类型属性--> <property name="teacherId" value="200"></property> <property name="teacherName" value="上官讲师"></property> </bean> <bean id="student" class="com.atguigu.spring6.iocxml.dimap.Student"> <!--注入普通类型属性--> <property name="sid" value="2000"></property> <property name="sname" value="张三"></property> <!--注入Map集合类型属性--> <property name="teacherMap"> <map> <entry> <key> <value>10010</value> </key> <ref bean="teacherone"></ref> </entry> <entry> <key> <value> 10086 </value> </key> <ref bean="teachertwo"></ref> </entry> </map> </property> </bean> </beans>
- 这是xml文件的约束,写明了xml文件中能用哪些标签,标签里能用哪些属性
- 使用 util:list 和 util:map 前,必须引入相应的命名空间,可以通过IDEA的提示功能选择
<?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 ">
- 注入List集合和Map集合的第二种方式( util:list 和 util:map 的id可以随便写,只是ref的时候是根据id来的):
<?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/util/spring-util.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd "> <!-- 1.创建三个对象 2.注入普通类型属性 3.使用util:类型 定义 4.在学生bean引入 --> <bean id="lessonone" class="com.atguigu.spring6.iocxml.dimap.Lesson"> <property name="lessonName" value="java开发"></property> </bean> <bean id="lessontwo" class="com.atguigu.spring6.iocxml.dimap.Lesson"> <property name="lessonName" value="前端开发"></property> </bean> <bean id="teacherone" class="com.atguigu.spring6.iocxml.dimap.Teacher"> <property name="teacherId" value="100"></property> <property name="teacherName" value="西门讲师"></property> </bean> <bean id="teachertwo" class="com.atguigu.spring6.iocxml.dimap.Teacher"> <property name="teacherId" value="200"></property> <property name="teacherName" value="欧阳讲师"></property> </bean> <bean id="student" class="com.atguigu.spring6.iocxml.dimap.Student"> <property name="sid" value="10000"></property> <property name="sname" value="lucy"></property> <!--注入list和map类型的属性--> <property name="lessonList" ref="lessonList"></property> <property name="teacherMap" ref="teacherMap"></property> </bean> <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> </beans>
- 注意:按照上面的图写的xml文件写错了,有个地方要注意一下。就是我们要把beans的地方全换成util,有个地方漏了
- 总结:
(9)实验八:p命名空间
- p命名空间是指这种(灰色部分):
- 注意:xsi:schemaLocation部分就不用像util一样修改了
- p命名空间注入:
<?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" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
(10)实验九:引入外部属性文件
- 加入数据库相关依赖(在pom.xml文件中,加入后记得刷新):
<!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <!-- 数据源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.15</version> </dependency>
- 创建外部属性文件(在 src/main/resources 中创建),properties 格式:
jdbc.user=root jdbc.password=atguigu jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC jdbc.driver=com.mysql.cj.jdbc.Driver
- 在src/main/resources中创建一个Spring的配置文件,并引入 context 命名空间,注意,此时还要添加xsi:schemaLocation的部分
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> </beans>
- 还要引入外部属性文件,并完成数据库信息的注入:
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <!--引入外部属性文件--> <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <!--完成数据库信息注入--> <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> </beans>
- 测试代码:
public class TestJdbc { @Test public void demo1(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/spring?serverTimezone=UTC"); dataSource.setUsername("root"); dataSource.setPassword("atguigu"); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); } @Test public void demo2(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean-jdbc.xml"); DruidDataSource druidDataSource = context.getBean(DruidDataSource.class); System.out.println(druidDataSource.getUrl()); } }
- 总结:
- druidDataSource:
(11)实验十:bean的作用域
- 在 Spring 中可以通过配置 bean 标签的 scope 属性来指定 bean 的作用域范围,scope的取值含义参见下表:
取值 含义 创建对象的时机 singleton(默认) 在IoC容器中,这个bean的对象始终为单实例 IoC容器初始化时 prototype 这个bean在IoC容器中有多个实例 获取bean时 - 如果是在WebApplicationContext环境下,还会有另外几个作用域(但不常用):
取值 含义 request 在一个请求范围内有效 session 在一个会话范围内有效 - scope 的取值为 singleton ,测试(三个文件的代码)
package com.atguigu.spring6.iocxml.scope; public class Orders { }
<?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>
package com.atguigu.spring6.iocxml.scope; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestOrders { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean-scope.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println(orders); } }
Creating shared instance of singleton bean 'orders'代表创建了一个单实例对象
- 我们再获取一个bean
看运行结果可以发现,当 scope 是 singleton 时,bean 标签创建的就是一个单实例对象
- scope 的取值为 prototype ,测试(我们只修改了 Spring 的 xml 文件,将 scope 的取值改成了 prototype )
可以看到,获取的两个 bean 其实是不同的对象
- 总结:
scope
为singleton
时,Spring IoC 容器初始化就创建 Bean 实例scope
为prototype
时,在调用getBean()
获取 Bean 时才创建
(12)实验十一:bean生命周期
- 具体的生命周期过程(单例bean就是指被Singleton修饰,原型bean就是指被prototype修饰):
- 创建User类:
package com.atguigu.spring6.iocxml.life; public class User { private String name; public String getName() { return name; } public void setName(String name) { System.out.println("2.给bean对象设置属性值"); this.name = name; } //无参数构造 public User() { System.out.println("1.bean对象创建,调用无参数构造"); } //初始化方法 public void initMethod(){ System.out.println("4.bean对象的初始化,会调用指定的初始化的方法"); } //销毁方法 public void destroyMethod(){ System.out.println("7.bean对象销毁,调用指定的销毁方法"); } }
- 编写 Spring 的配置文件:
<?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="destroyMethod"> <property name="name" value="lucy"></property> </bean> </beans>
- 测试类:
package com.atguigu.spring6.iocxml.life; 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(); } }
- 进行测试(注意:要想使用close方法,就不能用ApplicationContext接口了):
- Bean 后置处理器可在 Bean 生命周期的初始化前后添加额外操作,需实现 BeanPostProcessor 接口并配置到 IoC 容器中。它并非只对单个 Bean 生效,而是会作用于 IoC 容器里的所有 Bean
- 创建 bean 的后置处理器:
public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("3.后置处理器在初始化之前执行"); System.out.println(beanName + "::" + bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("5.后置处理器在初始化之后执行"); System.out.println(beanName + "::" + bean); return bean; } }
- 在 IoC 容器中配置后置处理器:
<?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="destroyMethod"> <property name="name" value="lucy"></property> </bean> <bean id="myBeanPost" class="com.atguigu.spring6.iocxml.life.MyBeanPost"></bean> </beans>
- 加了后置处理器以后的测试效果:
(13)实验十二:FactoryBean
- FactoryBean:
- 创建一个User类:
package com.atguigu.spring6.iocxml.factorybean; public class 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; } }
- 编写一个Spring的配置文件:
<?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.factorybean.MyFactoryBean"></bean> </beans>
- 编写一个测试类:
public class TestUser { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean-factorybean.xml"); User user = (User)context.getBean("user"); System.out.println(user); } }
- 根据测试效果可以发现,虽然我们在配置bean时,对class的配置是myFactoryBean,但是得到的bean并不是这个类的对象,而是该类中的getObject方法返回的对象
- 总结:
(14)实验十三:基于xml自动装配
- 基本环境准备:创建三个软件包(controller、service、dao),controller里面创建一个类UserController。service里面创建一个类UserServiceImpl、一个接口UserService,dao里面创建一个类UserDaoImpl、一个接口UserDao
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 { public void addUser(){ System.out.println("controller方法执行了..."); UserService userService = new UserServiceImpl(); userService.addUserService(); } }
package com.atguigu.spring6.iocxml.auto.service; public interface UserService { public void addUserService(); }
package com.atguigu.spring6.iocxml.auto.service; import com.atguigu.spring6.iocxml.auto.dao.UserDao; import com.atguigu.spring6.iocxml.auto.dao.UserDaoImpl; public class UserServiceImpl implements UserService{ @Override public void addUserService() { System.out.println("userService方法执行了"); UserDao userDao = new UserDaoImpl(); userDao.addUserDao(); } }
package com.atguigu.spring6.iocxml.auto.dao; public interface UserDao { public void addUserDao(); }
package com.atguigu.spring6.iocxml.auto.dao; public class UserDaoImpl implements UserDao{ @Override public void addUserDao() { System.out.println("userDao方法执行了"); } }
addUser方法里调用了addUserService方法、addUserService方法里调用了addUserDao方法
- 修改代码(UserController和UserServiceImpl):
package com.atguigu.spring6.iocxml.auto.controller; import com.atguigu.spring6.iocxml.auto.service.UserService; import com.atguigu.spring6.iocxml.auto.service.UserServiceImpl; import org.junit.jupiter.api.Test; public class UserController { private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } @Test public void addUser(){ System.out.println("controller方法执行了..."); userService.addUserService(); // UserService userService = new UserServiceImpl(); // userService.addUserService(); } }
package com.atguigu.spring6.iocxml.auto.service; import com.atguigu.spring6.iocxml.auto.dao.UserDao; import com.atguigu.spring6.iocxml.auto.dao.UserDaoImpl; public class UserServiceImpl implements UserService{ private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void addUserService() { System.out.println("userService方法执行了"); userDao.addUserDao(); // UserDao userDao = new UserDaoImpl(); // userDao.addUserDao(); } }
- 在resources中创建配置文件(这么写就是手动装配)
- 自动装配怎么写呢?
<?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="userController" class="com.atguigu.spring6.iocxml.auto.controller.UserController" autowire="byType"> </bean> <bean id="userService" class="com.atguigu.spring6.iocxml.auto.service.UserServiceImpl" autowire="byType"> </bean> <bean id="userDao" class="com.atguigu.spring6.iocxml.auto.dao.UserDaoImpl"> </bean> </beans>
- 测试代码:
package com.atguigu.spring6.iocxml.auto; import com.atguigu.spring6.iocxml.auto.controller.UserController; 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-auto.xml"); UserController userController = context.getBean("userController", UserController.class); userController.addUser(); } }
- 自动装配概念:原本通过外部 bean 方式注入对象类型属性,现在可利用 bean 标签的
autowire
属性实现自动装配 - byType装配方式:
- byName装配方式:
- 我们根据代码测试一下:
<?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="userController" class="com.atguigu.spring6.iocxml.auto.controller.UserController" autowire="byName"> </bean> <bean id="userService" class="com.atguigu.spring6.iocxml.auto.service.UserServiceImpl" autowire="byName"> </bean> <bean id="userDao" class="com.atguigu.spring6.iocxml.auto.dao.UserDaoImpl"> </bean> </beans>
- 当我们改变xml文件中对象的id,就可以发现不能自动装配成功了(因为UserController类中,UserService类型的属性名字叫userService,但是我们此时的对象id不是userService了,而是userServiceImpl。UserServiceImpl类中,UserDao类型的属性名字叫userDao,但是我们此时的对象id不是userDao了,而是userDaoImlp)
可以看到爆红
三、基于注解管理Bean(⭐)
从 Java 5 起,Java 支持注解,它是代码特殊标记,能在编译、类加载和运行时被读取以执行对应处理,开发人员可借此在不改动原代码逻辑时嵌入补充信息
Spring 2.5 起全面支持注解技术,可用注解实现自动装配,简化 XML 配置。通过注解实现自动装配步骤为:
- 引入依赖
- 开启组件扫描
- 使用注解定义bean
- 依赖注入
(1)搭建子模块spring6-ioc-annotation
- 引入日志模块log4j2.xml
- 创建一个Spring的配置文件
- 因为依赖已经在父模块中引入过,通过依赖的继承机制,子模块就不需要重复引入了
(2)开启组件扫描
- Spring 默认不采用注解装配 Bean,可在 Spring 的 XML 文件里,利用 <context:component-scan> 元素开启 Spring Beans 自动扫描功能。开启后,Spring 会自动扫描 base - package 属性指定的包及其子包下的所有类,若类使用了 @Component 注解,就会将该类装配到容器中
- 情况一:最基本的扫描方式
- 情况二:指定要排除的组件
<context:component-scan base-package="com.atguigu.spring6"> <!-- context:exclude-filter标签:指定排除规则 --> <!-- type:设置排除或包含的依据 type="annotation",根据注解排除,expression中设置要排除的注解的全类名 type="assignable",根据类型排除,expression中设置要排除的类型的全类名 --> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <!--<context:exclude-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>--> </context:component-scan>
(1)<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>表示Controller这个注解就不进行扫描(2)<context:exclude-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>表示UserController这个类就不进行扫描
- 情况三:仅扫描指定组件
<context:component-scan base-package="com.atguigu" use-default-filters="false"> <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 --> <!-- use-default-filters属性:取值false表示关闭默认扫描规则 --> <!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 --> <!-- type:设置排除或包含的依据 type="annotation",根据注解排除,expression中设置要排除的注解的全类名 type="assignable",根据类型排除,expression中设置要排除的类型的全类名 --> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <!--<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>--> </context:component-scan>
(1)<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>表示只扫描Controller这个注解(2)<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>表示只扫描UserController这个类
(3)使用注解定义bean
- Spring提供了多个注解,这些注解可以直接标注在Java类上,将它们定义成Spring Bean
注解 说明 @Component 该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如Service层、Dao层等。使用时只需将该注解标注在相应的类上即可 @Repository 该注解用于将Dao层(数据访问层)的类标识为Spring中的Bean,其功能和@Component相同 @Service 该注解用于将Service层(业务层)的类标识为Spring中的Bean,其功能和@Component相同 @Controller 该注解用于将Controller层(控制层)的类标识为Spring中的Bean,其功能和@Component相同 - 使用举例:value可以不写,比如类名为User,如果不写value的话,value的值默认为user
- 建一个User类,建一个TestUser类
package com.atguigu.spring6.bean; import org.springframework.stereotype.Component; @Component(value="user")//<bean id="user" class="..."> public class User { }
package com.atguigu.spring6.bean; 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.xml"); User user = context.getBean(User.class); System.out.println(user); } }
- 配置文件:
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <!--开启组件扫描--> <context:component-scan base-package="com.atguigu.spring6"></context:component-scan> </beans>
- 测试效果:
- 说明在User类上加了@Component注解,效果和bean标签一样
(4)实验一:@Autowired注入
单独使用@Autowired注解,默认根据类型装配
@Autowired注解源码:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
源码中有两处需要注意:
- 第一处:该注解可以标注在哪里:
- 构造方法上
- 方法上
- 属性上
- 形参上
- 注解上
- 第二处: 该注解有一个 required 属性,默认值是 true 。表示在注入的时候要求被注入的bean是必须存在的,如果不存在则报错。如果required属性设置为 false ,表示被注入的bean存在或不存在都没关系,存在的话就注入,不存在也不报错
3.4.1场景一:属性注入
- 属性注入就是根据类型找到对象,进行注入的。类似于,基于XML的自动装配
- 创建三个软件包(controller、service、dao)。分别在这几个包中创建实现类和接口
- 代码:
package com.atguigu.spring6.autowired.controller; import com.atguigu.spring6.autowired.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { //注入service //第一种方式:属性注入 @Autowired //根据类型找到对应的对象,完成注入 private UserService userService; public void add(){ System.out.println("controller......"); userService.add(); } }
package com.atguigu.spring6.autowired.service; public interface UserService { public void add(); }
package com.atguigu.spring6.autowired.service; import com.atguigu.spring6.autowired.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService{ //注入dao @Autowired private UserDao userDao; @Override public void add(){ System.out.println("service......"); userDao.add(); } }
package com.atguigu.spring6.autowired.dao; public interface UserDao { public void add(); }
package com.atguigu.spring6.autowired.dao; import org.springframework.stereotype.Repository; @Repository public class UserDaoImpl implements UserDao{ @Override public void add(){ System.out.println("dao执行..."); } }
- 创建测试类:
package com.atguigu.spring6.autowired; import com.atguigu.spring6.autowired.controller.UserController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestUserController { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); UserController userController = context.getBean(UserController.class); userController.add(); } }
- 解释:根据注解创建了三个bean(UserController、UserServiceImpl、UserDaoImpl),然后根据类型装配,UserController类中有一个Userservice类型的属性→给userService装配UserServiceImpl对象,UserServiceImpl中有一个UserDao类型的属性→给userDao装配UserDaoImlp对象
3.4.2场景二:set注入
- 修改代码(UserController和UserServiceImpl的):
package com.atguigu.spring6.autowired.controller; import com.atguigu.spring6.autowired.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } public void add(){ System.out.println("controller......"); userService.add(); } }
package com.atguigu.spring6.autowired.service; import com.atguigu.spring6.autowired.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService{ private UserDao userDao; @Autowired public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void add(){ System.out.println("service......"); userDao.add(); } }
- 测试效果:
- 理解:
3.4.3场景三:构造方法注入
- 修改代码(UserController和UserServiceImpl的):
package com.atguigu.spring6.autowired.controller; import com.atguigu.spring6.autowired.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { private UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; } public void add(){ System.out.println("controller......"); userService.add(); } }
package com.atguigu.spring6.autowired.service; import com.atguigu.spring6.autowired.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService{ private UserDao userDao; @Autowired public UserServiceImpl(UserDao userDao) { this.userDao = userDao; } @Override public void add(){ System.out.println("service......"); userDao.add(); } }
- 测试效果:
- 理解:
3.4.4场景四:形参上注入
- 就是不在构造器上加@Autowired,但是把 @Autowired 加在构造器形参上
- 修改代码(UserController和UserServiceImpl的):
package com.atguigu.spring6.autowired.controller; import com.atguigu.spring6.autowired.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { private UserService userService; public UserController(@Autowired UserService userService) { this.userService = userService; } public void add(){ System.out.println("controller......"); userService.add(); } }
package com.atguigu.spring6.autowired.service; import com.atguigu.spring6.autowired.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService{ private UserDao userDao; public UserServiceImpl(@Autowired UserDao userDao) { this.userDao = userDao; } @Override public void add(){ System.out.println("service......"); userDao.add(); } }
- 测试效果:
- 理解:
3.4.5场景五:只有一个构造函数,无注解

- 当有参数的构造方法只有一个时,@AutoWired注解可以省略
- 修改代码:
package com.atguigu.spring6.autowired.controller; import com.atguigu.spring6.autowired.service.UserService; import org.springframework.stereotype.Controller; @Controller public class UserController { private UserService userService; public UserController(UserService userService) { this.userService = userService; } public void add(){ System.out.println("controller......"); userService.add(); } }
package com.atguigu.spring6.autowired.service; import com.atguigu.spring6.autowired.dao.UserDao; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService{ private UserDao userDao; public UserServiceImpl(UserDao userDao) { this.userDao = userDao; } @Override public void add(){ System.out.println("service......"); userDao.add(); } }
- 测试通过:
3.4.6场景六:@Autowired注解和@Qualifier注解联合
- 比如,当接口UserDao有两个实现类UserDaoImpl和UserRedisDaoImpl时,由于这两个类上都加了@Repository注解,因此会生成它们各自对应的bean。当只根据类型查找时,会发现满足UserDao的对象有两个,这时就会报错(expected single matching bean but found 2: userDaoImpl,userRedisDaoImpl)
- 针对该问题,解决方案是:使用两个注解,根据名称注入
package com.atguigu.spring6.autowired.service; import com.atguigu.spring6.autowired.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService{ @Autowired @Qualifier(value = "userRedisDaoImpl") private UserDao userDao; @Override public void add(){ System.out.println("service......"); userDao.add(); } }
- 个人理解:
(5)实验二:@Resource注入
@Resource注解也可以完成属性注入。那么它和@Autowired注解有什么区别呢?
- 总结:
- 如图所示:
@Resource注解不属于 JDK 核心,而是 Java EE(现 Jakarta EE)规范一部分。在 JDK 8 环境下使用它无需额外引入依赖;但在高于 JDK 11 或低于 JDK 8 的环境中使用,则需要引入相应依赖
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
3.5.1根据name注入
- 总结:
- 测试代码:
package com.atguigu.spring6.resource.controller; import com.atguigu.spring6.resource.service.UserService; import jakarta.annotation.Resource; import org.springframework.stereotype.Controller; @Controller(value = "myUserController") public class UserController { @Resource(name="myUserService") private UserService userService;//此时,指定了名称,那么我们就要找到名为myUserService的对象 public void add(){ System.out.println("controller......"); userService.add(); } }
package com.atguigu.spring6.resource.service; import com.atguigu.spring6.resource.dao.UserDao; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @Service(value = "myUserService") public class UserServiceImpl implements UserService { @Resource private UserDao myUserDao; @Override public void add(){ System.out.println("service......"); myUserDao.add(); } }
package com.atguigu.spring6.resource.dao; import org.springframework.stereotype.Repository; @Repository(value = "myUserDao") public class UserDaoImpl implements UserDao { @Override public void add(){ System.out.println("dao执行..."); } }
package com.atguigu.spring6.resource.dao; import org.springframework.stereotype.Repository; @Repository(value = "myUserRedisDao") public class UserRedisDaoImpl implements UserDao { @Override public void add() { System.out.println("dao redis......"); } }
package com.atguigu.spring6.resource; import com.atguigu.spring6.resource.controller.UserController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestUserController { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); UserController userController = context.getBean("myUserController",UserController.class); userController.add(); } }
package com.atguigu.spring6.resource.service; public interface UserService { public void add(); }
package com.atguigu.spring6.resource.dao; public interface UserDao { public void add(); }
- 测试成功
- 如果根据name没有找到,就会根据类型找
- 我们修改一下UserController中的代码,把UserService类型的属性的属性名改成“userService”。由于此时@Resource也没有给name显式赋值,因此根据属性名“userService”来找对象,而此时没有叫userService的对象,所以它会根据类型来找
3.5.2name未知注入
- 当name没有显式指定时,name就是该属性的属性名
- 演示看3.5.1
3.5.3其他情况
- 当name没有显式指定时,name就是该属性的属性名。当根据属性名也找不到对应的对象时,我们就根据该类型的类型来找,找到可以兼容该类型的对象
- 演示看3.5.1
(6)Spring全注解开发
- 此前借助注解完成对象创建与属性注入时,需使用 Spring 配置文件开启组件扫描。而全注解开发则摒弃了配置文件,采用编写配置类的方式替代,以此实现组件扫描等配置功能
- 用配置类来代替配置文件,开启组件扫描。配置类写在 src/main/java 中
package com.atguigu.spring6.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.atguigu.spring6") public class SpringConfig { }
配置类上有两个注解,一个是@Configuration,一个是@ComponentScan。@Configuration说明这是一个配置类,@ComponentScan("包名")和配置文件中<context:component-scan base-package="com.atguigu.spring6"></context:component-scan>作用一样
- 在测试程序中进行修改,把加载配置文件改成加载配置类
package com.atguigu.spring6.resource; import com.atguigu.spring6.config.SpringConfig; import com.atguigu.spring6.resource.controller.UserController; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class TestUserController { public static void main(String[] args) { //加载配置类 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserController userController = context.getBean("myUserController",UserController.class); userController.add(); } }
把原来的 ClassPathXMLApplicationContext 改成 AnnotationConfigApplicationContext