3、容器:IoC
IoC 是 Inversion of Control 的簡寫,譯為“控制反轉”,它不是一門技術,而是一種設計思想,是一個重要的面向對象編程法則,能夠指導我們如何設計出松耦合、更優良的程序。
Spring 通過 IoC 容器來管理所有 Java 對象的實例化和初始化,控制對象與對象之間的依賴關系。我們將由 IoC 容器管理的 Java 對象稱為 Spring Bean,它與使用關鍵字 new 創建的 Java 對象沒有任何區別。
IoC 容器是 Spring 框架中最重要的核心組件之一,它貫穿了 Spring 從誕生到成長的整個過程。
3.1、IoC容器
3.1.1、控制反轉(IoC)
控制反轉是一種思想。
控制反轉是為了降低程序耦合度,提高程序擴展力。
控制反轉,反轉的是什么?
將對象的創建權利交出去,交給第三方容器負責。
將對象和對象之間關系的維護權交出去,交給第三方容器負責。
控制反轉這種思想如何實現呢?
DI(Dependency Injection):依賴注入
3.1.2、依賴注入
DI(Dependency Injection):依賴注入,依賴注入實現了控制反轉的思想。
依賴注入:
指Spring創建對象的過程中,將對象依賴屬性通過配置進行注入
依賴注入常見的實現方式包括兩種:
第一種:set注入
第二種:構造注入
所以結論是:IOC 就是一種控制反轉的思想, 而 DI 是對IoC的一種具體實現。
Bean管理說的是:Bean對象的創建,以及Bean對象中屬性的賦值(或者叫做Bean對象之間關系的維護)。
3.1.3、IoC容器在Spring的實現
Spring 的 IoC 容器就是 IoC思想的一個落地的產品實現。IoC容器中管理的組件也叫做 bean。在創建 bean 之前,首先需要創建IoC 容器。Spring 提供了IoC 容器的兩種實現方式:
①BeanFactory
這是 IoC 容器的基本實現,是 Spring 內部使用的接口。面向 Spring 本身,不提供給開發人員使用。
②ApplicationContext
BeanFactory 的子接口,提供了更多高級特性。面向 Spring 的使用者,幾乎所有場合都使用 ApplicationContext 而不是底層的 BeanFactory。
③ApplicationContext的主要實現類
3.2、基于XML管理Bean
3.2.1、搭建子模塊spring6-ioc-xml
①搭建模塊
搭建方式如:spring-first
②引入配置文件
引入spring-first模塊配置文件:beans.xml、log4j2.xml
③添加依賴
<dependencies><!--spring context依賴--><!--當你引入Spring Context依賴之后,表示將Spring的基礎依賴引入了--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.3</version></dependency><!--junit5測試--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.1</version></dependency><!--log4j2的依賴--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.19.0</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j2-impl</artifactId><version>2.19.0</version></dependency>
</dependencies>
④引入java類
引入spring-first模塊java及test目錄下實體類
package com.atguigu.spring6.bean;public class HelloWorld {public HelloWorld() {System.out.println("無參數構造方法執行");}public void sayHello(){System.out.println("helloworld");}
}
package com.atguigu.spring6.bean;import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class HelloWorldTest {private Logger logger = LoggerFactory.getLogger(HelloWorldTest.class);@Testpublic void testHelloWorld(){}
}
3.2.2、實驗一:獲取bean
①方式一:根據id獲取
由于 id 屬性指定了 bean 的唯一標識,所以根據 bean 標簽的 id 屬性可以精確獲取到一個組件對象。上個實驗中我們使用的就是這種方式
②方式二:根據類型獲取
@Test
public void testHelloWorld1(){ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");HelloWorld bean = ac.getBean(HelloWorld.class);bean.sayHello();
}
;
③方式三:根據id和類型
@Test
public void testHelloWorld2(){ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");HelloWorld bean = ac.getBean("helloworld", HelloWorld.class);bean.sayHello();
}
④注意的地方
當根據類型獲取bean時,要求IOC容器中指定類型的bean有且只能有一個
當IOC容器中一共配置了兩個:
<bean id="helloworldOne" class="com.atguigu.spring6.bean.HelloWorld"></bean>
<bean id="helloworldTwo" class="com.atguigu.spring6.bean.HelloWorld"></bean>
根據類型獲取時會拋出異常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.atguigu.spring6.bean.HelloWorld’ available: expected single matching bean but found 2: helloworldOne,helloworldTwo
⑤擴展知識
如果組件類實現了接口,根據接口類型可以獲取 bean 嗎?
可以,前提是bean唯一
如果一個接口有多個實現類,這些實現類都配置了 bean,根據接口類型可以獲取 bean 嗎?
不行,因為bean不唯一
結論
根據類型來獲取bean時,在滿足bean唯一性的前提下,其實只是看:『對象 instanceof 指定的類型』的返回結果,只要返回的是true就可以認定為和類型匹配,能夠獲取到。
java中,instanceof運算符用于判斷前面的對象是否是后面的類,或其子類、實現類的實例。如果是返回true,否則返回false。也就是說:用instanceof關鍵字做判斷時, instanceof 操作符的左右操作必須有繼承或實現關系
3.2.3、實驗二:依賴注入之setter注入
3.2.3、實驗二:依賴注入之setter注入
package com.atguigu.spring6.bean;public class Student {private Integer id;private String name;private Integer age;private String sex;public Student() {}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 Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}@Overridepublic String toString() {return "Student{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", sex='" + sex + '\'' +'}';}}
②配置bean時為屬性賦值
spring-di.xml
<bean id="studentOne" class="com.atguigu.spring6.bean.Student"><!-- property標簽:通過組件類的setXxx()方法給組件對象設置屬性 --><!-- name屬性:指定屬性名(這個屬性名是getXxx()、setXxx()方法定義的,和成員變量無關) --><!-- value屬性:指定屬性值 --><property name="id" value="1001"></property><property name="name" value="張三"></property><property name="age" value="23"></property><property name="sex" value="男"></property>
</bean>
③測試
@Test
public void testDIBySet(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring-di.xml");Student studentOne = ac.getBean("studentOne", Student.class);System.out.println(studentOne);
}
3.2.4、實驗三:依賴注入之構造器注入
①在Student類中添加有參構造
public Student(Integer id, String name, Integer age, String sex) {this.id = id;this.name = name;this.age = age;this.sex = sex;
}
②配置bean
spring-di.xml
<bean id="studentTwo" class="com.atguigu.spring6.bean.Student"><constructor-arg value="1002"></constructor-arg><constructor-arg value="李四"></constructor-arg><constructor-arg value="33"></constructor-arg><constructor-arg value="女"></constructor-arg>
</bean>
注意:
constructor-arg標簽還有兩個屬性可以進一步描述構造器參數:
index屬性:指定參數所在位置的索引(從0開始)
name屬性:指定參數名
③測試
@Test
public void testDIByConstructor(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring-di.xml");Student studentOne = ac.getBean("studentTwo", Student.class);System.out.println(studentOne);
}
3.2.5、實驗四:特殊值處理
①字面量賦值
什么是字面量?int a = 10;聲明一個變量a,初始化為10,此時a就不代表字母a了,而是作為一個變量的名字。當我們引用a的時候,我們實際上拿到的值是10。而如果a是帶引號的:‘a’,那么它現在不是一個變量,它就是代表a這個字母本身,這就是字面量。所以字面量沒有引申含義,就是我們看到的這個數據本身。
<!-- 使用value屬性給bean的屬性賦值時,Spring會把value屬性的值看做字面量 -->
<property name="name" value="張三"/>
②null值
<property name="name"><null />
</property>
注意:<property name="name" value="null"></property>
復制
以上寫法,為name所賦的值是字符串null
③xml實體
<!-- 小于號在XML文檔中用來定義標簽的開始,不能隨便使用 -->
<!-- 解決方案一:使用XML實體來代替 -->
<property name="expression" value="a < b"/>
④CDATA節
<property name="expression"><!-- 解決方案二:使用CDATA節 --><!-- CDATA中的C代表Character,是文本、字符的含義,CDATA就表示純文本數據 --><!-- XML解析器看到CDATA節就知道這里是純文本,就不會當作XML標簽或屬性來解析 --><!-- 所以CDATA節中寫什么符號都隨意 --><value><![CDATA[a < b]]></value>
</property>
3.2.6、實驗五:為對象類型屬性賦值
①創建班級類Clazz
package com.atguigu.spring6.beanpublic class Clazz {private Integer clazzId;private String clazzName;public Integer getClazzId() {return clazzId;}public void setClazzId(Integer clazzId) {this.clazzId = clazzId;}public String getClazzName() {return clazzName;}public void setClazzName(String clazzName) {this.clazzName = clazzName;}@Overridepublic String toString() {return "Clazz{" +"clazzId=" + clazzId +", clazzName='" + clazzName + '\'' +'}';}public Clazz() {}public Clazz(Integer clazzId, String clazzName) {this.clazzId = clazzId;this.clazzName = clazzName;}
}
②修改Student類
在Student類中添加以下代碼:
private Clazz clazz;public Clazz getClazz() {return clazz;
}public void setClazz(Clazz clazz) {this.clazz = clazz;
}
方式一:引用外部bean
配置Clazz類型的bean:
<bean id="clazzOne" class="com.atguigu.spring6.bean.Clazz"><property name="clazzId" value="1111"></property><property name="clazzName" value="財源滾滾班"></property>
</bean>
為Student中的clazz屬性賦值:
<bean id="studentFour" class="com.atguigu.spring6.bean.Student"><property name="id" value="1004"></property><property name="name" value="趙六"></property><property name="age" value="26"></property><property name="sex" value="女"></property><!-- ref屬性:引用IOC容器中某個bean的id,將所對應的bean為屬性賦值 --><property name="clazz" ref="clazzOne"></property>
</bean>
錯誤演示:
<bean id="studentFour" class="com.atguigu.spring6.bean.Student"><property name="id" value="1004"></property><property name="name" value="趙六"></property><property name="age" value="26"></property><property name="sex" value="女"></property><property name="clazz" value="clazzOne"></property>
</bean>
如果錯把ref屬性寫成了value屬性,會拋出異常:
Caused by: java.lang.IllegalStateException: Cannot convert
value of type ‘java.lang.String’ to required type‘com.atguigu.spring6.bean.Clazz’ for property ‘clazz’: no matching editors or conversion strategy found意思是不能把String類型轉換成我們要的Clazz類型,
說明我們使用value屬性時,Spring只把這個屬性看做一個普通的字符串,
不會認為這是一個bean的id,更不會根據它去找到bean來賦值
方式二:內部bean
<bean id="studentFour" class="com.atguigu.spring6.bean.Student"><property name="id" value="1004"></property><property name="name" value="趙六"></property><property name="age" value="26"></property><property name="sex" value="女"></property><property name="clazz"><!-- 在一個bean中再聲明一個bean就是內部bean --><!-- 內部bean只能用于給屬性賦值,不能在外部通過IOC容器獲取,因此可以省略id屬性 --><bean id="clazzInner" class="com.atguigu.spring6.bean.Clazz"><property name="clazzId" value="2222"></property><property name="clazzName" value="遠大前程班"></property></bean></property>
</bean>
方式三:級聯屬性賦值
<bean id="studentFour" class="com.atguigu.spring6.bean.Student"><property name="id" value="1004"></property><property name="name" value="趙六"></property><property name="age" value="26"></property><property name="sex" value="女"></property><property name="clazz" ref="clazzOne"></property><property name="clazz.clazzId" value="3333"></property><property name="clazz.clazzName" value="最強王者班"></property>
</bean>
3.2.7、實驗六:為數組類型屬性賦值
①修改Student類
在Student類中添加以下代碼:
private String[] hobbies;public String[] getHobbies() {return hobbies;
}public void setHobbies(String[] hobbies) {this.hobbies = hobbies;
}
②配置bean
<bean id="studentFour" class="com.atguigu.spring.bean6.Student"><property name="id" value="1004"></property><property name="name" value="趙六"></property><property name="age" value="26"></property><property name="sex" value="女"></property><!-- ref屬性:引用IOC容器中某個bean的id,將所對應的bean為屬性賦值 --><property name="clazz" ref="clazzOne"></property><property name="hobbies"><array><value>抽煙</value><value>喝酒</value><value>燙頭</value></array></property>
</bean>
3.2.8、實驗七:為集合類型屬性賦值
①為List集合類型屬性賦值
在Clazz類中添加以下代碼:
private List<Student> students;public List<Student> getStudents() {return students;
}public void setStudents(List<Student> students) {this.students = students;
}
配置bean:
<bean id="clazzTwo" class="com.atguigu.spring6.bean.Clazz"><property name="clazzId" value="4444"></property><property name="clazzName" value="Javaee0222"></property><property name="students"><list><ref bean="studentOne"></ref><ref bean="studentTwo"></ref><ref bean="studentThree"></ref></list></property>
</bean>
若為Set集合類型屬性賦值,只需要將其中的list標簽改為set標簽即可
②為Map集合類型屬性賦值
創建教師類Teacher:
package com.atguigu.spring6.bean;
public class Teacher {private Integer teacherId;private String teacherName;public Integer getTeacherId() {return teacherId;}public void setTeacherId(Integer teacherId) {this.teacherId = teacherId;}public String getTeacherName() {return teacherName;}public void setTeacherName(String teacherName) {this.teacherName = teacherName;}public Teacher(Integer teacherId, String teacherName) {this.teacherId = teacherId;this.teacherName = teacherName;}public Teacher() {}@Overridepublic String toString() {return "Teacher{" +"teacherId=" + teacherId +", teacherName='" + teacherName + '\'' +'}';}
}
在Student類中添加以下代碼:
private Map<String, Teacher> teacherMap;public Map<String, Teacher> getTeacherMap() {return teacherMap;
}public void setTeacherMap(Map<String, Teacher> teacherMap) {this.teacherMap = teacherMap;
}
配置bean:
<bean id="teacherOne" class="com.atguigu.spring6.bean.Teacher"><property name="teacherId" value="10010"></property><property name="teacherName" value="大寶"></property>
</bean><bean id="teacherTwo" class="com.atguigu.spring6.bean.Teacher"><property name="teacherId" value="10086"></property><property name="teacherName" value="二寶"></property>
</bean><bean id="studentFour" class="com.atguigu.spring6.bean.Student"><property name="id" value="1004"></property><property name="name" value="趙六"></property><property name="age" value="26"></property><property name="sex" value="女"></property><!-- ref屬性:引用IOC容器中某個bean的id,將所對應的bean為屬性賦值 --><property name="clazz" ref="clazzOne"></property><property name="hobbies"><array><value>抽煙</value><value>喝酒</value><value>燙頭</value></array></property><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>
③引用集合類型的bean
<!--list集合類型的bean-->
<util:list id="students"><ref bean="studentOne"></ref><ref bean="studentTwo"></ref><ref bean="studentThree"></ref>
</util:list>
<!--map集合類型的bean-->
<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>
<bean id="clazzTwo" class="com.atguigu.spring6.bean.Clazz"><property name="clazzId" value="4444"></property><property name="clazzName" value="Javaee0222"></property><property name="students" ref="students"></property>
</bean>
<bean id="studentFour" class="com.atguigu.spring6.bean.Student"><property name="id" value="1004"></property><property name="name" value="趙六"></property><property name="age" value="26"></property><property name="sex" value="女"></property><!-- ref屬性:引用IOC容器中某個bean的id,將所對應的bean為屬性賦值 --><property name="clazz" ref="clazzOne"></property><property name="hobbies"><array><value>抽煙</value><value>喝酒</value><value>燙頭</value></array></property><property name="teacherMap" ref="teacherMap"></property>
</bean>
使用util:list、util: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/utilhttp://www.springframework.org/schema/util/spring-util.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">
3.2.9、實驗八:p命名空間
引入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/utilhttp://www.springframework.org/schema/util/spring-util.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">
引入p命名空間后,可以通過以下方式為bean的各個屬性賦值
<bean id="studentSix" class="com.atguigu.spring6.bean.Student"p:id="1006" p:name="小明" p:clazz-ref="clazzOne" p:teacherMap-ref="teacherMap"></bean>
3.2.10、實驗九:引入外部屬性文件
①加入依賴
<!-- 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>
②創建外部屬性文件
jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
③引入屬性文件
引入context 名稱空間
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"></beans>
<!-- 引入外部屬性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
注意:在使用 context:property-placeholder 元素加載外包配置文件功能前,首先需要在 XML 配置的一級標簽 中添加 context 相關的約束。
④配置bean
@Test
public void testDataSource() throws SQLException {ApplicationContext ac = new ClassPathXmlApplicationContext("spring-datasource.xml");DataSource dataSource = ac.getBean(DataSource.class);Connection connection = dataSource.getConnection();System.out.println(connection);
}
3.2.11、實驗十:bean的作用域
①概念
在Spring中可以通過配置bean標簽的scope屬性來指定bean的作用域范圍,各取值含義參加下表:
②創建類User
package com.atguigu.spring6.bean;
public class User {private Integer id;private String username;private String password;private Integer age;public User() {}public User(Integer id, String username, String password, Integer age) {this.id = id;this.username = username;this.password = password;this.age = age;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", age=" + age +'}';}
}
③配置bean
<!-- scope屬性:取值singleton(默認值),bean在IOC容器中只有一個實例,IOC容器初始化時創建對象 -->
<!-- scope屬性:取值prototype,bean在IOC容器中可以有多個實例,getBean()時創建對象 -->
<bean class="com.atguigu.spring6.bean.User" scope="prototype"></bean>
④測試
@Test
public void testBeanScope(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring-scope.xml");User user1 = ac.getBean(User.class);User user2 = ac.getBean(User.class);System.out.println(user1==user2);
}
3.2.12、實驗十一:bean生命周期
①具體的生命周期過程
bean對象創建(調用無參構造器)
給bean對象設置屬性
bean的后置處理器(初始化之前)
bean對象初始化(需在配置bean時指定初始化方法)
bean的后置處理器(初始化之后)
bean對象就緒可以使用
bean對象銷毀(需在配置bean時指定銷毀方法)
IOC容器關閉
修改類User
public class User {private Integer id;private String username;private String password;private Integer age;public User() {System.out.println("生命周期:1、創建對象");}public User(Integer id, String username, String password, Integer age) {this.id = id;this.username = username;this.password = password;this.age = age;}public Integer getId() {return id;}public void setId(Integer id) {System.out.println("生命周期:2、依賴注入");this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public void initMethod(){System.out.println("生命周期:3、初始化");}public void destroyMethod(){System.out.println("生命周期:5、銷毀");}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", age=" + age +'}';}
}
注意其中的initMethod()和destroyMethod(),可以通過配置bean指定為初始化和銷毀的方法
```javascript
<!-- 使用init-method屬性指定初始化方法 -->
<!-- 使用destroy-method屬性指定銷毀方法 -->
<bean class="com.atguigu.spring6.bean.User" scope="prototype" init-method="initMethod" destroy-method="destroyMethod"><property name="id" value="1001"></property><property name="username" value="admin"></property><property name="password" value="123456"></property><property name="age" value="23"></property>
</bean>
④測試
@Test
public void testLife(){ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-lifecycle.xml");User bean = ac.getBean(User.class);System.out.println("生命周期:4、通過IOC容器獲取bean并使用");ac.close();
}
⑤bean的后置處理器
bean的后置處理器會在生命周期的初始化前后添加額外的操作,需要實現BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置處理器不是單獨針對某一個bean生效,而是針對IOC容器中所有bean都會執行
創建bean的后置處理器:
package com.atguigu.spring6.process;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;public class MyBeanProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("☆☆☆" + beanName + " = " + bean);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("★★★" + beanName + " = " + bean);return bean;}
}
在IOC容器中配置后置處理器:
<!-- bean的后置處理器要放入IOC容器才能生效 -->
<bean id="myBeanProcessor" class="com.atguigu.spring6.process.MyBeanProcessor"/>
3.2.13、實驗十二:FactoryBean
①簡介
FactoryBean是Spring提供的一種整合第三方框架的常用機制。和普通的bean不同,配置一個FactoryBean類型的bean,在獲取bean的時候得到的并不是class屬性中配置的這個類的對象,而是getObject()方法的返回值。通過這種機制,Spring可以幫我們把復雜組件創建的詳細過程和繁瑣細節都屏蔽起來,只把最簡潔的使用界面展示給我們。
將來我們整合Mybatis時,Spring就是通過FactoryBean機制來幫我們創建SqlSessionFactory對象的。
/** Copyright 2002-2020 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package org.springframework.beans.factory;import org.springframework.lang.Nullable;/*** Interface to be implemented by objects used within a {@link BeanFactory} which* are themselves factories for individual objects. If a bean implements this* interface, it is used as a factory for an object to expose, not directly as a* bean instance that will be exposed itself.** <p><b>NB: A bean that implements this interface cannot be used as a normal bean.</b>* A FactoryBean is defined in a bean style, but the object exposed for bean* references ({@link #getObject()}) is always the object that it creates.** <p>FactoryBeans can support singletons and prototypes, and can either create* objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}* interface allows for exposing more fine-grained behavioral metadata.** <p>This interface is heavily used within the framework itself, for example for* the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the* {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for* custom components as well; however, this is only common for infrastructure code.** <p><b>{@code FactoryBean} is a programmatic contract. Implementations are not* supposed to rely on annotation-driven injection or other reflective facilities.</b>* {@link #getObjectType()} {@link #getObject()} invocations may arrive early in the* bootstrap process, even ahead of any post-processor setup. If you need access to* other beans, implement {@link BeanFactoryAware} and obtain them programmatically.** <p><b>The container is only responsible for managing the lifecycle of the FactoryBean* instance, not the lifecycle of the objects created by the FactoryBean.</b> Therefore,* a destroy method on an exposed bean object (such as {@link java.io.Closeable#close()}* will <i>not</i> be called automatically. Instead, a FactoryBean should implement* {@link DisposableBean} and delegate any such close call to the underlying object.** <p>Finally, FactoryBean objects participate in the containing BeanFactory's* synchronization of bean creation. There is usually no need for internal* synchronization other than for purposes of lazy initialization within the* FactoryBean itself (or the like).** @author Rod Johnson* @author Juergen Hoeller* @since 08.03.2003* @param <T> the bean type* @see org.springframework.beans.factory.BeanFactory* @see org.springframework.aop.framework.ProxyFactoryBean* @see org.springframework.jndi.JndiObjectFactoryBean*/
public interface FactoryBean<T> {/*** The name of an attribute that can be* {@link org.springframework.core.AttributeAccessor#setAttribute set} on a* {@link org.springframework.beans.factory.config.BeanDefinition} so that* factory beans can signal their object type when it can't be deduced from* the factory bean class.* @since 5.2*/String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";/*** Return an instance (possibly shared or independent) of the object* managed by this factory.* <p>As with a {@link BeanFactory}, this allows support for both the* Singleton and Prototype design pattern.* <p>If this FactoryBean is not fully initialized yet at the time of* the call (for example because it is involved in a circular reference),* throw a corresponding {@link FactoryBeanNotInitializedException}.* <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}* objects. The factory will consider this as normal value to be used; it* will not throw a FactoryBeanNotInitializedException in this case anymore.* FactoryBean implementations are encouraged to throw* FactoryBeanNotInitializedException themselves now, as appropriate.* @return an instance of the bean (can be {@code null})* @throws Exception in case of creation errors* @see FactoryBeanNotInitializedException*/@NullableT getObject() throws Exception;/*** Return the type of object that this FactoryBean creates,* or {@code null} if not known in advance.* <p>This allows one to check for specific types of beans without* instantiating objects, for example on autowiring.* <p>In the case of implementations that are creating a singleton object,* this method should try to avoid singleton creation as far as possible;* it should rather estimate the type in advance.* For prototypes, returning a meaningful type here is advisable too.* <p>This method can be called <i>before</i> this FactoryBean has* been fully initialized. It must not rely on state created during* initialization; of course, it can still use such state if available.* <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return* {@code null} here. Therefore it is highly recommended to implement* this method properly, using the current state of the FactoryBean.* @return the type of object that this FactoryBean creates,* or {@code null} if not known at the time of the call* @see ListableBeanFactory#getBeansOfType*/@NullableClass<?> getObjectType();/*** Is the object managed by this factory a singleton? That is,* will {@link #getObject()} always return the same object* (a reference that can be cached)?* <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,* the object returned from {@code getObject()} might get cached* by the owning BeanFactory. Hence, do not return {@code true}* unless the FactoryBean always exposes the same reference.* <p>The singleton status of the FactoryBean itself will generally* be provided by the owning BeanFactory; usually, it has to be* defined as singleton there.* <p><b>NOTE:</b> This method returning {@code false} does not* necessarily indicate that returned objects are independent instances.* An implementation of the extended {@link SmartFactoryBean} interface* may explicitly indicate independent instances through its* {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}* implementations which do not implement this extended interface are* simply assumed to always return independent instances if the* {@code isSingleton()} implementation returns {@code false}.* <p>The default implementation returns {@code true}, since a* {@code FactoryBean} typically manages a singleton instance.* @return whether the exposed object is a singleton* @see #getObject()* @see SmartFactoryBean#isPrototype()*/default boolean isSingleton() {return true;}
}
②創建類UserFactoryBean
package com.atguigu.spring6.bean;
public class UserFactoryBean implements FactoryBean<User> {@Overridepublic User getObject() throws Exception {return new User();}@Overridepublic Class<?> getObjectType() {return User.class;}
}
③配置bean
<bean id="user" class="com.atguigu.spring6.bean.UserFactoryBean"></bean>
④測試
@Test
public void testUserFactoryBean(){//獲取IOC容器ApplicationContext ac = new ClassPathXmlApplicationContext("spring-factorybean.xml");User user = (User) ac.getBean("user");System.out.println(user);
}
3.2.14、實驗十三:基于xml自動裝配
自動裝配:根據指定的策略,在IOC容器中匹配某一個bean,自動為指定的bean中所依賴的類類型或接口類型屬性賦值
①場景模擬
創建類UserController
package com.atguigu.spring6.autowire.controller
public class UserController {private UserService userService;public void setUserService(UserService userService) {this.userService = userService;}public void saveUser(){userService.saveUser();}}
創建接口UserService
package com.atguigu.spring6.autowire.service
public interface UserService {void saveUser();}
創建類UserServiceImpl實現接口UserService
package com.atguigu.spring6.autowire.service.impl
public class UserServiceImpl implements UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}@Overridepublic void saveUser() {userDao.saveUser();}}
創建接口UserDao
package com.atguigu.spring6.autowire.dao
public interface UserDao {void saveUser();}
創建類UserDaoImpl實現接口UserDao
package com.atguigu.spring6.autowire.dao.impl
public class UserDaoImpl implements UserDao {@Overridepublic void saveUser() {System.out.println("保存成功");}}
②配置bean
使用bean標簽的autowire屬性設置自動裝配效果自動裝配方式:byTypebyType:根據類型匹配IOC容器中的某個兼容類型的bean,為屬性自動賦值若在IOC中,沒有任何一個兼容類型的bean能夠為屬性賦值,則該屬性不裝配,即值為默認值null若在IOC中,有多個兼容類型的bean能夠為屬性賦值,則拋出異常NoUniqueBeanDefinitionException
<bean id="userController" class="com.atguigu.spring6.autowire.controller.UserController" autowire="byType"></bean><bean id="userService" class="com.atguigu.spring6.autowire.service.impl.UserServiceImpl" autowire="byType"></bean><bean id="userDao" class="com.atguigu.spring6.autowire.dao.impl.UserDaoImpl"></bean>
自動裝配方式:byNamebyName:將自動裝配的屬性的屬性名,作為bean的id在IOC容器中匹配相對應的bean進行賦值
<bean id="userController" class="com.atguigu.spring6.autowire.controller.UserController" autowire="byName"></bean><bean id="userService" class="com.atguigu.spring6.autowire.service.impl.UserServiceImpl" autowire="byName"></bean>
<bean id="userServiceImpl" class="com.atguigu.spring6.autowire.service.impl.UserServiceImpl" autowire="byName"></bean><bean id="userDao" class="com.atguigu.spring6.autowire.dao.impl.UserDaoImpl"></bean>
<bean id="userDaoImpl" class="com.atguigu.spring6.autowire.dao.impl.UserDaoImpl"></bean>
③測試
@Test
public void testAutoWireByXML(){ApplicationContext ac = new ClassPathXmlApplicationContext("autowire-xml.xml");UserController userController = ac.getBean(UserController.class);userController.saveUser();
}
3.3、基于注解管理Bean(☆)
從 Java 5 開始,Java 增加了對注解(Annotation)的支持,它是代碼中的一種特殊標記,可以在編譯、類加載和運行時被讀取,執行相應的處理。開發人員可以通過注解在不改變原有代碼和邏輯的情況下,在源代碼中嵌入補充信息。
Spring 從 2.5 版本開始提供了對注解技術的全面支持,我們可以使用注解來實現自動裝配,簡化 Spring 的 XML 配置。
Spring 通過注解實現自動裝配的步驟如下:
引入依賴
開啟組件掃描
使用注解定義 Bean
依賴注入
3.3.1、搭建子模塊spring6-ioc-annotation
①搭建模塊
搭建方式如:spring6-ioc-xml
②引入配置文件
引入spring-ioc-xml模塊日志log4j2.xml
③添加依賴
<dependencies><!--spring context依賴--><!--當你引入Spring Context依賴之后,表示將Spring的基礎依賴引入了--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.3</version></dependency><!--junit5測試--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId></dependency><!--log4j2的依賴--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.19.0</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j2-impl</artifactId><version>2.19.0</version></dependency>
</dependencies>
3.3.2、開啟組件掃描
Spring 默認不使用注解裝配 Bean,因此我們需要在 Spring 的 XML 配置中,通過 context:component-scan 元素開啟 Spring Beans的自動掃描功能。開啟此功能后,Spring 會自動從掃描指定的包(base-package 屬性設置)及其子包下的所有類,如果類上使用了 @Component 注解,就將該類裝配到容器中。
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--開啟組件掃描功能--><context:component-scan base-package="com.atguigu.spring6"></context:component-scan>
</beans>
注意:在使用 context:component-scan 元素開啟自動掃描功能前,首先需要在 XML 配置的一級標簽 中添加 context 相關的約束。
情況一:最基本的掃描方式
<context:component-scan base-package="com.atguigu.spring6">
</context:component-scan>
情況二:指定要排除的組件
<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>
情況三:僅掃描指定組件
<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>
3.3.3、使用注解定義 Bean
Spring 提供了以下多個注解,這些注解可以直接標注在 Java 類上,將它們定義成 Spring Bean。
3.3.4、實驗一:@Autowired注入
單獨使用@Autowired注解,默認根據類型裝配。【默認是byType】
查看源碼:
package org.springframework.beans.factory.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@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存在或者不存在都沒關系,存在的話就注入,不存在的話,也不報錯。
①場景一:屬性注入
創建UserDao接口
package com.atguigu.spring6.dao;public interface UserDao {public void print();
}
創建UserDaoImpl實現
package com.atguigu.spring6.dao.impl;import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;@Repository
public class UserDaoImpl implements UserDao {@Overridepublic void print() {System.out.println("Dao層執行結束");}
}
創建UserService接口
package com.atguigu.spring6.service;public interface UserService {public void out();
}
創建UserServiceImpl實現類
package com.atguigu.spring6.service.impl;import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Overridepublic void out() {userDao.print();System.out.println("Service層執行結束");}
}
創建UserController類
package com.atguigu.spring6.controller;import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController {@Autowiredprivate UserService userService;public void out() {userService.out();System.out.println("Controller層執行結束。");}}
測試一
package com.atguigu.spring6.bean;import com.atguigu.spring6.controller.UserController;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class UserTest {private Logger logger = LoggerFactory.getLogger(UserTest.class);@Testpublic void testAnnotation(){ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");UserController userController = context.getBean("userController", UserController.class);userController.out();logger.info("執行成功");}}
測試結果:
以上構造方法和setter方法都沒有提供,經過測試,仍然可以注入成功。
②場景二:set注入
修改UserServiceImpl類
package com.atguigu.spring6.service.impl;import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {private UserDao userDao;@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}@Overridepublic void out() {userDao.print();System.out.println("Service層執行結束");}
}
修改UserController類
package com.atguigu.spring6.controller;import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController {private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public void out() {userService.out();System.out.println("Controller層執行結束。");}}
測試:成功調用
③場景三:構造方法注入
修改UserServiceImpl類
package com.atguigu.spring6.service.impl;import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {private UserDao userDao;@Autowiredpublic UserServiceImpl(UserDao userDao) {this.userDao = userDao;}@Overridepublic void out() {userDao.print();System.out.println("Service層執行結束");}
}
修改UserController類
package com.atguigu.spring6.controller;import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController {private UserService userService;@Autowiredpublic UserController(UserService userService) {this.userService = userService;}public void out() {userService.out();System.out.println("Controller層執行結束。");}}
測試:成功調用
④場景四:形參上注入
修改UserServiceImpl類
package com.atguigu.spring6.service.impl;import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
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;}@Overridepublic void out() {userDao.print();System.out.println("Service層執行結束");}
}
修改UserController類
package com.atguigu.spring6.controller;import com.atguigu.spring6.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 out() {userService.out();System.out.println("Controller層執行結束。");}}
測試:成功調用
⑤場景五:只有一個構造函數,無注解
修改UserServiceImpl類
package com.atguigu.spring6.service.impl;import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
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 {@Autowiredprivate UserDao userDao;public UserServiceImpl(UserDao userDao) {this.userDao = userDao;}@Overridepublic void out() {userDao.print();System.out.println("Service層執行結束");}
}
測試通過
當有參數的構造方法只有一個時,@Autowired注解可以省略。
說明:有多個構造方法時呢?大家可以測試(再添加一個無參構造函數),測試報錯
⑥場景六:@Autowired注解和@Qualifier注解聯合
添加dao層實現
package com.atguigu.spring6.dao.impl;import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;@Repository
public class UserDaoRedisImpl implements UserDao {@Overridepublic void print() {System.out.println("Redis Dao層執行結束");}
}
測試:測試異常
錯誤信息中說:不能裝配,UserDao這個Bean的數量等于2
怎么解決這個問題呢?當然要byName,根據名稱進行裝配了。
修改UserServiceImpl類
package com.atguigu.spring6.service.impl;import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {@Autowired@Qualifier("userDaoImpl") // 指定bean的名字private UserDao userDao;@Overridepublic void out() {userDao.print();System.out.println("Service層執行結束");}
}
總結
@Autowired注解可以出現在:屬性上、構造方法上、構造方法的參數上、setter方法上。
當帶參數的構造方法只有一個,@Autowired注解可以省略。()
@Autowired注解默認根據類型注入。如果要根據名稱注入的話,需要配合@Qualifier注解一起使用。
3.3.5、實驗二:@Resource注入
@Resource注解也可以完成屬性注入。那它和@Autowired注解有什么區別?
@Resource注解是JDK擴展包中的,也就是說屬于JDK的一部分。所以該注解是標準注解,更加具有通用性。(JSR-250標準中制定的注解類型。JSR是Java規范提案。)
@Autowired注解是Spring框架自己的。
@Resource注解默認根據名稱裝配byName,未指定name時,使用屬性名作為name。通過name找不到的話會自動啟動通過類型byType裝配。
@Autowired注解默認根據類型裝配byType,如果想根據名稱裝配,需要配合@Qualifier注解一起用。
@Resource注解用在屬性上、setter方法上。
@Autowired注解用在屬性上、setter方法上、構造方法上、構造方法參數上。
@Resource注解屬于JDK擴展包,所以不在JDK當中,需要額外引入以下依賴:【如果是JDK8的話不需要額外引入依賴。高于JDK11或低于JDK8需要引入以下依賴。】
<dependency><groupId>jakarta.annotation</groupId><artifactId>jakarta.annotation-api</artifactId><version>2.1.1</version>
</dependency>
源碼:
package jakarta.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Resources.class)
public @interface Resource {String name() default "";String lookup() default "";Class<?> type() default Object.class;Resource.AuthenticationType authenticationType() default Resource.AuthenticationType.CONTAINER;boolean shareable() default true;String mappedName() default "";String description() default "";public static enum AuthenticationType {CONTAINER,APPLICATION;private AuthenticationType() {}}
}
①場景一:根據name注入
修改UserDaoImpl類
package com.atguigu.spring6.dao.impl;import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;@Repository("myUserDao")
public class UserDaoImpl implements UserDao {@Overridepublic void print() {System.out.println("Dao層執行結束");}
}
修改UserServiceImpl類
package com.atguigu.spring6.service.impl;import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import jakarta.annotation.Resource;
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 {@Resource(name = "myUserDao")private UserDao myUserDao;@Overridepublic void out() {myUserDao.print();System.out.println("Service層執行結束");}
}
測試通過
②場景二:name未知注入
修改UserDaoImpl類
package com.atguigu.spring6.dao.impl;import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;@Repository("myUserDao")
public class UserDaoImpl implements UserDao {@Overridepublic void print() {System.out.println("Dao層執行結束");}
}
修改UserServiceImpl類
package com.atguigu.spring6.service.impl;import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import jakarta.annotation.Resource;
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 {@Resourceprivate UserDao myUserDao;@Overridepublic void out() {myUserDao.print();System.out.println("Service層執行結束");}
}
測試通過
當@Resource注解使用時沒有指定name的時候,還是根據name進行查找,這個name是屬性名。
③場景三 其他情況
修改UserServiceImpl類,userDao1屬性名不存在
package com.atguigu.spring6.service.impl;import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import jakarta.annotation.Resource;
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 {@Resourceprivate UserDao userDao1;@Overridepublic void out() {userDao1.print();System.out.println("Service層執行結束");}
}
測試異常
根據異常信息得知:顯然當通過name找不到的時候,自然會啟動byType進行注入,以上的錯誤是因為UserDao接口下有兩個實現類導致的。所以根據類型注入就會報錯。
@Resource的set注入可以自行測試
總結:
@Resource注解:默認byName注入,沒有指定name時把屬性名當做name,根據name找不到時,才會byType注入。byType注入時,某種類型的Bean只能有一個
3.3.6、Spring全注解開發
全注解開發就是不再使用spring配置文件了,寫一個配置類來代替配置文件。
package com.atguigu.spring6.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
//@ComponentScan({"com.atguigu.spring6.controller", "com.atguigu.spring6.service","com.atguigu.spring6.dao"})
@ComponentScan("com.atguigu.spring6")
public class Spring6Config {
}
測試類
@Test
public void testAllAnnotation(){ApplicationContext context = new AnnotationConfigApplicationContext(Spring6Config.class);UserController userController = context.getBean("userController", UserController.class);userController.out();logger.info("執行成功");
}