1) IOC/DI (IOC容器)
2) AOP面向切面編程
3) 宣告式事務
官網地址:spring是一家公司-商業化運作,apache是一家基金會組織,接收社會的捐贈,
https://spring.io/
io表示開發者平臺


1. Spring Framework
Spring 基礎框架,可以視為 Spring 基礎設施,基本上任何其他 Spring 專案都是以 Spring Framework 為基礎的,
Spring5,基于jdk1.8 ,
1.1 Spring Framework的優良特性
1 非侵入式:使用 Spring Framework 開發應用程式時,Spring 對應用程式本身的結構影響非常小,對領域模型(Java Bean)可以做到零污染;對功能性組件也只需要使用幾個簡單的注解進行標記,完全不會破壞原有結構,反而能將組件結構進一步簡化,這就使得基于 Spring Framework 開發應用程式時結構清晰、簡潔優雅,
2 控制反轉:IOC——Inversion of Control,翻轉資源獲取方向,把自己創建資源、向環境索取資源變成環境將資源準備好,我們享受資源注入,
3 面向切面編程:AOP——Aspect Oriented Programming,在不修改源代碼的基礎上增強代碼功能,
4 容器:Spring IOC 是一個容器,因為它包含并且管理組件物件的生命周期,組件享受到了容器化的管理,替程式員屏蔽了組件創建程序中的大量細節,極大的降低了使用門檻,大幅度提高了開發效率,
5 組件化:Spring 實作了使用簡單的組件配置組合成一個復雜的應用,在 Spring 中可以使用 XML 和 Java 注解組合這些物件,這使得我們可以基于一個個功能明確、邊界清晰的組件有條不紊的搭建超大型復雜應用系統,
6 宣告式:很多以前需要撰寫代碼才能實作的功能,現在只需要宣告需求即可由框架代為實作,
7 一站式:在 IOC 和 AOP 的基礎上可以整合各種企業應用的開源框架和優秀的第三方類別庫,而且 Spring 旗下的專案已經覆寫了廣泛領域,很多方面的功能性需求可以在 Spring Framework 的基礎上全部使用 Spring 來實作,
1.2 Spring Framework五大功能模塊

2. IOC容器的概念
2.1 生活中的普通容器

普通容器只能用來存盤,沒有更多功能,
2.2 程式中的普通容器
變數,陣列,集合
普通容器只能用來存盤,沒有更多功能,
2.3 生活中的復雜容器

政府管理我們的一生,生老病死都和政府有關,
4. IOC容器在Spring中的實作
Spring 的 IOC 容器就是 IOC 思想的一個落地的產品實作,IOC 容器中管理的組件也叫做 bean,在創建 bean 之前,首先需要創建 IOC 容器,Spring 提供了 IOC 容器的兩種實作方式:
①BeanFactory
這是 IOC 容器的基本實作,是 Spring 內部使用的介面,面向 Spring 本身,不提供給開發人員使用,
②ApplicationContex -- 我們自己使用
BeanFactory 的子介面,提供了更多高級特性,面向 Spring 的使用者,幾乎所有場合都使用 ApplicationContext 而不是底層的 BeanFactory,
所以,以后在 Spring 環境下看到一個類或介面的名稱中包含 ApplicationContext,那基本就可以斷定,這個類或介面與 IOC 容器有關,
③ApplicationContext的主要實作類


5. 基于XML管理bean
5.1 實驗01[重要]創建bean,并根據id獲取
5.1.1 目標
由 Spring 的 IOC 容器創建類的物件,并根據bean的id屬性值來獲取bean物件,
5.1.2 思路

自定義一個類IEmpService => EmpServiceImpl,這個類在Spring的組態檔中進行配置,然后用ClassPathXMLApplicationContext這個類去讀取組態檔,然后根據組態檔中配置的東西,將這個物件建立出來,
5.1.3 創建maven專案,匯入依賴
完整的pom檔案
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hy</groupId> <artifactId>mybatis001</artifactId> <version>0.0.1</version> <packaging>war</packaging>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> </properties>
<dependencies> <!-- Spring core--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.20.RELEASE</version> </dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency>
<!-- Spring-Test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.20.RELEASE</version> </dependency> <!--junit的核心jar包 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <!-- 指定jdk,防止update project --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <!-- 專案編碼 --> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project> |
匯入一個依賴,傳遞過來剩下的jar檔案



5.1.4 EmpService -> EmpServiceImpl
|
package com.hy.service.impl;
import com.hy.service.EmpService;
public class EmpServiceImpl implements EmpService { @Override public void reg() { System.out.println("用戶注冊"); } }
|
1) 先匯入spring的相關環境,其實就是匯入pom檔案jar包的坐標gav,
2) 創建spring的組態檔applicationContext.xml


|
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 實驗01 [重要] 創建bean --> <!-- bean標簽,通過配置bean標簽告訴IOC容器需要創建是哪個類的物件 id屬性:當前bean的唯一標識,設定了這個唯一標識后,在其他地方就可以用來參考這個bean物件 class屬性:指定這個bean要配置的類的全類名 --> <bean id="empService" class="com.hy.service.impl.EmpServiceImpl" /> </beans> |
這個bean沒有屬性,所以可以寫成單標簽
5.1.5 創建測驗類
|
public class TestService { // ApplicationContext就是我們的IOC容器 private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");
//實驗01 [重要] 創建bean //從IOC容器中獲取已配置的bean @Test public void testEmpService() { //通過bean的id屬性的值得,獲取這個bean物件 EmpService empService = (EmpService)iocContainer.getBean("empService"); empService.reg(); } } |
注意:Spring底層默認通過反射技術呼叫類的無參構造方法來創建類的物件,
5.1.6 無參構造方法的重要性,寫一個有參的構造方法,測驗
|
package com.hy.service.impl;
import com.hy.service.EmpService;
public class EmpServiceImpl implements EmpService {
public EmpServiceImpl(int age) { }
@Override public void reg() { System.out.println("用戶注冊"); } } |
報錯: 如果沒有無參構造方法,則會拋出下面的例外:


所以對一個JavaBean來說,無參構造器和屬性的getXxx()、setXxx()方法是必須存在的,特別是在框架中,

5.1.7 用IOC容器創建物件和自己建區別

被放入到容器當中,相當于加入了會員俱樂部
在Spring環境下能夠享受到的所有福利,都必須通過 IOC 容器附加到組件類上,所以隨著我們在 Spring 中學習的功能越來越多,IOC 容器創建的組件類的物件就會比自己 new 的物件 其功能強大的越來越多,
5.2 實驗02[重要]創建bean,并根據class型別來獲取
5.2.1 指定型別的 bean 唯一(只有一個)

5.2.2 測驗
|
package com.hy.test;
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hy.service.EmpService; import com.hy.service.impl.EmpServiceImpl;
public class TestService2 { // ApplicationContext就是我們的IOC容器 private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");
//實驗02 [重要] 創建bean,并且根據型別獲取bean物件 //從IOC容器中根據型別獲取已配置的bean物件 @Test public void testEmpService() { // EmpService empService = iocContainer.getBean(EmpService.class); //正確 EmpService empService = iocContainer.getBean(EmpServiceImpl.class); //正確 empService.reg(); //用戶注冊 } }
|

5.2.3 問題:如果配置了兩個同樣型別的bean物件,則報錯,根據型別獲取報錯,
|
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 實驗01 [重要] 創建bean --> <!-- bean標簽,通過配置bean標簽告訴IOC容器需要創建是哪個類的物件 id屬性:當前bean的唯一標識,設定了這個唯一標識后,在其他地方就可以用來參考這個bean物件 class屬性:指定這個bean要配置的類的全類名 --> <bean id="empService" class="com.hy.service.impl.EmpServiceImpl" />
<bean id="empService2" class="com.hy.service.impl.EmpServiceImpl" /> </beans>
|
|
package com.hy.test;
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hy.service.EmpService; import com.hy.service.impl.EmpServiceImpl;
public class TestService2 { // ApplicationContext就是我們的IOC容器 private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");
//實驗02 [重要] 創建bean,并且根據型別獲取bean物件 //如果容器中配置了同樣型別的兩個bean物件, //從IOC容器中根據型別獲取已配置的bean物件時,則報錯, @Test public void testEmpService() { // EmpService empService = iocContainer.getBean(EmpService.class); //失敗 EmpService empService = iocContainer.getBean(EmpServiceImpl.class); //失敗 } }
|


5.2.4 總結
如果一個類實作了某個介面,根據介面型別可以獲取 bean 嗎?
可以,前提是bean唯一,如下的配置就不行,
|
<bean id="empService" class="com.hy.service.impl.EmpServiceImpl" />
<bean id="empService2" class="com.hy.service.impl.EmpServiceImpl" /> |
如果一個介面有多個實作類,這些實作類都配置了 bean標簽,根據介面型別可以獲取 bean 嗎?
不行,因為bean不唯一
根據型別來獲取bean時,在滿足bean唯一性的前提下,其實只是看:『物件 instanceof 指定的型別』的回傳結果,只要回傳的是true就可以認定為和型別匹配,能夠獲取到,
5.3 實驗03[重要] 給bean的屬性賦值:通過setXxx方法注入
5.3.1 首先,給EmpServiceImpl添加一個屬性,提供setXxx和getXxx方法
|
package com.hy.service.impl;
import com.hy.service.EmpService;
public class EmpServiceImpl implements EmpService { private String empStatus;
public String getEmpStatus() { return empStatus; }
public void setEmpStatus(String empStatus) { this.empStatus = empStatus; } @Override public void reg() { System.out.println("用戶注冊"); } } |
5.3.2 在配置時給通過property標簽給屬性賦值

|
<!-- 實驗03 [重要] 創建bean,同時給bean的屬性賦值:通過setXxx方法注入 --> <bean id="empService3" class="com.hy.service.impl.EmpServiceImpl"> <!-- property標簽:通過類的setXxx()方法給該類的物件設定屬性值 name屬性:指定屬性名(其實這個名字是setXxx和getXx方法定義的,和屬性名無關) value屬性:指定屬性值 --> <property name="empStatus" value=https://www.cnblogs.com/lijili/p/"積極"/> </bean> |
5.3.3 測驗
|
package com.hy.test;
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hy.service.impl.EmpServiceImpl;
public class TestService3 { private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");
//實驗03 [重要] @Test public void testEmpService() { //通過bean的id屬性的值得,獲取這個bean物件 EmpServiceImpl empService3 = (EmpServiceImpl)iocContainer.getBean("empService3"); System.out.println(empService3.getEmpStatus()); //積極 } } |
5.4 實驗04[重要] 引入外部已宣告的bean
|
<!-- 實驗四 [重要]給bean的屬性賦值:參考外部已宣告的bean --> <bean id="empMapper" font-family: Consolas">hy.mapper.impl.EmpMapperImpl "> <property name="name" value="https://www.cnblogs.com/lijili/p/范冰冰"/> </bean>
<bean id="empService" > <!-- 使用外部已經配置好的empMapper這個bean來給當前組件的happyMachine屬性賦值 --> <!-- 參考另外一個bean不能再使用value屬性,而要使用ref屬性指定bean的id --> <!-- 如果錯把ref屬性寫成了value屬性,會拋出例外: Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.' for property ' empMapper ': no matching editors or conversion strategy found 意思是不能把String型別轉換成我們要的EmpMapper型別 說明我們使用value屬性時,Spring只把這個屬性看做一個普通的字串,不會認為這是一個bean的id,更不會根據它去找到bean來賦值 --> <property name="empMapper" ref=" empMapper "/> </bean> |
5.5 內部bean[重要]
|
<!-- 內部bean PersonServiceImpl里有一個Dept屬性 --> <bean id="personService" class="com.hy.service.impl.PersonServiceImpl"> <property name="dept" > <!-- 在property標簽內部再配置一個bean,這就是內部bean 內部bean可以直接用來給property指定的屬性賦值 - -> <!-- 內部bean,僅僅被外部bean使用,不能讓其他的bean使用,所以內部bean不需要id --> <bean class="com.hy.bean.Dept"/> </property> </bean> |
5.6 引入外部屬性檔案[重要]
<!-- 實驗六 [重要]給bean的屬性賦值:引入外部屬性檔案 -->
<!-- 使用context名稱空間下的property-placeholder標簽引入外部屬性檔案(本質上就是記錄外部屬性檔案的位置) -->
<!-- location屬性:指定外部屬性檔案的路徑,classpath:表示這個路徑從類路徑根目錄開始 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="druidDataSource" >
<!-- 通過 ${key}的形式參考外部屬性檔案中的資料 -->
<property name="url" value="https://www.cnblogs.com/lijili/p/${jdbc.url}"/>
<property name="driverClassName" value="https://www.cnblogs.com/lijili/p/${jdbc.driver}"/>
<property name="username" value="https://www.cnblogs.com/lijili/p/${jdbc.user}"/>
<property name="password" value="https://www.cnblogs.com/lijili/p/${jdbc.password}"/>
</bean>
5.7 給bean的屬性賦值:級聯屬性賦值(不重要,不怎么使用)
使用內部bean,給級聯屬性賦值,
<!-- 實驗七 給bean的屬性賦值:級聯屬性賦值 -->
<bean id="happyComponent7" >
<!-- 使用內部bean的形式將happyMachine屬性初始化 -->
<property name="happyMachine">
<!-- 相當于創建了HappyMachine的空物件賦值給happyMachine屬性 -->
<bean font-family: Consolas">hy.ioc.component.HappyMachine"/>
</property>
<!--
如果happyMachine屬性沒有被初始化,那么直接訪問級聯屬性會拋出例外:
Caused by: org.springframework.beans.NullValueInNestedPathException: Invalid property 'happyMachine' of bean class [com.atguigu.ioc.component.HappyComponent]: Value of nested property 'happyMachine' is null
-->
<property name="happyMachine.machineName" value="https://www.cnblogs.com/lijili/p/aaa"/>
</bean>
5.8 通過構造方法,給bean屬性賦值[不重要]
<!-- 實驗八 給bean的屬性賦值:構造器注入 -->
<bean id="happyTeam" >
<!-- 呼叫類的構造器創建物件并同時傳入引數值 -->
<!-- constructor-arg標簽:給構造器對應位置傳入引數 -->
<!-- index屬性:指定當前引數在引數串列中的索引位置 -->
<!-- name屬性:指定當前引數的引數名 -->
<!-- value屬性:指定引數值 -->
<constructor-arg index="0" name="teamName" value="https://www.cnblogs.com/lijili/p/gaysTeam"/>
<constructor-arg index="1" name="memberCount" value="https://www.cnblogs.com/lijili/p/10" />
<constructor-arg index="2" name="memberSalary" value="https://www.cnblogs.com/lijili/p/555.55"/>
</bean>
5.9 給bean的集合屬性賦值[不重要]
private List<String> nameList; //標簽不需要完全一致,一樣可以賦值成功
private Map<String,String> nameMap;
<!-- 實驗十一 給bean的屬性賦值:集合屬性 -->
<bean id="classes" >
<property name="nameList">
<!-- list標簽:準備一組集合型別的資料,給集合屬性賦值 -->
<!--<list>
<value>member01</value>
<value>member02</value>
<value>member03</value>
</list>-->
<!-- 使用set標簽也能實作相同效果,只是附帶了去重功能 -->
<!--<set>
<value>member01</value>
<value>member02</value>
<value>member02</value>
</set>-->
<!-- array也同樣兼容 -->
<array>
<value>member01</value>
<value>member02</value>
<value>member02</value>
</array>
</property>
<property name="nameMap">
<!-- 給Map型別的屬性賦值 -->
<!--<map>
<entry key="財務部" value="https://www.cnblogs.com/lijili/p/張三"/>
<entry key="行政部" value="https://www.cnblogs.com/lijili/p/李四"/>
<entry key="銷售部" value="https://www.cnblogs.com/lijili/p/王五"/>
</map>-->
<!-- 也可以使用props標簽 -->
<props>
<prop key="財務部">張三2</prop>
<prop key="行政部">李四2</prop>
<prop key="銷售部">王五2</prop>
</props>
</property>
</bean>
5.10 自動裝配 與 手動裝配[了解]
所謂自動裝配就是一個組件需要其他組件時,由 IOC 容器負責找到那個需要的組件,并裝配進去,
|
<!-- 實驗十二 自動裝配 --> <bean id="happyService3" /> <bean id="happyService2" />
<!-- 使用bean標簽的autowire屬性設定自動裝配效果 --> <!-- byType表示根據型別進行裝配,此時如果型別匹配的bean不止一個,那么會拋NoUniqueBeanDefinitionException --> <!-- byName表示根據bean的id進行匹配,而bean的id是根據需要裝配組件的屬性的屬性名來確定的 --> <bean id="happyController"
autowire="byName"> <!-- 手動裝配:在property標簽中使用ref屬性明確指定要裝配的bean --> <!--<property name="happyService" ref="happyService"/>--> </bean> |
@Test
public void testExperiment12() {
HappyController happyController = iocContainer.getBean(HappyController.class);
HappyService happyService = happyController.getHappyService();
System.out.println("happyService = " + happyService);
}
5.11 bean的宣告周期以及初始化
在無參構造方法打一個斷點,
bean標簽scope配置singleton時,bean的創建是在IOC容器初始化的時候,創建的,而不是在getBean的時候創建的,
bean標簽scope配置prototype時,bean的創建實在呼叫getBean方法的時候創建的,

IOC容器關閉之前銷毀bean物件,


這個介面總沒有close方法在子介面中,


6,基于注解管理bean
和 XML 組態檔一樣,注解本身并不能執行,注解本身僅僅只是做一個標記,具體的功能是框架檢測到注解標記的位置,然后針對這個位置按照注解標記的功能來執行具體操作,
本質上:所有一切的操作都是Java代碼來完成的,XML和注解只是告訴框架中的Java代碼如何執行,


1) 首先根據所需要的組件型別到IOC容器中查找
能夠找到唯一的bean:直接執行裝配
如果完全找不到匹配這個型別的bean:裝配失敗
和所需型別匹配的bean不止一個
沒有@Qualifier注解:根據@Autowired標記位置成員變數的變數名作為bean的id進行匹配
能夠找到:執行裝配
找不到:裝配失敗
使用@Qualifier注解:根據@Qualifier注解中指定的名稱作為bean的id進行匹配
能夠找到:執行裝配
找不到:裝配失敗
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/505576.html
標籤:Java
