# Spring5筆記
```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
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
```
### Spring開發步驟
1. 導入座標(maven dependency)
```xml=
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>test</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-01-ioc</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
</project>
```
2. 創建Bean

3. 創建applecationContext.xml(配置文件)
4. 在配置文件中進行配置
```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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.ithema.dao.impl.UserDaoImpl"></bean>
</beans>
```
5. 創建applicationContext物件,getBean
# 1. IOC容器
* 控制反轉:反轉bean的控制權
:star:舊式做法:Service -> DAO, 必須在Service new一個DAO的實例出來, 但是使用Spring的話, 當Service層呼叫DAO時, 並不需要創建一個新的實例, 因為我們把創建的動作交給了Spring負責(IOC), 並且達到了解耦的效果
(DI的概念)
* 使用IOC的目的: <font color="red">降低耦合度</font>
* IOC底層原理: xml解析, 工廠模式, 映射

## 1.2 IOC操作Bean管理
* Bean管理指的是兩個操作
1. Spring 創建對象
2. Spring 屬性注入
:star:Bean管理操作有2種方式:star:
1. 基於xml配置文件實現
2. 基於註解方式實現
##### 建構子創建對象的方式
* 共<font color="red">三種方式</font>
1. 第一種 index賦值
```xml=
<bean id="user" class="com.liz.pojo.User">
<constructor-arg index="0" value="狂神說java">
</bean>
```
2. 第二種 通過型別創建(<font color="red">不建議使用!!</font>)
:point_right:<font color="#33BDFF">如果兩個參數的型別都一樣, 將無法指定!</font>
```xml=
<bean id="user" class="com.liz.pojo.User">
<constructor-arg type="java.lang.String" value="狂神說">
<constructor-arg type="int" value="0">
</bean>
```
3. 第三種 直接通過參數名來設置
```xml=
<bean id="user" class="com.liz.pojo.User">
<constructor-arg name="name" value="老師">
</bean>
```
總結: 在配置文件(bean.xml)加載的時候, 容器就已經創建出初始化的instance了!
---
# 2. Spring的配置
### 1. 別名
```xml=
<!-- 如果增加了別名, 我們也可以使用這個別名來取到這個對象 -->
<alias id="bean id的值" alias="別名" />
//name=""也可以作為bean的標示, 但是在Struct1較常用
```
### 2. Bean的配置
```xml=
<!--
id : bean的唯一識別名, 也相當於我們知道的instance name
class : bean對象的全域名稱 package + class
name : 也是別名, 而且name可以同時取多個別名
scope
autowire
-->
<bean id="userT" class="com.liz.pojo.UseT" name="user2 u2,u3;user4">
<constructor-arg name="name" value="學習">
</bean>
```
user2 u2,u3;user4都會呼叫同一個bean對象
### 3. import
:star:一般用於<font color="red">團隊開發</font>
import可以將多個配置檔導入合併為一個
假設現在專案有多人開發, 大家負責不同的類別開發, 則不同的類別需要在不同的bean中, 我們可以利用import將所有人的bean.xml合併成一個總和的檔案!
```xml=
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
```
使用的時候, 用まとめた配置檔就可以了!
:bulb:如果宣告的內容重覆, 則內容依樣會被合併
---
# 3. IOC 依賴注入(DI)
### 1. 建構子注入
上面提過了
### 2. Set方式注入:bulb:<font color="red">!重點!</font>:bulb:
* 依賴注入的本質 = set注入
* 依賴 : bean對象的創建依賴於容器
* 注入 : bean對象中的所有屬性, 由容器來注入!
2.1 普通值注入(經由value="")
`<property name="name" value="大衛"/>`
2.2 Bean注入(使用ref="")
`<property name="address" ref="address"/>`
2.3 陣列注入
```xml=
<property name="books">
<array>
<value>紅樓夢</value>
<value>西遊記</value>
<value>水滸傳</value>
<value>三國演義</value>
</array>
</property>
```
2.4 List
```xml=
<property name="hobbies">
<list>
<value>聽歌</value>
<value>看電影</value>
</list>
</property>
```
2.5 Set
```xml=
<property name="games">
<set>
<value>LOL</value>
<value>WOW</value>
</set>
</property>
```
2.6 Map
```xml=
<property name="cards">
<map>
<entry key="身分證" value="B00221"/>
<entry key="提款卡" value="005-888-166"/>
<entry key="學生證" value="100123456"/>
</map>
</property>
```
2.7 <font color="red">空字串注入</font>
`<property name="wife" value=""/>`
2.8 <font color="red">null值注入</font>
```xml=
<property name="wife">
<null/>
</property>
```
2.9 Properties注入(鍵值對, 但value要寫角括號外面)
```xml=
<property name="info">
<props>
<prop key="學號">20210103</prop>
<prop key="性別">男</prop>
<prop key="name">大衛海鮮</prop>
<prop key="userName">DS</prop>
<prop key="password">123456</prop>
</props>
</property>
```
2.10 注入特殊符號
```xml=
// 把<>進行轉譯(<和>)
// 把特殊符號內容寫到CDATA
<bean id="orderDao" class="com.ithema.dao.OrderDao">
<constructor-arg name="oname" value="電腦"></constructor-arg>
<constructor-arg name="address" value="TW"></constructor-arg>
<property name="label" value="<城市>"></property>
<property name="place">
<value><![CDATA[<<南京>>]]></value>
</property>
</bean>
```
2.11 外部Bean注入
```xml=
//xml
<bean id="userDaoImpl" class="com.ithema.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.ithema.service.UserService">
<property name="userDaoA" ref="userDaoImpl"/>
</bean>
```
```java=
//java code
public class UserService {
private UserDao userDaoA;
public void updateService(){
userDaoA.save();
System.out.println("user service....");
}
public void setUserDaoA(UserDao userDaoA) {
this.userDaoA = userDaoA;
}
}
```
2.12 內部Bean
```xml=
<!-- 內部bean-->
<bean id="emp" class="com.ithema.bean.Emp">
<property name="ename" value="Lucy"/>
<property name="gender" value="女"/>
<property name="dept">
<bean id="dept" class="com.ithema.bean.Dept">
<property name="dname" value="會計部"/>
</bean>
</property>
</bean>
```
2.13 注入屬性-集聯賦值
第一種寫法
```xml=
<!-- 集聯賦值-->
<bean id="emp" class="com.ithema.bean.Emp">
<property name="ename" value="Lucy"/>
<property name="gender" value="女"/>
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.ithema.bean.Dept">
<property name="dname" value="財務部"/>
</bean>
```
第二種寫法
此種寫法是先在emp instance裡生成dept, 再從dept裡面拿取dname,
所以java file裡面必須有dept的getter, 不然會報錯
```xml=
<!-- 集聯賦值-->
<bean id="emp" class="com.ithema.bean.Emp">
<property name="ename" value="Lucy"/>
<property name="gender" value="女"/>
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="技術部"></property>
</bean>
<bean id="dept" class="com.ithema.bean.Dept">
<property name="dname" value="財務部"/>
</bean>
```
2.14 將集合注入部分提取出來供其他bean使用
1. 在spring配置文件中引入名稱空間util
```xml=
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd">
```
2. 使用util標籤完成list集合注入提取
```xml=
<util:list id="bookList">
<value>易筋經</value>
<value>九陰真經</value>
<value>九陽神功</value>
</util:list>
<bean id="books" class="com.atguigu.spring5.collectiontype.Book">
<property name="list" ref="bookList"></property>
</bean>
```
### 3. 拓展方式注入(第三方)
我們可以使用p命名空間和c命名空間來進行注入
:bulb:注意點 : c命名空間和p命名空間不可直接使用, 需要導入xml約束
*p命名空間的底層還是使用setter來實現
3.1 p 注入方式(p命名空間)
* 註冊檔開頭需要導入Xml約束
`xmlns:p="http://www.springframework.org/schema/p"`
* 類似property宣告, 對應到set方式注入
```xml=
<bean name="user" class="com.spring.pojo.User" p:userName="Peter" p:age="45"/>
```
3.2 c 注入方式(c命名空間)
* 註冊檔開頭需要導入Xml約束
`xmlns:c="http://www.springframework.org/schema/c"`
* 通過建構子注入, 對應到建構子注入
```xml=
<bean name="user2" class="com.spring.pojo.User" c:userName="Tom" c:age="18"/>
```
---
# 4. IOC容器Bean管理
## Factory Bean
#### Spring有兩種類型的Bean
1. 普通Bean: 在配置文件中定義的Bean類型就是返回類型
2. Factory Bean: 配置文件中定義類型的類型可以跟返回類型不一樣
Howto:
1. 創建一個Class, 然後實現FactoryBean介面
2. Override介面的方法, 在實現的方法中定義返回的Bean類型
```java=
public class MyBean implements FactoryBean<Course> {
//定義return的bean
//我們設定的Bean是"MyBean", 但是返回的對象是"Course"這就是工廠Bean
//配置文件中定義類型的類型可以跟返回類型不一樣
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("工廠bean");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
```
```xml=
<bean id="myBean" class="com.atguigu.spring5.factoryBean.MyBean"></bean>
```
```java=
@Test
public void testCollection3(){
//以下寫法會報錯, 因為返回的類型不適MyBean而是Course
// ApplicationContext app = new ClassPathXmlApplicationContext("bean3.xml");
// MyBean myBean = app.getBean("myBean", MyBean.class);
//
// System.out.println(myBean);
//========================================
ApplicationContext app = new ClassPathXmlApplicationContext("bean3.xml");
Course myBean = app.getBean("myBean", Course.class);
System.out.println(myBean); //輸出Course{cname='工廠bean'}
}
```
---
## Bean Scope
#### 如何配置
```xml=
<bean id="books" class="com.atguigu.spring5.collectiontype.Book">
<property name="list" ref="bookList" scope="singleton"></property>
</bean>
```
1. Singleton (Default)單實例
無論用哪個DAO去拿, 都用同一個instance去回應

記憶體位址相同

2. Prototype(原型模式)多實例
如果scope設定為prototype, 則上圖程式碼執行結果為<font color="red">false</font>
每次從容器中get的時候, 都會產生一個新的instance
```xml=
<bean id="books" class="com.atguigu.spring5.collectiontype.Book" scope="prototype">
<property name="list" ref="bookList"></property>
</bean>
```

3. request/session/application/websocket
這些只能在web開發裡使用到!
##### :bulb:scope="songleton"的時候, 加載配置文件時spring就會創建instance
##### :bulb:scope="prototypr"則不是在加載配置文件是創建instance, 而是在調用getBean方法時創建多實例對象
---
## Bean的生命週期
1. 通過無參數建構子創建bean
2. 設置屬性(setter)
3. 調用bean初始化方法(需要在配置文件裡面配置)
4. bean可以使用了
5. 當容器關閉的時候, 調用的bean銷毀方法(需要在配置文件裡面配置)
```xml=
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手機"></property>
</bean>
```
```java=
public class Orders {
private String oname;
public Orders() {
System.out.println("第一步, 調用無參數建構子");
}
//執行的初始化方法
public void initMethod() {
System.out.println("第三步, 執行init 方法");
}
//執行的銷毀方法
public void destroyMethod(){
System.out.println("第五步, 銷毀時的方法");
}
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步, 調用setter設定屬性值");
}
}
```
```java=
@Test
public void testBeanInit(){
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = app.getBean("orders", Orders.class);
System.out.println("第四步, 獲取創建bean實例對象");
System.out.println(orders);
//手動讓bean實例銷毀
app.close();
}
```

### Bean的後置處理器(before/after)
Runtime的順序如下 :arrow_down:
###### `postProcessBeforeInitialization`
3. 把實例bean傳給後置處理器的方法(需要在配置文件裡面配置)
3.1 實現interface `BeanPostProcessor`
###### `postProcessAfterInitialization`
:warning: <font color='red'>後置處理器會被適用於當前配置文件中的所有bean實例</font>
```xml=
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手機"></property>
</bean>
<!-- 配置後置處理器-->
<!-- 此後置處理器會被適用於當前配置文件中的所有bean實例-->
<bean id="myBean" class="com.atguigu.spring5.bean.MyBeanPost">
</bean>
```
---
## Bean的自動裝配
* 自動裝配是Spring滿足bean依賴的一種方式
* Spring會在上下文中自動尋找, 並自動為bean裝配屬性
* IOC操作Bean管理(xml/annotaion)參照之前筆記
在Spring中有三種裝配方式
1. 在xml中配置 <font color='red'>(手動裝配)</font>
2. 在java中顯示配置
3. 隱式的自動裝配bean:bulb:<font color="red">!重點!</font>:bulb:
### 基於XML方式進行自動裝配(少用)
一般進行自動裝配時, 還是使用annotation來進行
### bean標籤屬性autowire來配置自動裝配
autowire屬性常用的兩個值:
1. byName: 根據屬性名稱(attribute name)注入
2. byType: 根據屬性的類型(Class)注入
### autowire="byName"
:bulb: 和class裡面的attribute一樣名稱
會自動在容器上下文中尋找, 和自己set方法後面的值對應的<font color="red">bean id!</font>
```xml=
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="people" class="com.kuang.pojo.People" autowire="byName">
<property name="name" value="大衛"/>
</bean>
```
### autowire="byType"
會自動在容器上下文中尋找, 和自己對象屬性<font color="red">class相同</font>的bean!
```xml=
<bean class="com.kuang.pojo.Dog"/>
<bean class="com.kuang.pojo.Cat"/>
<bean id="people" class="com.kuang.pojo.People" autowire="byType">
<property name="name" value="大衛"/>
</bean>
```
小結:
* byName的時候, 需要保證所有bean的id是唯一的! 並且這個bean需要和自動注入的屬性的set方法的值一致!
* byType的時候, 需要保證所有bean的class唯一! 並且這個bean需要和自動注入的屬性的類別一致!
```xml=
當使用"byType"時, 以下配置會出錯, 因為dept和dept1都是Dept class
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType"></bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
<bean id="dept1" class="com.atguigu.spring5.autowire.Dept"></bean>
```
---
# 5. IOC操作Bean管理(外部屬性文件)
將經常使用的constant(ex. DB的帳密, enpoint)抽取出來寫在properties檔之後引用
例:
1. 直接配置database資訊
1.1 配置德魯伊連接池
1.2 引入德魯伊連接池的依賴 or jar包
```xml=
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
```
```xml=
<!--直接配置連線池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/userDB"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
```
2. 引入外部屬性文件配置database連接池
2.1 創建外部屬性文件(properties檔), 創建資料庫資訊

2.2 把外部properties文件引入到Spring配置文件中
* 引入context名稱空間
* 在spring配置文件中使用標籤引入外部屬性文件
```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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd
https://www.springframework.org/schema/context/spring-context.xsd">
</beans>
```
```xml=
<!-- 引入外部屬性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置連線池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.username}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
```
---
# 6. Bean管理-使用註解開發
* 何謂註解:
1. 註解是程式碼的特殊標記, 格式:@註解名稱(屬性名稱=屬性值, 屬性名稱=屬性值...)
2. 註解可以使用於class上面, attribute上面, method上面
3. 使用註解目的: 簡化xml配置
* Spring針對Bean管理中創建對象提供註解
1. `@Component`
* 相當於`<bean id="user" class="com.kuang.pojo.User" />`
3. `@Service`
4. `@Controller`
5. `@Repository` (DAO)
上面4個註解功能是一樣的, 都可以用來創建bean實例
## 6.1 使用註解實現自動裝配
要使用註解(Spring4之後), 必須保證 :
1. 導入aop的依賴(jar檔)

2. 開啟組件掃描
* 在xml配置文件中導入context約束

```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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 開啟組件掃描, 可以用","隔開多個package或是掃描上層package目錄-->
<!-- <context:component-scan base-package="com.atguigu.spring5.dao,com.atguigu.spring5.service"></context:component-scan>-->
<!-- 掃描"com.atguigu"底下的所有類別 -->
<context:component-scan base-package="com.atguigu"></context:component-scan>
<!-- 例1
use-default-filters="false" 表示現在不使用預設 filter, 自己配置filter
context:include-filter 設置掃描那些內容
只掃描package底下帶有"@Controller"註解的class
-->
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 例2
下面配置掃描"com.atguigu"所有內容
context:exclude-filter, 設置哪些內容不進行掃描 -->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
```
3. 配置support註解 :bulb:<font color="red">!重點!</font>:bulb:
` <context:annotation-config/>` 或
` <context:component-scan base-package="com.atguigu"></context:component-scan>`
4. 創建class, 在class上面添加創建對象註解
```java=
@Component(value = "userService")
public class UserService {
public void add() {
System.out.println("Service add...");
}
}
```
## 6.2 基於註解實現屬性注入
### 1. `@Autowired`註解
第一步 把service跟dao對象創建, 在service跟dao添加創建對象註釋
第二步 在service注入dao對象, 在service類添加dao類型屬性, 在屬性上面使用註釋
* 根據屬性類型進行自動裝配(類似byType)
使用autowire annotation的話, 可以不寫set方法!(但getter還是需要!)
```java=
@Autowired
private Dog dog;
```
注意事項:
1. 直接在屬性上使用, 也可在setter上使用
2. 使用Autowired, 我們可以不用編寫set方法, 前提是這個自動配裝的屬性在IOC容器(Spring)容器中存在, 且符合名字("byName")!!
如果定義了Autowired的required屬性為false, 則表示這個對象可以為null, 否則不允許為空值`@Autowired(required = false)`
### 2. `@Qualifier`註解
* 根據屬性的名稱進行注入
* 必須和`@Autowired`一起使用
如果自動裝配的環境比較複雜, 我們可以使用`@Qualifier(value="xxx")`去配合`@Autowired`, 指定一個唯一的bean對象注入
```java=
@Service
public class UserService {
//定義Dao類型屬性
//不需要添加Set方法
//添加注入屬性註解
@Autowired
@Qualifier(value = "userDaoImpl1")
private UserDao userDao;
public void add() {
System.out.println("Service add...");
userDao.addUser();
}
}
```
### 3. `@Resource`註解
* 可以根據類型注入, 也可以根據名稱注入
先找上下文中id/name相同的對象, 如果都不相同, 在尋找相同class的對象
如果自動裝配的環境比較複雜, 我們可以使用`@Resource(name="xxx")`, 來指定一個唯一的bean對象注入
```java=
@Resource(name = "userDaoImpl2") //預設是根據名稱進行注入
private UserDao userDao;
public void add() {
System.out.println("Service add...");
userDao.addUser();
}
```
:bulb: <font color='red'>建議使用Autowired和Qualifier</font>

### 4. `@Value`註解
* 注入普通類型屬性
```java=
@Value(value = "王阿花")
private String name;
```
小結:
1. 都是用來自動裝配的, 都可以放在屬性上
2. @Autoired通過byType的方式實現, 而且必須要求這個對象存在!
3. @Resource預設通過byName的方式實現, 如果找不到對應的name, 則通過byType方式實現! 如果兩個都找不到的話就會報錯
4. 區別: @Autoired通過byType的方式實現。@Resource預設通過byName實現。
重點:
1. bean
2. 屬性如何注入
```java=
@Component
public class User {
public String name;
@Value("老師")
//相當於<property name="name" value="老師"/>
public void setName(String name) {
this.name = name;
}
}
```
3. 衍生註解
***@Component***有幾個衍生註解, 我們在web開發中, 會按照mvc分層架構分成三層
* dao【@Repository】
* service 【@Service】
* controller 【@Controller】
:star: <font color="red">這四個註解功能都是一樣的, 都是代表將某個class註冊到Spring容器中裝配Bean</font>:star:
4. 自動裝配
上面寫過了
5. scope
```java=
@Component
@Scope("singleton")
```
6. 小結
xml與註解:
* xml更加萬能, 適用於任何場合, 維護簡單方便
* 註解不是自己的class使用不了, 維護相對複雜
xml與註解最佳實踐方法:
* xml用來管理bean
* 註解只負責屬性的注入
* 我們在使用的過程中, 只需要注意一個問題: 要讓註解生效, 就必須開啟支援註解!!!
```xml=
<!--指定要掃描的package, 這個package底下的註解就會生效-->
<context:component-scan base-package="com.kuang"/>
<context:annotation-config/>
```
---
## 6.3 完全註解開發
1. 創建配置類, 取代xml配置文件
### 使用java的方式配置Spring
完全不使用Spring的xml配置, 全部使用java來達到Spring配置
JavaConfig是Spring的一個子項目, 在Spring4之後, 他成為了一個核心功能
### `@Configuration`
代表一個配置Class, 就和之前的beans.xml是一樣的
```java=
@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
public class SpringConfig {
}
```
```java=
@Test
public void testService2(){
//加載配置類
ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService us = app.getBean("userService", UserService.class);
us.add();
}
```
---
# 7. AOP 面向導向程式設計
#### 不影響原本的業務類別下, 實現動態增強
* OOP利用AOP可以對業務邏輯的各部分進行隔離(耦合性低!)
* 不通過修改源碼方式, 在主幹功能裡面添加新功能
<font color="red">使用AOP時需要多導入一個aspect(織入)檔</font>
```xml=
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
```
bean.xml要導入aop的約束
```xml=
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
```


方法一: 使用spring提供的interface(主要是SpringAPI介面實現)
```java=
MethodBeforeAdvice
AfterReturningAdvice
```
```xml=
<!-- 使用spring原生的interface-->
<!-- 配置aop: 需要導入AOP的約束-->
<aop:config>
<!--切入點(在哪個地方執行Spring): expression 表達式 execution(要執行的位置! * * * * *)-->
<aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<!-- 執行環繞增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut" />
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut" />
</aop:config>
```
方法二: 自定義類別來實現AOP(主要是切面定義)
方式三: 使用註解實現!
---
# 8. 代理模式
SpringAOP的底層
代理模式的分類:
* 靜態代理
* 動態代理
### 8.1 靜態代理
腳色分析
* 抽象角色: 一般會使用interface或抽象類別來解決(ex. 租房)
* 真實角色: 被代理的角色(ex. 房東)
* 代理角色:代理真實角色, 代理真實角色之後, 我們一般會做一些附屬操作
* 客戶: 訪問代理對象的人
代理模式的優點:
* 可以使真實角色的操作更加純粹! 不用去關注公共業務(提高重用性)
* 公共業務交給代理角色, 實現業務分工
* 公共業務發生擴展的時候, 方便集中管理
缺點:
* 一個真實角色就會產生一個代理角色, 程式碼量翻倍, 開發效率變低
:point_right: <font color="red">靜態代理撰寫步驟</font>
1. 介面
```java=
package com.kuang.demo01;
//租房--抽象角色
public interface Rent {
public void rent();
}
```
2. 真實角色
```java=
package com.kuang.demo01;
//房東--真實角色
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房東要出租房子");
}
}
```
3. 代理角色
```java=
package com.kuang.demo01;
//代理角色--房屋仲介
public class Proxy implements Rent{
//房東
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
//代理的抽象角色
@Override
public void rent() {
seeHouse();
sign();
takeFee();
host.rent();
}
//只有代理能做的事(ex. 看房)
public void seeHouse(){
System.out.println("仲介帶你看房");
}
public void sign(){
System.out.println("簽租賃合約");
}
public void takeFee(){
System.out.println("收仲介費");
}
}
```
4. 訪問代理角色的客戶端
```java=
package com.kuang.demo01;
public class Client {
public static void main(String []args){
//原來的模式, 沒有使用代理
//房東要租房子
Host host = new Host();
//host.rent();
//可以放入不同的房東代理!
//代理, 仲介幫房東租房, 但是代理一般會有一些附屬操作
Proxy proxy = new Proxy(host);
//你不用面對房你不用面對房東, 直接找仲介租房即可
proxy.rent();
}
}
```
### 8.2 動態代理
* 動態代理跟靜態代理的角色一樣
* 動態代理的代理類別是動態生成的, 不是我們直接寫好的
* 動態代理分為兩大類
* 基於介面的動態代理, 基於class的動態代理
* 基於介面---JDK動態代理

* 基於class---CGLIB

* java字節碼實現: javasist
需要了解兩個類別
* Proxy
* 生成動態代理實例
* InvocationHandler
* 調用處理程序(InvocationHandler), 並返回結果
```java=
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
UserDao userDao = new UserDaoImpl();
//創建介面實現類的代理對象
UserDao up = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserProxy(userDao));
// System.out.println("result: "+ up.add(10,5));
System.out.println("result: "+ up.update("測試"));
}
}
//創建代理對象
class UserProxy implements InvocationHandler {
//把欲代理的class傳遞過來
//使用建構子傳遞
private Object obj;
public UserProxy(Object userDao) {
this.obj = userDao;
}
// Override後方法理實現增強的邏輯
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前..." + method.getName() + ":傳遞的參數...:" + Arrays.toString(args));
//執行被增強的方法
Object res = method.invoke(obj, args);
//方法之後
System.out.println("方法之後執行..." + obj);
return res;
}
}
```

:point_right: <font color="red">動態代理的優點</font>
* 所有靜態代理有的優點動態代理都有
* 一個動態代理類, 代理的是一個介面, 一般就是對應相同類型的業務
* 一個動態代理類, 可以代理多個實現了同一個介面的類!
---
# 9. AOP操作
1. Spring框架一般都是基於AspectJ實現AOP操作
1.1 什麼是AspectJ
* AspectJ不是Spring組成的一部分, 是獨立的AOP框架, 一般把AspectJ跟Spring框架一起使用, 進行AOP操作
2. 基於AspectJ實現AOP操作
2.1 基於xml配置文件
2.2 基於註解方式(常用)
3. 引入AOP依賴

4. 切入點表達式
4.1 切入點表達式作用: 知道對哪個類裡面的哪個方法進行增強
4.2 語法結構:
execution( \[權限修飾符\]\[返回類型\]\[類全路徑\]\[方法名稱\](\[參數列表\]) )
* 舉例1: 對com.atguigu.dao.BookDao類裡面的add進行增強
execution(* com.atguigu.dao.BookDao.add(..))
* 舉例2: 對com.atguigu.dao.BookDao類裡面的所有方法進行增強
execution(* com.atguigu.dao.BookDao.*(..))
* 舉例3: 對com.atguigu.dao包裡面的所有類的所有方法進行增強
execution(* com.atguigu.dao.*.*(..))
### AspectJ註解
1. 創建class, 在class裡面定義方法
2. 創建增強class(編寫增強邏輯)
2.1 在增強類裡面創建方法, 讓不同方法代表不同通知類型
3. 進行通知的配置
3.1 在spring配置文件中, 開啟註釋掃描
```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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--開啟註釋掃描-->
<context:component-scan base-package="com.atguigu.spring5.aopanno"/>
</beans>
```
3.2 使用註釋創建User跟UserProxy
```java=
//被增強的類
@Component
public class User {
public void add(){
System.out.println("add....");
}
}
```
```java=
//增強的類
@Component
@Aspect //生成代理對象
public class UserProxy {
//前置通知
public void before(){
System.out.println("before...");
}
}
```
3.3 在增強類上面添加註釋`@Aspect`
3.4 在配置文件中開啟生成代理對象
```xml=
<!--開啟AspectJ, 生成代理對象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
```
4. 配置不同類型的通知
4.1 在增強類的裡面, 在作為通知的方法上面添加通知類型註釋, 並且使用切入點表達式配置
```java=
package com.atguigu.spring5.aopanno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//增強的類
@Component
@Aspect //生成代理對象
public class UserProxy {
//前置通知
@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void before() {
System.out.println("before...");
}
//最終通知
//不管有沒有異常, 都會執行
@After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after...");
}
//後置通知(返回通知)
@AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning...");
}
//異常通知
@AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println("AfterThrowing...");
}
//環繞通知
@Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPointo) throws Throwable {
System.out.println("環繞之前...");
//被增強的方法執行
proceedingJoinPointo.proceed();
System.out.println("環繞之後...");
}
}
```
* 正常執行:

* 有例外:

5. 抽取相同的切入點
```java=
//抽取相同的切入點
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointDemo(){
}
//前置通知
@Before(value = "pointDemo()")
public void before() {
System.out.println("before...");
}
```
6. 有多個增強類, 對同一個方法進行增強, 我們可以設置增強類的優先度
* 在增強類上面添加註釋`@Order(數字的值)`, 數字越小優先度越高
```java=
@Component
@Aspect
@Order(1)
public class PersonProxy {
}
```
7. 完全使用註解開發
* 創建配置類, 不需要創建xml配置文件
```java=
@Configuration
@ComponentScan(basePackages = {"com.atguigu.spring5"})
@EnableAspectJAutoProxy(proxyTargetClass = true) //預設是false
public class ConfigAop {
}
```
### AspectJ XML配置文件
1. 創建2個class, 增強類與被增強類, 創建方法
2. 在spring配置文件中創造2個類的對象
```xml=
<!--創建對象-->
<bean id="book" class="com.atguigu.spring5.aopanno.aopxml.Book"/>
<bean id="bookProxy" class="com.atguigu.spring5.aopanno.aopxml.BookProxy"/>
```
3. 在spring文件中配置切入點
```xml=
<!--配置AOP增強-->
<aop:config>
<!--切入點-->
<aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopanno.aopxml.Book.buy(..))"/>
<!--配置切面-->
<aop:aspect ref="bookProxy">
<!--配置要增強的具體方法-->
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
```
---
# 10. Jdbc Template
### 10.1 什麼是Jdbc Template?
* Spring框架對JDBC進行封裝, 方便實現對資料庫操作
1. 引入依賴

2. 在配置文件中配置連接池
(可以用properties配置文件設定)
```xml=
<!--直接配置連線池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///user_db"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
```
3. 配置JdbcTemplate, 注入DataSource
```xml=
<!--JDBC Template對象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入DataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
```
4. 創建service, dao類, 在dao class注入JdbcTemplate
* 配置文件
```xml=
<!--組件掃描-->
<context:component-scan base-package="com.atguigu.spring5"></context:component-scan>
```
* DAO
```java=
@Repository
public class BookDaoImpl implements BookDao {
//注入JDBC Template
@Autowired
private JdbcTemplate jdbcTemplate;
}
```
* Service
```java=
@Service
public class BookService {
//注入dao
@Autowired
private BookDao bookDao;
}
```
### 10.2 Jdbc Template操作database(Add)
1. 對應資料庫table, 創建實體類(一般來說, 一個table一個對應的實體類)
```java=
package com.atguigu.spring5.entity;
public class User {
private String userId;
private String userName;
private String userStatus;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserStatus() {
return userStatus;
}
public void setUserStatus(String userStatus) {
this.userStatus = userStatus;
}
}
```
2. 編寫dao和service
2.1 在dao進行資料庫add操作
2.2 調用JdbcTemplate的update方法實現添加操作
* 第一個參數: SQL語法
* 第二個參數: 可變參數(? 代表的屬性)

```java=
@Repository
public class BookDaoImpl implements BookDao {
//1. 創建SQL語句
private String addBookSQL = "INSERT INTO t_book values(?,?,?)";
//注入JDBC Template
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void add(Book book) {
//2.調用方法實現
int resultRow = jdbcTemplate.update(addBookSQL,book.getBookId(), book.getBookName(), book.getBookStatus());
System.out.println(resultRow);
}
}
```
### 10.2 Jdbc Template操作database(修改和刪除)
1. 修改
```java=
@Override
public void updateBook(Book book) {
String sql = "UPDATE t_book set bookname=?,bookstatus=? WHERE bookid=?";
Object[] args = {book.getBookName(), book.getBookStatus(), book.getBookId()};
int update = jdbcTemplate.update(sql, args);
System.out.println(update);
}
```
2. 刪除
```java=
@Override
public void deleteBook(String id) {
String sql = "DELETE FROM t_book WHERE bookid=?";
int delete = jdbcTemplate.update(sql, id);
System.out.println(delete);
}
```
### 10.2 Jdbc Template操作database(查詢)
1. 查詢返回值
```java=
@Override
public int selectCount() {
String sql = "SELECT COUNT(*) FROM t_book";
return jdbcTemplate.queryForObject(sql, Integer.class); //參數: 1. SQL語法, 2. 返回值的Class
}
```
2. 查詢返回Object
* 第一個參數:SQL語句, 第三個參數:參數值(?的參數)
* `.queryForObject的第二個參數`Row Mapper是一個interface, 針對返回不同類型數據, 使用這個介面實現類完成數據封裝
```java=
//查詢詳情(返回Object)
@Override
public Book findOne(String id) {
String sql = "SELECT * WHERE bookid=?";
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
return book;
}
```
3. 查詢返回Collection
```java=
//查詢返回集合
@Override
public List<Book> findList() {
String sql = "SELECT * FROM t_book";
List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
return bookList;
}
```
### 10.3 批量添加
```java=
//批量添加
@Override
public void batchAddBooks(List<Object[]> batchAddArgs) {
String sql = "INSERT INTO t_book values(?,?,?)";
int[] ints = jdbcTemplate.batchUpdate(sql, batchAddArgs);
System.out.println(Arrays.toString(ints));
}
```
### 10.4 批量修改
```java=
//批量修改
@Override
public void batchUpdateBooks(List<Object[]> batchAddArgs) {
int[] ints = jdbcTemplate.batchUpdate(updateBookSql, batchAddArgs);
System.out.println(ints);
}
```
### 10.5 批量刪除
```java=
//批量刪除
@Override
public void batchDeleteBooks(List<Object[]> batchAddArgs) {
int[] ints = jdbcTemplate.batchUpdate(deleteBooksql, batchAddArgs);
System.out.println(Arrays.toString(ints));
}
```
---
# 11. Spring的交易管理
### 維持交易的ACID!
1. 原子性: 過程中不可分割, 操作中如果有一個失敗, 所有操作都失敗
2. 一致性: 操作前和操作後總量不變
3. 隔離性: 多事務操作之間(各種事務可以併發執行), 彼此不會產生影響
4. 持久性: 狀態的改變是持久的,不會失效。一旦某個事務提交, 則它造成的狀態變更就是永久性的。
### 當出現異常時
#### 事務操作
1. 開啟交易管理
2. 進行業務操作
3. (沒有發生異常)提交事務(=交易 = transaction)
4. (出現異常)事務回滾(rollback)
##### Spring交易管理介紹
1. 交易的程式碼要加在service層(業務邏輯層)
2. 在Spring進行事務管理操作, 有兩種方式:
1. 編程式交易管理
* 在程式碼裡面進行交易管理
* 需要不斷實現程式碼, 程式碼會變得攏長, 一般不使用
```java=
//原理
public void changeMoneys(){
try {
//第一步: 開啟交易管理
//第二步: 進行交易操作
userDao.addMoney();
//製造異常
int i = 10/0;
userDao.reduceMondey();
//第三步: 沒有發生異常, 提交事務
} catch (Exception e){
//第四步: 出現異常, 事務rollback
}
}
```
2.聲明式交易管理(使用)
1. :bulb:基於註解方式(方便, 簡單)
2. 基於xml配置文件方式
3. Spring的聲明式交易管理, 底層使用AOP
* 聲明式交易 : AOP (交由容器管理), 不改變原本的程式碼, 達到交易的效果
4.Spring事務管理API
* 提供一個介面,代表事務管理器, 此介面針對不同的ORM框架, 提供不同的實現類

#### 以註解方式實現聲明式事務管理
1. 在Spring配置文件配置事務管理器
```xml=
<!--配置事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入DataSource-->
<property name="dataSource" ref="dataSource" />
</bean>
```
2. 在Spring配置文件開啟事務註解
(1)在配置文件中引入名稱空間 tx
```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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd">
```
(2)開啟事務註解
```xml
<!--開啟事務註解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
```
3. 在Service類上, 或者Service類裡面的method上面, 添加事務註解
(1) `@Transactional` 可以加上class上面, 也可以加上方法上面
(2)如果把這個註解加在class上面, 表示這個class的所有方法都添加事務
(3)如果把這個註解加在方法上面, 表示為這個方法添加事務
#### 聲明式事務管理參數配置
1. 在Service class上面添加註釋`@Transactional`, 在這個註釋裡面可以配置事務相關參數

* propogation: 事務的傳播行為
* 多事務方法直接進行調用, 這個過程中事務是如何進行管理的


(如果不指定傳播行為參數, 預設為REQUIRED)
* isolation: 事務的隔離級別
* 併發操作中產生的級別
* 多事務操作之間, 彼此不會產生影響
* 不考慮隔離性的話, 會產生三讀問題
1. 髒讀: 一個未提交事務, 讀取到另一個未提交事務的數據
2. 不可重複讀: 一個未提交事務, 讀取到另一個提交事務修改的數據
3. 幻讀: 一個未提交事務, 讀取到另一個提交事務添加的數據
* 解決方法: 通過設置事務隔離級別, 解決讀的問題

* Spring的預設隔離級別是根據使用的database而不同 ex. MySQL=REPEATABLE READ

```java=
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
```
* timeout: 超時時間
* 事務需要在一定時間內提交, 否則進行rollback
* Spring預設值是-1(不超時), 設置單位以秒來計算
```java=
@Transactional(timeout = 5,propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
```
* readOnly: 是否只讀
* 讀:查詢操作, 寫:添加修改刪除操作
* readOnly預設值為false, 表可以查詢, 也可以新增刪除修改
* readyOnly值設置為true之後, 只能進行查詢操作
```java=
@Transactional(readOnly = true,timeout = 5,propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
```
* rollbackFor: 回滾
* 設置出現那些異常, 進行交易rollback
* [觸發時可能遇到的問題, 參考我的notion](https://www.notion.so/transactional-does-not-rollback-on-checked-exceptions-97bc2dc798df49a98d21d682c7d36e0f)
:::danger
:warning: 注意 :warning:
spring的交易管理註解 @Transactional 預設只會被runtime exception觸發rollback, checked exception會因為可以使用try catch捕獲而不觸發rollback
:bulb: 解決方法: @Transactional(rollbackFor = Exception.class)
:::
* noRollbackFor: 不回滾
* 設置出現那些異常, 不進行交易rollback
#### XML方式實現聲明式事務管理
##### 在Spring配置文件中進行配置
1. 配置事務管理器
2. 配置通知
3. 配置切入點和切面
需要在xml檔裡面撰寫配置
```xml=
<!--配置聲明式交易-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="datasource" />
</bean>
<!--結合AOP實現交易的織入(注意!!需導入XML約束文件!)-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--配置交易方法-->
<!--配置交易傳播特性: new propagation(預設為REQUIRED)-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>(可以只寫這個)
</tx:attributes>
</tx:advice>
<!--配置交易切入點-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.kuang.mapper.*.* (..))" />
<aop:advicor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
```
#### 完全註解方式實現聲明式事務管理
```java=
package com.atguigu.spring5.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration //表示此class為配置類
@ComponentScan(basePackages = "com.atguigu") //開啟組件掃描
@EnableTransactionManagement//開啟事務管理
public class TxConfig {
// 創建連接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///user_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
// 創建JDBC template對象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
//()裡面的參數, spring會到ioc容器中, 根據類型找到dataSource
JdbcTemplate jdbc = new JdbcTemplate();
//注入dataSource
jdbc.setDataSource(dataSource);
return jdbc;
}
// 創建事務管理器
@Bean
public DataSourceTransactionManager manager(DataSource dataSource){
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
}
```
### Q:為何需要交易管理
* 如無配置交易管理, 則可能存在數據提交不一致的情況(ex. 存提款餘額金額不一致)
* 如果我們不在SPRING中配置聲明式交易, 我們就需要在程式碼裡面手動配置交易管理!
* 交易管理在開發中相當重要, 涉及到數據的一致性和完整性問題, 不可馬虎!
---
# Spring5新功能
### 整合日誌框架log4j2
### Nullable註解
* Nullable註解可以用在方法上面, 屬性上面, 參數上面
1. method上面: 表示方法返回值可以為空
2. 參數前面: 參數值可以為空
3. 屬性上面: 屬性值可以為空
### Spring5核心容器支援函數式風格表達式GenericApplicationContext(lambda風格)
```java=
//函數式風格創建對象, 交給Spring進行管理
@Test
public void testGenericApplicationContext(){
//第一步: 創建GenericApplicationContext對象
GenericApplicationContext context = new GenericApplicationContext();
//第二步: 調用方法進行對象註冊
context.refresh();
context.registerBean(User.class, () -> new User());
//第三步: 獲取在Spring裡面註冊的對象
// !!此處注意不是用小寫的bean name獲取bean, 因為我們並沒有用bean name註冊, ioc容器裡面沒有此實例!!
//需要用完整路徑獲取bean, 或註冊時指定bean name
User user = (User)context.getBean("com.atguigu.spring5.dao.User");
System.out.println(user);
}
```
### Spring5支援整合JUnit5
* 整合JUnit4
* 第一步: 引入Spring相關針對測試之依賴


* 第二步: 創建測試類, 使用註解方式完成
```java=
@RunWith(SpringJUnit4ClassRunner.class) //單元測試框架
@ContextConfiguration("classpath:bean1.xml") //加載配置文件
public class JTest {
//直接注入Service
@Autowired
private UserService userService;
@Test
public void test1(){
userService.changeMoneys();
}
}
```
* Spring5整合JUnit5
* 第一步: 引入JUnit5的jar包
* 第二步: 創建測試類, 使用註解完成
* 也可使用複合註解替代上面兩行註解完成整合`@SpringJUnitConfig(locations = "classpath:bean1.xml")`
```java=
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
// @SpringJUnitConfig(locations = "classpath:bean1.xml")
public class JTest5 {
//直接注入Service
@Autowired
private UserService userService;
@Test
public void test1() {
userService.changeMoneys();
}
}
```