spring6:3容器:IoC

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的主要實現類

iamges

類型名簡介
ClassPathXmlApplicationContext通過讀取類路徑下的 XML 格式的配置文件創建 IOC 容器對象
FileSystemXmlApplicationContext通過文件系統路徑讀取 XML 格式的配置文件創建 IOC 容器對象
ConfigurableApplicationContextApplicationContext 的子接口,包含一些擴展方法 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 &lt; 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>

②創建外部屬性文件

images

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 通過注解實現自動裝配的步驟如下:

  1. 引入依賴
  2. 開啟組件掃描
  3. 使用注解定義 Bean
  4. 依賴注入
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("執行成功");}}

測試結果:

image-20221101153556681

以上構造方法和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("執行成功");
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/62219.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62219.shtml
英文地址,請注明出處:http://en.pswp.cn/web/62219.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【認證法規】安全隔離變壓器

文章目錄 定義反激電源變壓器 定義 安全隔離變壓器&#xff08;safety isolating transformer&#xff09;&#xff0c;通過至少相當于雙重絕緣或加強絕緣的絕緣使輸入繞組與輸出繞組在電氣上分開的變壓器。這種變壓器是為以安全特低電壓向配電電路、電器或其它設備供電而設計…

車機端同步outlook日歷

最近在開發一個車機上的日歷助手&#xff0c;其中一個需求就是要實現手機端日歷和車機端日歷數據的同步。然而這種需求似乎沒辦法實現&#xff0c;畢竟手機日歷是手機廠商自己帶的系統應用&#xff0c;根本不能和車機端實現數據同步的。 那么只能去其他公共的平臺尋求一些機會&…

OpenCV-圖像閾值

簡單閾值法 此方法是直截了當的。如果像素值大于閾值&#xff0c;則會被賦為一個值&#xff08;可能為白色&#xff09;&#xff0c;否則會賦為另一個值&#xff08;可能為黑色&#xff09;。使用的函數是 cv.threshold。第一個參數是源圖像&#xff0c;它應該是灰度圖像。第二…

力扣300.最長遞增子序列

題目描述 題目鏈接300. 最長遞增子序列 給你一個整數數組 nums &#xff0c;找到其中最長嚴格遞增子序列的長度。 子序列 是由數組派生而來的序列&#xff0c;刪除&#xff08;或不刪除&#xff09;數組中的元素而不改變其余元素的順序。例如&#xff0c;[3,6,2,7] 是數組 […

Vue CLI的作用

Vue CLI&#xff08;Command Line Interface&#xff09;是一個基于Vue.js的官方腳手架工具&#xff0c;其主要作用是幫助開發者快速搭建Vue項目的基礎結構和開發環境。以下是Vue CLI的具體作用&#xff1a; 1、項目模板與快速生成 Vue CLI提供了一系列預設的項目模板&#x…

【藍橋杯每日一題】掃雷

掃雷 知識點 2024-12-3 藍橋杯每日一題 掃雷 dfs &#xff08;bfs也是可行的&#xff09; 題目大意 在一個二維平面上放置這N個炸雷&#xff0c;每個炸雷的信息有$(x_i,y_i,r_i) $&#xff0c;前兩個是坐標信息&#xff0c;第三個是爆炸半徑。然后會輸入M個排雷火箭&#xff0…

【大數據學習 | 面經】Spark 3.x 中的AQE(自適應查詢執行)

Spark 3.x 中的自適應查詢執行&#xff08;Adaptive Query Execution&#xff0c;簡稱 AQE&#xff09;通過多種方式提升性能&#xff0c;主要包括以下幾個方面&#xff1a; 動態合并 Shuffle 分區&#xff08;Coalescing Post Shuffle Partitions&#xff09;&#xff1a; 當 …

城電科技 | 光伏景觀長廊 打造美麗鄉村綠色低碳示范區 光伏景觀設計方案

光伏景觀長廊是一種結合了光伏發電技術和零碳景觀設計的新型公共公共設施&#xff0c;光伏景觀長廊頂上的光伏板不僅可以為周邊用電設備提供清潔電能&#xff0c;而且還能作為遮陽設施使用&#xff0c;為人們提供一個美麗又實用的休閑娛樂空間。 光伏景觀長廊建設對打造美麗鄉…

開發系統準備與開發環境配置總結

開發前系統配置及環境搭建 系統配置0 Github打不開、速度慢怎么辦1 WSL、Linux、Ubuntu、Docker都是什么鬼2 在Windows下安裝WSL和Ubuntu3 配置MySQL4 配置Redis并啟動服務5 Docker&#xff08;Windows和Ubuntu下&#xff09;6 Nginx 系統配置 你好&#xff01; 這是你第一次使…

uniapp 添加loading

在uniapp中添加loading可以使用uni的API uni.showLoading 方法。以下是一個簡單的示例代碼 // 顯示loading uni.showLoading({title: 加載中 });// 假設這里是異步操作&#xff0c;比如網絡請求 setTimeout(function () {// 隱藏loadinguni.hideLoading(); }, 2000);

C++(九)

前言&#xff1a; 本文主要講述運算符的優先順序。 一&#xff0c;運算符的優先級。 請看以下表達式&#xff1a; a32*5 運算結果為&#xff1a;13. 可以看到&#xff0c;在此代碼中&#xff0c;先運行了2*5的結果&#xff0c;在此基礎上在進行3操作&#xff0c;因此結果…

Android 拍照(有無存儲權限兩種方案,兼容Q及以上版本)

在某些行業&#xff0c;APP可能被禁止使用存儲權限&#xff0c;或公司在寫SDK功能&#xff0c;不方便獲取權限 所以需要有 無存儲權限拍照方案。這里兩種方案都列出里。 對于寫入權限&#xff0c;在高版本中&#xff0c;已經廢棄&#xff0c; 不可用文件寫入讀取權限&#xf…

【Altium Designer 】AD如何使用嘉立創元器件的3D封裝

1.下載3D封裝 以STM32F407VGT6為例&#xff0c;進入嘉立創商城網站&#xff0c;找到需要的元器件封裝 復制編號&#xff0c;打開嘉立創EDA&#xff0c;編譯器選擇專業版&#xff0c;新建工程&#xff0c;點擊PCB1 復制編號在搜索框中&#xff0c;點擊搜索&#xff0c;然后放置…

爬蟲運行后數據如何存儲?

爬蟲運行后獲取的數據可以存儲在多種不同的存儲系統中&#xff0c;具體選擇取決于數據的規模、查詢需求以及應用場景。以下是一些常見的數據存儲方法&#xff1a; 1. 文件系統 對于小型項目或臨時數據存儲&#xff0c;可以直接將數據保存到本地文件中。常見的文件格式包括&…

【機器學習】機器學習的基本分類-監督學習-梯度提升樹(Gradient Boosting Decision Tree, GBDT)

梯度提升樹是一種基于**梯度提升&#xff08;Gradient Boosting&#xff09;**框架的機器學習算法&#xff0c;通過構建多個決策樹并利用每棵樹擬合前一棵樹的殘差來逐步優化模型。 1. 核心思想 Boosting&#xff1a;通過逐步調整模型&#xff0c;使后續的模型重點學習前一階段…

【機器學習 | 基于Lasso回歸和隨機森林的上海鏈家二手房房價預測】

文章目錄 &#x1f3f3;??&#x1f308; 1. 導入模塊&#x1f3f3;??&#x1f308; 2. Pandas數據處理2.1 讀取數據2.2 查看數據信息2.3 去除重復數據2.4 去除缺失數據2.5 面積、價格、單價、樓層、建筑時間數據提取2.6 朝向數據處理 &#x1f3f3;??&#x1f308; 3. 特…

【HarmonyOS NEXT】flexShrink屬性

一、背景 希望達到的布局效果是文字與按鈕左右對齊&#xff0c;居中顯示&#xff0c;但實際效果中按鈕的顯示與效果不符&#xff0c;如下圖所示 二、問題 按鈕是用row組件包裹的text&#xff0c;左右padding給的是一樣的大小&#xff0c;但是明顯右邊padding會比左邊padding大…

CentOS 7 上安裝 MySQL 8.0.40 (二進制安裝)

要在 CentOS 7 上安裝 MySQL 8.0.40&#xff0c;按照以下步驟操作&#xff1a; 下載安裝包。 https://dev.mysql.com/downloads/mysql/ 下載之前查看系統c版本 解壓安裝包 首先&#xff0c;解壓下載的 .tar.xz 安裝包。 cd /path/to/your/downloads tar -xvf mysql-8.0…

PHP語法學習(第六天)

&#x1f4a1;依照慣例&#xff0c;回顧一下昨天講的內容 PHP語法學習(第五天)主要講了PHP中的常量和運算符的運用。 &#x1f525; 想要學習更多PHP語法相關內容點擊“PHP專欄” 今天給大家講課的角色是&#x1f34d;菠蘿吹雪&#xff0c;“我菠蘿吹雪吹的不是雪&#xff0c;而…

Python Web 開發:使用 FastAPI 進行依賴注入與異常處理

Python Web 開發&#xff1a;使用 FastAPI 進行依賴注入與異常處理 目錄 &#x1f6e0;? 依賴注入與 FastAPI 高級特性?? 自定義異常類的實現與應用&#x1f6a8; 使用 HTTPException 處理常見錯誤&#x1f30d; 全局異常處理器的設計與實現?? 異常處理與 API 響應的整合…