spring6:3容器:IoC
目錄
- spring6:3容器:IoC
- 3、容器:IoC
- 3.1、IoC容器
- 3.1.1、控制反轉(IoC)
- 3.1.2、依賴注入
- 3.1.3、IoC容器在Spring的實現
- 3.2、基于XML管理Bean
- 3.2.1、搭建子模塊spring6-ioc-xml
- 3.2.2、實驗一:獲取bean
- ①方式一:根據id獲取
- ②方式二:根據類型獲取
- ③方式三:根據id和類型
- ④注意的地方
- ⑤擴展知識
- 3.2.3、實驗二:依賴注入之setter注入
- 3.2.4、實驗三:依賴注入之構造器注入
- 3.2.5、實驗四:特殊值處理
- ①字面量賦值
- ②null值
- ③xml實體
- ④CDATA節
- 3.2.6、實驗五:為對象類型屬性賦值
- 方式一:引用外部bean
- 方式二:內部bean
- 方式三:級聯屬性賦值
- 3.2.7、實驗六:為數組類型屬性賦值
- 3.2.8、實驗七:為集合類型屬性賦值
- ①為List集合類型屬性賦值
- ②為Map集合類型屬性賦值
- ③引用集合類型的bean
- 3.2.9、實驗八:p命名空間
- 3.2.10、實驗九:引入外部屬性文件
- 3.2.11、實驗十:bean的作用域
- 3.2.12、實驗十一:bean生命周期
- 3.2.13、實驗十二:FactoryBean
- 3.2.14、實驗十三:基于xml自動裝配
- 3.3、基于注解管理Bean(☆)
- 3.3.1、搭建子模塊spring6-ioc-annotation
- 3.3.2、開啟組件掃描
- 3.3.3、使用注解定義 Bean
- 3.3.4、實驗一:@Autowired注入
- ①場景一:屬性注入
- ②場景二:set注入
- ③場景三:構造方法注入
- ④場景四:形參上注入
- ⑤場景五:只有一個構造函數,無注解
- ⑥場景六:@Autowired注解和@Qualifier注解聯合
- 3.3.5、實驗二:@Resource注入
- ①場景一:根據name注入
- ②場景二:name未知注入
- ③場景三 其他情況
- 3.3.6、Spring全注解開發
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的主要實現類
類型名 | 簡介 |
---|---|
ClassPathXmlApplicationContext | 通過讀取類路徑下的 XML 格式的配置文件創建 IOC 容器對象 |
FileSystemXmlApplicationContext | 通過文件系統路徑讀取 XML 格式的配置文件創建 IOC 容器對象 |
ConfigurableApplicationContext | ApplicationContext 的子接口,包含一些擴展方法 refresh() 和 close() ,讓 ApplicationContext 具有啟動、關閉和刷新上下文的能力。 |
WebApplicationContext | 專門為 Web 應用準備,基于 Web 環境創建 IOC 容器對象,并將對象引入存入 ServletContext 域中。 |
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注入
①創建學生類Student
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
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"/><property name="driverClassName" value="${jdbc.driver}"/><property name="username" value="${jdbc.user}"/><property name="password" value="${jdbc.password}"/>
</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的作用域范圍,各取值含義參加下表:
取值 | 含義 | 創建對象的時機 |
---|---|---|
singleton(默認) | 在IOC容器中,這個bean的對象始終為單實例 | IOC容器初始化時 |
prototype | 這個bean在IOC容器中有多個實例 | 獲取bean時 |
如果是在WebApplicationContext環境下還會有另外幾個作用域(但不常用):
取值 | 含義 |
---|---|
request | 在一個請求范圍內有效 |
session | 在一個會話范圍內有效 |
②創建類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指定為初始化和銷毀的方法
③配置bean
<!-- 使用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屬性設置自動裝配效果
自動裝配方式:byType
byType:根據類型匹配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>
自動裝配方式:byName
byName:將自動裝配的屬性的屬性名,作為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。
注解 | 說明 |
---|---|
@Component | 該注解用于描述 Spring 中的 Bean,它是一個泛化的概念,僅僅表示容器中的一個組件(Bean),并且可以作用在應用的任何層次,例如 Service 層、Dao 層等。 使用時只需將該注解標注在相應類上即可。 |
@Repository | 該注解用于將數據訪問層(Dao 層)的類標識為 Spring 中的 Bean,其功能與 @Component 相同。 |
@Service | 該注解通常作用在業務層(Service 層),用于將業務層的類標識為 Spring 中的 Bean,其功能與 @Component 相同。 |
@Controller | 該注解通常作用在控制層(如SpringMVC 的 Controller),用于將控制層的類標識為 Spring 中的 Bean,其功能與 @Component 相同。 |
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("執行成功");
}