1市俊、什么是 AOP
(1)面向切面編程(方面)庄蹋,利用 AOP 可以對業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離官研,從而使得
業(yè)務(wù)邏輯各部分之間的耦合度降低婆殿,提高程序的可重用性丛肢,同時(shí)提高了開發(fā)的效率围肥。
(2)通俗描述:不通過修改源代碼方式,在主干功能里面添加新功能
(3)使用登錄例子說明 AOP
2蜂怎、AOP(底層原理)
1穆刻、AOP 底層使用動態(tài)代理
(1)有兩種情況動態(tài)代理
第一種 有接口情況,使用 JDK 動態(tài)代理
? 創(chuàng)建接口實(shí)現(xiàn)類代理對象杠步,增強(qiáng)類的方法
第二種 沒有接口情況氢伟,使用 CGLIB 動態(tài)代理
? 創(chuàng)建子類的代理對象,增強(qiáng)類的方法
2幽歼、AOP(JDK 動態(tài)代理)
1朵锣、使用 JDK 動態(tài)代理,使用 Proxy 類里面的方法創(chuàng)建代理對象
Proxy
(1)調(diào)用 newProxyInstance 方法
newProxyInstance
方法有三個(gè)參數(shù):
第一參數(shù)甸私,類加載器
第二參數(shù)诚些,增強(qiáng)方法所在的類,這個(gè)類實(shí)現(xiàn)的接口皇型,支持多個(gè)接口
第三參數(shù)诬烹,實(shí)現(xiàn)這個(gè)接口 InvocationHandler,創(chuàng)建代理對象弃鸦,寫增強(qiáng)的部分
2绞吁、編寫 JDK 動態(tài)代理代碼
(1)創(chuàng)建接口,定義方法
public interface UserDao {
public int add(int a, int b);
public String update(String id);
}
(2)創(chuàng)建接口實(shí)現(xiàn)類唬格,實(shí)現(xiàn)方法
public class UserDaoImpl implements UserDao {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public String update(String id) {
return id;
}
}
(3)使用 Proxy 類創(chuàng)建接口代理對象
public class JDKProxy {
public static void main(String[] args) {
//創(chuàng)建接口實(shí)現(xiàn)類代理對象
Class[] interfaces = {UserDao.class};
/* 匿名內(nèi)部類寫法
Proxy.newProxyInstance (JDKProxy.class.getClassLoader ( ), interfaces, new InvocationHandler ( ) {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
*/
UserDaoImpl userDao = new UserDaoImpl ( );
UserDao dao = (UserDao) Proxy.newProxyInstance (JDKProxy.class.getClassLoader ( ), interfaces, new UserDaoProxy (userDao));
int result = dao.add (1, 2);
System.out.println ("result:" + result);
}
}
//創(chuàng)建代理對象
class UserDaoProxy implements InvocationHandler {
//1 把創(chuàng)建的是誰的代理對象家破,把誰傳遞過來
//有參數(shù)構(gòu)造傳遞
private Object obj;
public UserDaoProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println ("方法之前執(zhí)行...." + method.getName ( ) + " :傳遞的參數(shù)..." + Arrays.toString (args));
//被增強(qiáng)的方法執(zhí)行
Object res = method.invoke (obj, args);
//方法之后
System.out.println ("方法之后執(zhí)行...." + obj);
return res;
}
}
---------------------------輸出打印結(jié)果---------------------------------------
方法之前執(zhí)行....add :傳遞的參數(shù)...[1, 2]
方法之后執(zhí)行....com.kk.jdk.UserDaoImpl@2b193f2d
result:3
3、AOP(術(shù)語)
1西轩、連接點(diǎn):類里面哪些可以被增強(qiáng)的方法
2员舵、切入點(diǎn):實(shí)際被增強(qiáng)的方法
3脑沿、通知(增強(qiáng)):
(1)實(shí)際增強(qiáng)的邏輯部分稱為通知(增強(qiáng))
(2):通知的類型
通知的類型
4藕畔、切面:是動作,把通知應(yīng)用到切入點(diǎn)的過程
AOP 操作(準(zhǔn)備工作)
1庄拇、Spring 框架一般都是基于 AspectJ 實(shí)現(xiàn) AOP 操作
(1)AspectJ 不是 Spring 組成部分注服,獨(dú)立 AOP 框架韭邓,一般把 AspectJ 和 Spirng 框架一起使
用,進(jìn)行 AOP 操作
2溶弟、基于 AspectJ 實(shí)現(xiàn) AOP 操作
(1)基于 xml 配置文件實(shí)現(xiàn)
(2)基于注解方式實(shí)現(xiàn)(使用)
3女淑、在項(xiàng)目工程里面引入 AOP 相關(guān)依賴
aop依賴
4、切入點(diǎn)表達(dá)式
(1)切入點(diǎn)表達(dá)式作用:知道對哪個(gè)類里面的哪個(gè)方法進(jìn)行增強(qiáng)
(2)語法結(jié)構(gòu): execution([權(quán)限修飾符] [返回類型] [類全路徑] [方法名稱]([參數(shù)列表] ))
舉例 1:對 com.kk.dao.BookDao 類里面的 add 進(jìn)行增強(qiáng)
execution(* com.kk.dao.BookDao.add(..))
舉例 2:對 com.kk.dao.BookDao 類里面的所有的方法進(jìn)行增強(qiáng)
execution(* com.kk.dao.BookDao.* (..))
舉例 3:對 com.kk.dao 包里面所有類辜御,類里面所有方法進(jìn)行增強(qiáng)
execution(* com.kk.dao.. (..))
AOP 操作(AspectJ 注解)
1鸭你、創(chuàng)建類,在類里面定義方法
public class User {
public void add() {
System.out.println ("add.......");
}
}
2擒权、創(chuàng)建增強(qiáng)類(編寫增強(qiáng)邏輯)
(1)在增強(qiáng)類里面袱巨,創(chuàng)建方法,讓不同方法代表不同通知類型
//增強(qiáng)的類
public class UserProxy {
public void before() {//前置通知
System.out.println ("before......");
}
}
3碳抄、進(jìn)行通知的配置
(1)在 spring 配置文件中愉老,開啟注解掃描
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 開啟注解掃描 -->
<context:component-scan base-package="com.kk.aopanno"></context:component-scan>
</beans>
(2)使用注解創(chuàng)建 User 和 UserProxy 對象
(3)在增強(qiáng)類上面添加注解 @Aspect
//增強(qiáng)的類
@Component
@Aspect //生成代理對象
public class UserProxy {
public void before() {//前置通知
System.out.println ("before......");
}
}
(4)在 spring 配置文件中開啟生成代理對象
<!-- 開啟 Aspect 生成代理對象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4、配置不同類型的通知
(1)在增強(qiáng)類的里面剖效,在作為通知方法上面添加通知類型注解嫉入,使用切入點(diǎn)表達(dá)式配置
//增強(qiáng)的類
@Component
@Aspect //生成代理對象
public class UserProxy {
//前置通知
//@Before 注解表示作為前置通知
@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void before() {
System.out.println ("before.........");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.kk.aopanno.User.add(..))")
public void afterReturning() {
System.out.println ("afterReturning.........");
}
//最終通知
@After(value = "execution(* com.kk.aopanno.User.add(..))")
public void after() {
System.out.println ("after.........");
}
//異常通知
@AfterThrowing(value = "execution(* com.kk.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println ("afterThrowing.........");
}
//環(huán)繞通知
@Around(value = "execution(* com.kk.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws
Throwable {
System.out.println ("環(huán)繞之前.........");
//被增強(qiáng)的方法執(zhí)行
proceedingJoinPoint.proceed ( );
System.out.println ("環(huán)繞之后.........");
}
}
5、相同的切入點(diǎn)抽取
//相同切入點(diǎn)抽取
@Pointcut(value = "execution(* com.kk.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before 注解表示作為前置通知
@Before(value = "pointdemo()")
public void before() {
System.out.println ("before.........");
}
6璧尸、有多個(gè)增強(qiáng)類多同一個(gè)方法進(jìn)行增強(qiáng)咒林,設(shè)置增強(qiáng)類優(yōu)先級
(1)在增強(qiáng)類上面添加注解 @Order(數(shù)字類型值),數(shù)字類型值越小優(yōu)先級越高
@Component
@Aspect
@Order(1)
public class PersonProxy
7爷光、完全使用注解開發(fā)
(1)創(chuàng)建配置類映九,不需要創(chuàng)建 xml 配置文件
@Configuration
@ComponentScan(basePackages = {"com.kk"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}
AOP 操作(AspectJ 配置文件)
1、創(chuàng)建兩個(gè)類瞎颗,增強(qiáng)類和被增強(qiáng)類件甥,創(chuàng)建方法
2、在 spring 配置文件中創(chuàng)建兩個(gè)類對象
<bean id="user" class="com.kk.aopanno.User"></bean>
<bean id="userProxy" class="com.kk.aopanno.UserProxy"></bean>
3哼拔、在 spring 配置文件中配置切入點(diǎn)
<!--配置 aop 增強(qiáng)-->
<aop:config>
<!--切入點(diǎn)-->
<aop:pointcut id="p" expression="execution(* com.kk.aopanno.User.add(..))"/>
<!--配置切面-->
<aop:aspect ref="userProxy">
<!--增強(qiáng)作用在具體的方法上-->
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
4引有、JdbcTemplate(概念和準(zhǔn)備)
1、什么是 JdbcTemplate
(1)Spring 框架對 JDBC 進(jìn)行封裝倦逐,使用 JdbcTemplate 方便實(shí)現(xiàn)對數(shù)據(jù)庫操作
2譬正、準(zhǔn)備工作
(1)引入相關(guān) jar 包
(2)在 spring 配置文件配置數(shù)據(jù)庫連接池
<!-- 數(shù)據(jù)庫連接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="jdbc:mysql:///test"/>
<property name="username" value="root"/>
<property name="password" value="a1b2c3"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
(3)配置 JdbcTemplate 對象,注入 DataSource
<!-- JdbcTemplate 對象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入 dataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
(4)創(chuàng)建 service 類檬姥,創(chuàng)建 dao 類曾我,在 dao 注入 jdbcTemplate 對象
? 配置文件
<!-- 組件掃描 -->
<context:component-scan base-package="com.kk"></context:component-scan>
? Service
@Service
public class BookService {
//注入 dao
@Autowired
private BookDao bookDao;
}
? Dao
@Repository
public class BookDaoImpl implements BookDao {
//注入 JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
}
4.1、JdbcTemplate 操作數(shù)據(jù)庫(添加)
1健民、對應(yīng)數(shù)據(jù)庫創(chuàng)建實(shí)體類
user
2抒巢、編寫 service 和 dao
(1)在 dao 進(jìn)行數(shù)據(jù)庫添加操作
(2)調(diào)用 JdbcTemplate 對象里面 update 方法實(shí)現(xiàn)添加操作
update
? 有兩個(gè)參數(shù)
? 第一個(gè)參數(shù):sql 語句
? 第二個(gè)參數(shù):可變參數(shù),設(shè)置 sql 語句值
public class BookDaoImpl implements BookDao { //注入 JdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; //添加的方法 @Override public void add(Book book) { //1 創(chuàng)建 sql 語句 String sql = "insert into t_book values(?,?,?)"; //2 調(diào)用方法實(shí)現(xiàn) Object[] args = {book.getUserId ( ), book.getUsername ( ), book.getUsetatus ( )}; int update = jdbcTemplate.update (sql, args); System.out.println (update); } }
3秉犹、測試類
@Test
public void testJdbcTemplate() {
ApplicationContext context =
new ClassPathXmlApplicationContext ("bean7.xml");
BookService bookService = context.getBean("bookService",
BookService.class);
Book book = new Book();
book.setUserId("1");
book.setUsername("java");
book.setUsetatus("a");
bookService.addBook(book);
}
4.2蛉谜、JdbcTemplate 操作數(shù)據(jù)庫(修改和刪除)
1稚晚、修改
@Override
public void updateBook(Book book) {
String sql = "update t_book set username=?,ustatus=? where user_id=?";
Object[] args = {book.getUsername ( ), book.getUsetatus ( ), book.getUserId ( )};
int update = jdbcTemplate.update (sql, args);
System.out.println (update);
}
2、刪除
@Override
public void delete(String id) {
String sql = "delete from t_book where user_id=?";
int update = jdbcTemplate.update (sql, id);
System.out.println (update);
}
4.3型诚、JdbcTemplate 操作數(shù)據(jù)庫(查詢返回某個(gè)值)
1客燕、查詢表里面有多少條記錄,返回是某個(gè)值
2狰贯、使用 JdbcTemplate 實(shí)現(xiàn)查詢返回某個(gè)值代碼
queryForObject
? 有兩個(gè)參數(shù)
? 第一個(gè)參數(shù):sql 語句
? 第二個(gè)參數(shù):返回類型 Class//查詢表記錄數(shù) @Override public int selectCount() { String sql = "select count(*) from t_book"; Integer count = jdbcTemplate.queryForObject (sql, Integer.class); return count; }
4.4也搓、JdbcTemplate 操作數(shù)據(jù)庫(查詢返回對象)
1、場景:查詢圖書詳情
2涵紊、JdbcTemplate 實(shí)現(xiàn)查詢返回對象
queryForObject
? 有三個(gè)參數(shù)
? 第一個(gè)參數(shù):sql 語句
? 第二個(gè)參數(shù):RowMapper 是接口还绘,針對返回不同類型數(shù)據(jù),使用這個(gè)接口里面實(shí)現(xiàn)類完成
數(shù)據(jù)封裝
? 第三個(gè)參數(shù):sql 語句值//查詢返回對象 @Override public Book findBookInfo(String id) { String sql = "select * from t_book where user_id=?"; //調(diào)用方法 Book book = jdbcTemplate.queryForObject (sql, new BeanPropertyRowMapper<Book> (Book.class), id); return book; }
4.5栖袋、JdbcTemplate 操作數(shù)據(jù)庫(查詢返回集合---分頁)
1拍顷、場景:查詢圖書列表分頁…
2、調(diào)用 JdbcTemplate 方法實(shí)現(xiàn)查詢返回集合
query
? 有三個(gè)參數(shù)
? 第一個(gè)參數(shù):sql 語句
? 第二個(gè)參數(shù):RowMapper 是接口塘幅,針對返回不同類型數(shù)據(jù)昔案,使用這個(gè)接口里面實(shí)現(xiàn)類完成
數(shù)據(jù)封裝
? 第三個(gè)參數(shù):sql 語句值//查詢返回集合 @Override public List<Book> findAllBook() { String sql = "select * from t_book"; //調(diào)用方法 List<Book> bookList = jdbcTemplate.query (sql, new BeanPropertyRowMapper<Book> (Book.class)); return bookList; }
4.6、JdbcTemplate 操作數(shù)據(jù)庫(批量操作)
1电媳、批量操作:操作表里面多條記錄
2踏揣、JdbcTemplate 實(shí)現(xiàn)批量添加操作
? 有兩個(gè)參數(shù)
? 第一個(gè)參數(shù):sql 語句
? 第二個(gè)參數(shù):List 集合,添加多條記錄數(shù)據(jù)
//批量添加
@Override
public void batchAddBook(List<Object[]> batchArgs) {
String sql = "insert into t_book values(?,?,?)";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
//批量添加測試
List<Object[]> batchArgs = new ArrayList<> ();
Object[] o1 = {"3","java","a"};
Object[] o2 = {"4","c++","b"};
Object[] o3 = {"5","MySQL","c"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
//調(diào)用批量添加
bookService.batchAdd(batchArgs);
3匾乓、JdbcTemplate 實(shí)現(xiàn)批量修改操作
//批量修改
@Override
public void batchUpdateBook(List<Object[]> batchArgs) {
String sql = "update t_book set username=?,ustatus=? where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
//批量修改
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"java0909","a3","3"};
Object[] o2 = {"c++1010","b4","4"};
Object[] o3 = {"MySQL1111","c5","5"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
//調(diào)用方法實(shí)現(xiàn)批量修改
bookService.batchUpdate(batchArgs);
4捞稿、JdbcTemplate 實(shí)現(xiàn)批量刪除操作
//批量刪除
@Override
public void batchDeleteBook(List<Object[]> batchArgs) {
String sql = "delete from t_book where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
//批量刪除
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"3"};
Object[] o2 = {"4"};
batchArgs.add(o1);
batchArgs.add(o2);
//調(diào)用方法實(shí)現(xiàn)批量刪除
bookService.batchDelete(batchArgs);
5、事務(wù)操作(事務(wù)概念)
1拼缝、事務(wù)操作(事務(wù)概念)
1娱局、什么事務(wù)
(1)事務(wù)是數(shù)據(jù)庫操作最基本單元,邏輯上一組操作咧七,要么都成功衰齐,如果有一個(gè)失敗所有操
作都失敗
(2)典型場景:銀行轉(zhuǎn)賬
*^ lucy 轉(zhuǎn)賬 100 元 給 mary
*^ lucy 少 100,mary 多 100
2继阻、事務(wù)四個(gè)特性(ACID) (1)原子性
(2)一致性
(3)隔離性
(4)持久性
2耻涛、事務(wù)操作(搭建事務(wù)操作環(huán)境)
業(yè)務(wù)模擬圖
1、創(chuàng)建數(shù)據(jù)庫表瘟檩,添加記錄
模擬數(shù)據(jù)
2抹缕、創(chuàng)建 service,搭建 dao墨辛,完成對象創(chuàng)建和注入關(guān)系
(1)service 注入 dao卓研,在 dao 注入 JdbcTemplate,在 JdbcTemplate 注入 DataSource
service&&dao
3背蟆、在 dao 創(chuàng)建兩個(gè)方法:多錢和少錢的方法鉴分,在 service 創(chuàng)建方法(轉(zhuǎn)賬的方法)@Repository public class UserDaoImpl implements UserDao { @Autowired private JdbcTemplate jdbcTemplate; //lucy 轉(zhuǎn)賬 100 給 mary //少錢 @Override public void reduceMoney() { String sql = "update t_account set money=money-? where username=?"; jdbcTemplate.update (sql, 100, "lucy"); } //多錢 @Override public void addMoney() { String sql = "update t_account set money=money+? where username=?"; jdbcTemplate.update (sql, 100, "mary"); } } @Service public class UserService { //注入 dao @Autowired private UserDao userDao; //轉(zhuǎn)賬的方法 public void accountMoney() { //lucy 少 100 userDao.reduceMoney ( ); //mary 多 100 userDao.addMoney ( ); } }
4哮幢、上面代碼带膀,如果正常執(zhí)行沒有問題的志珍,但是如果代碼執(zhí)行過程中出現(xiàn)異常,有問題
問題代碼
(1)使用事務(wù)解決上面問題:
事務(wù)解決
3垛叨、事務(wù)操作(Spring 事務(wù)管理介紹)
1伦糯、事務(wù)添加到 JavaEE 三層結(jié)構(gòu)里面 Service 層(業(yè)務(wù)邏輯層)
2、在 Spring 進(jìn)行事務(wù)管理操作
(1)有兩種方式:編程式事務(wù)管理和聲明式事務(wù)管理(使用)
3嗽元、聲明式事務(wù)管理
(1)基于注解方式(使用)
(2)基于 xml 配置文件方式
4敛纲、在 Spring 進(jìn)行聲明式事務(wù)管理,底層使用 AOP 原理
5剂癌、Spring 事務(wù)管理 API
(1)提供一個(gè)接口淤翔,代表事務(wù)管理器,這個(gè)接口針對不同的框架提供不同的實(shí)現(xiàn)類
PlatformTransactionManager結(jié)構(gòu)樹
4佩谷、事務(wù)操作(注解聲明式事務(wù)管理)
1旁壮、在 spring 配置文件配置事務(wù)管理器
<!--創(chuàng)建事務(wù)管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入數(shù)據(jù)源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
2、在 spring 配置文件谐檀,開啟事務(wù)注解
(1)在 spring 配置文件引入名稱空間 tx
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>
(2)開啟事務(wù)注解
<!--開啟事務(wù)注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
3抡谐、在 service 類上面(或者 service 類里面方法上面)添加事務(wù)注解
(1)@Transactional,這個(gè)注解添加到類上面桐猬,也可以添加方法上面
(2)如果把這個(gè)注解添加類上面麦撵,這個(gè)類里面所有的方法都添加事務(wù)
(3)如果把這個(gè)注解添加方法上面,為這個(gè)方法添加事務(wù)
@Service
@Transactional
public class UserService {
}
5溃肪、事務(wù)操作(聲明式事務(wù)管理參數(shù)配置)
1免胃、在 service 類上面添加注解@Transactional,在這個(gè)注解里面可以配置事務(wù)相關(guān)參數(shù)
image.png
2惫撰、propagation:事務(wù)傳播行為
(1)多事務(wù)方法直接進(jìn)行調(diào)用杜秸,這個(gè)過程中事務(wù) 是如何進(jìn)行管理的
多事務(wù)模擬圖
(2)事務(wù)的傳播行為是由傳播屬性指定的,Spring定義了7中類傳播行為
7中傳播行為
3润绎、ioslation:事務(wù)隔離級別
(1)事務(wù)有特性成為隔離性撬碟,多事務(wù)操作之間不會產(chǎn)生影響。不考慮隔離性產(chǎn)生很多問題
(2)有三個(gè)讀問題:臟讀莉撇、不可重復(fù)讀呢蛤、虛(幻)讀
(3)臟讀:一個(gè)未提交事務(wù)讀取到另一個(gè)未提交事務(wù)的數(shù)據(jù)
隔離級別
(4)不可重復(fù)讀:一個(gè)未提交事務(wù)讀取到另一提交事務(wù)修改數(shù)據(jù)
不可重復(fù)讀
(5)虛讀:一個(gè)未提交事務(wù)讀取到另一提交事務(wù)添加數(shù)據(jù)
(6)解決:通過設(shè)置事務(wù)隔離級別,解決讀問題
隔離級別
4棍郎、timeout:超時(shí)時(shí)間
(1)事務(wù)需要在一定時(shí)間內(nèi)進(jìn)行提交其障,如果不提交進(jìn)行回滾
(2)默認(rèn)值是 -1 ,設(shè)置時(shí)間以秒單位進(jìn)行計(jì)算
5涂佃、readOnly:是否只讀
(1)讀:查詢操作励翼,寫:添加修改刪除操作
(2)readOnly 默認(rèn)值 false蜈敢,表示可以查詢,可以添加修改刪除操作
(3)設(shè)置 readOnly 值是 true汽抚,設(shè)置成 true 之后抓狭,只能查詢
6、rollbackFor:回滾
(1)設(shè)置出現(xiàn)哪些異常進(jìn)行事務(wù)回滾
7造烁、noRollbackFor:不回滾
(1)設(shè)置出現(xiàn)哪些異常不進(jìn)行事務(wù)回滾
6否过、事務(wù)操作(XML 聲明式事務(wù)管理)
1、在 spring 配置文件中進(jìn)行配置
第一步 配置事務(wù)管理器
第二步 配置通知
第三步 配置切入點(diǎn)和切面
<!--1 創(chuàng)建事務(wù)管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入數(shù)據(jù)源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2 配置通知-->
<tx:advice id="txadvice">
<!--配置事務(wù)參數(shù)-->
<tx:attributes>
<!--指定哪種規(guī)則的方法上面添加事務(wù)-->
<tx:method name="accountMoney" propagation="REQUIRED"/>
<!--<tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
<!--3 配置切入點(diǎn)和切面-->
<aop:config>
<!--配置切入點(diǎn)-->
<aop:pointcut id="pt" expression="execution(* com.kk.service.UserService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
7惭蟋、事務(wù)操作(完全注解聲明式事務(wù)管理)--注解在java中代替Xml的配置
1苗桂、創(chuàng)建配置類,使用配置類替代 xml 配置文件
@Configuration //配置類
@ComponentScan(basePackages = "com.kk") //組件掃描
@EnableTransactionManagement //開啟事務(wù)
public class TxConfig {
//創(chuàng)建數(shù)據(jù)庫連接池
@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;
}
//創(chuàng)建 JdbcTemplate 對象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {//import javax.sql.DataSource;
//到 ioc 容器中根據(jù)類型找到 dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate ( );
//注入 dataSource
jdbcTemplate.setDataSource (dataSource);
return jdbcTemplate;
}
//創(chuàng)建事務(wù)管理器
@Bean
public DataSourceTransactionManager
getDataSourceTransactionManager(DataSource dataSource) {//import javax.sql.DataSource;
DataSourceTransactionManager transactionManager = new
DataSourceTransactionManager ( );
transactionManager.setDataSource (dataSource);
return transactionManager;
}
}
Spring5 框架新功能
1告组、整個(gè) Spring5 框架的代碼基于 Java8煤伟,運(yùn)行時(shí)兼容 JDK9,許多不建議使用的類和方
法在代碼庫中刪除
2木缝、Spring 5.0 框架自帶了通用的日志封裝
(1)Spring5 已經(jīng)移除 Log4jConfigListener便锨,官方建議使用 Log4j2
(2)Spring5 框架整合 Log4j2
第一步 引入 jar 包
Log4j2_lib
第二步 創(chuàng)建 log4j2.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?> <!--日志級別以及優(yōu)先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration后面的status用于設(shè)置log4j2自身內(nèi)部的信息輸出,可以不設(shè)置氨肌,當(dāng)設(shè)置成trace時(shí)鸿秆,可以看到log4j2內(nèi)部各種詳細(xì)輸出--> <configuration status="INFO"> <!--先定義所有的appender--> <appenders> <!--輸出日志信息到控制臺--> <console name="Console" target="SYSTEM_OUT"> <!--控制日志輸出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - >%msg%n"/> </console> </appenders> <!--然后定義logger,只有定義了logger并引入的appender怎囚,appender才會生效--> <!--root:用于指定項(xiàng)目的根日志卿叽,如果沒有單獨(dú)指定Logger,則會使用root作為默認(rèn)的日志輸出--> <loggers> <root level="info"> <appender-ref ref="Console"/> </root> </loggers> </configuration>
3恳守、Spring5 框架核心容器支持@Nullable 注解
(1)@Nullable 注解可以使用在方法上面考婴,屬性上面,參數(shù)上面催烘,表示方法返回可以為空沥阱,屬性值可以
為空,參數(shù)值可以為空
(2)注解用在方法上面伊群,方法返回值可以為空
方法
(3)注解使用在方法參數(shù)里面考杉,方法參數(shù)可以為空
參數(shù)
(4)注解使用在屬性上面,屬性值可以為空
屬性
4舰始、Spring5 核心容器支持函數(shù)式風(fēng)格 GenericApplicationContext
//函數(shù)式風(fēng)格創(chuàng)建對象崇棠,交給 spring 進(jìn)行管理
@Test
public void testGenericApplicationContext() {
//1 創(chuàng)建 GenericApplicationContext 對象
GenericApplicationContext context = new GenericApplicationContext ( );
//2 調(diào)用 context 的方法對象注冊
context.refresh ( );
context.registerBean ("user1", User.class, () -> new User ( ));
//3 獲取在 spring 注冊的對象
// User user = (User)context.getBean("com.kk.test.User");
User user = (User) context.getBean ("user1");
System.out.println (user);
}
5、Spring5 支持整合 JUnit5
(1)整合 JUnit4(4可忽略直接了解5)
第一步 引入 Spring 相關(guān)針對測試依賴:
引入
第二步 創(chuàng)建測試類丸卷,使用注解方式完成:
@RunWith(SpringJUnit4ClassRunner.class) //單元測試框架 @ContextConfiguration("classpath:bean1.xml") //加載配置文件 public class JTest4 { @Autowired private UserService userService; @Test public void test1() { userService.accountMoney ( ); } }
(2)Spring5 整合 JUnit5
第一步 引入 JUnit5 的 jar 包:
ju5
第二步 創(chuàng)建測試類枕稀,使用注解完成:
@ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:bean1.xml") public class JTest5 { @Autowired private UserService userService; @Test public void test1() { userService.accountMoney ( ); } }
(3)使用一個(gè)復(fù)合注解替代上面兩個(gè)注解完成整合:
@SpringJUnitConfig(locations = "classpath:bean1.xml") public class JTest5 { @Autowired private UserService userService; @Test public void test1() { userService.accountMoney ( ); } }
二、Spring5 框架新功能(Webflux)
1、SpringWebflux 介紹
(1)是 Spring5 添加新的模塊萎坷,用于 web 開發(fā)的凹联,功能和 SpringMVC 類似的,Webflux 使用
當(dāng)前一種比較流程響應(yīng)式編程出現(xiàn)的框架哆档。
web-webFlux
(2)使用傳統(tǒng) web 框架蔽挠,比如 SpringMVC,這些基于 Servlet 容器虐呻,Webflux 是一種異步非阻
塞的框架象泵,異步非阻塞的框架在 Servlet3.1 以后才支持寞秃,核心是基于 Reactor 的相關(guān) API 實(shí)現(xiàn)
的斟叼。
(3)解釋什么是異步非阻塞
? 異步和同步
? 非阻塞和阻塞
** 上面都是針對對象不一樣
** 異步和同步針對調(diào)用者,調(diào)用者發(fā)送請求春寿,如果等著對方回應(yīng)之后才去做其他事情就是同
步朗涩,如果發(fā)送請求之后不等著對方回應(yīng)就去做其他事情就是異步
** 阻塞和非阻塞針對被調(diào)用者,被調(diào)用者受到請求之后绑改,做完請求任務(wù)之后才給出反饋就是阻
塞谢床,受到請求之后馬上給出反饋然后再去做事情就是非阻塞
(4)Webflux 特點(diǎn):
第一 非阻塞式:在有限資源下,提高系統(tǒng)吞吐量和伸縮性厘线,以 Reactor 為基礎(chǔ)實(shí)現(xiàn)響應(yīng)式編程
第二 函數(shù)式編程:Spring5 框架基于 java8识腿,Webflux 使用 Java8 函數(shù)式編程方式實(shí)現(xiàn)路由請求
(5)比較 SpringMVC
比較圖
第一 兩個(gè)框架都可以使用注解方式,都運(yùn)行在 Tomet 等容器中
第二 SpringMVC 采用命令式編程造壮,Webflux 采用異步響應(yīng)式編程
2渡讼、響應(yīng)式編程(Java 實(shí)現(xiàn))
(1)什么是響應(yīng)式編程
響應(yīng)式編程是一種面向數(shù)據(jù)流和變化傳播的編程范式。這意味著可以在編程語言中很方便
地表達(dá)靜態(tài)或動態(tài)的數(shù)據(jù)流耳璧,而相關(guān)的計(jì)算模型會自動將變化的值通過數(shù)據(jù)流進(jìn)行傳播成箫。
電子表格程序就是響應(yīng)式編程的一個(gè)例子。單元格可以包含字面值或類似"=B1+C1"的公
式旨枯,而包含公式的單元格的值會依據(jù)其他單元格的值的變化而變化蹬昌。
(2)Java8 及其之前版本
? 提供的觀察者模式兩個(gè)類 Observer 和 Observablepublic class ObserverDemo extends Observable { public static void main(String[] args) { ObserverDemo observer = new ObserverDemo ( ); //添加觀察者 observer.addObserver ((o, arg) -> { System.out.println ("發(fā)生變化"); }); observer.addObserver ((o, arg) -> { System.out.println ("手動被觀察者通知,準(zhǔn)備改變"); }); observer.setChanged ( ); //數(shù)據(jù)變化 observer.notifyObservers ( ); //通知 } }
3攀隔、響應(yīng)式編程(Reactor 實(shí)現(xiàn))
(1)響應(yīng)式編程操作中皂贩,Reactor 是滿足 Reactive 規(guī)范框架
(2)Reactor 有兩個(gè)核心類,Mono 和 Flux昆汹,這兩個(gè)類實(shí)現(xiàn)接口 Publisher明刷,提供豐富操作
符。Flux 對象實(shí)現(xiàn)發(fā)布者筹煮,返回 N 個(gè)元素遮精;Mono 實(shí)現(xiàn)發(fā)布者,返回 0 或者 1 個(gè)元素
(3)Flux 和 Mono 都是數(shù)據(jù)流的發(fā)布者,使用 Flux 和 Mono 都可以發(fā)出三種數(shù)據(jù)信號:
元素值本冲,錯誤信號准脂,完成信號,錯誤信號和完成信號都代表終止信號檬洞,終止信號用于告訴
訂閱者數(shù)據(jù)流結(jié)束了狸膏,錯誤信號終止數(shù)據(jù)流同時(shí)把錯誤信息傳遞給訂閱者
Flux-Mono
(4)代碼演示 Flux 和 Mono
第一步 引入依賴:<dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> <version>3.1.5.RELEASE</version> </dependency>
第二步 編程代碼:
public static void main(String[] args) { //just 方法直接聲明 Flux.just (1, 2, 3, 4); Mono.just (1); //其他的方法 Integer[] array = {1, 2, 3, 4}; Flux.fromArray (array); List<Integer> list = Arrays.asList (array); Flux.fromIterable (list); Stream<Integer> stream = list.stream ( ); Flux.fromStream (stream); }
(5)三種信號特點(diǎn)
? 錯誤信號和完成信號都是終止信號,不能共存的
? 如果沒有發(fā)送任何元素值添怔,而是直接發(fā)送錯誤或者完成信號湾戳,表示是空數(shù)據(jù)流
? 如果沒有錯誤信號,沒有完成信號广料,表示是無限數(shù)據(jù)流
(6)調(diào)用 just 或者其他方法只是聲明數(shù)據(jù)流砾脑,數(shù)據(jù)流并沒有發(fā)出,只有進(jìn)行訂閱之后才會觸發(fā)數(shù)據(jù)流艾杏,不訂閱什么都不會發(fā)生的
just方法聲明
(7)操作符
? 對數(shù)據(jù)流進(jìn)行一道道操作韧衣,成為操作符,比如工廠流水線
第一 map 元素映射為新元素
Map->流
第二 flatMap 元素映射為流
? 把每個(gè)元素轉(zhuǎn)換流购桑,把轉(zhuǎn)換之后多個(gè)流合并大的流
flatMap->流
4畅铭、SpringWebflux 執(zhí)行流程和核心 API
SpringWebflux 基于 Reactor,默認(rèn)使用容器是 Netty勃蜘,Netty 是高性能的 NIO 框架硕噩,異步非阻塞的框架
(1)Netty
? BIO:
BIO
? NIO:
NIO
(2)SpringWebflux 執(zhí)行過程和 SpringMVC 相似的
? SpringWebflux 核心控制器 DispatchHandler,實(shí)現(xiàn)接口 WebHandler
? 接口 WebHandler 有一個(gè)方法
WebHandler-1WebHandler-2
(3)SpringWebflux 里面 DispatcherHandler缭贡,負(fù)責(zé)請求的處理
? HandlerMapping:請求查詢到處理的方法
? HandlerAdapter:真正負(fù)責(zé)請求處理
? HandlerResultHandler:響應(yīng)結(jié)果處理
(4)SpringWebflux 實(shí)現(xiàn)函數(shù)式編程炉擅,兩個(gè)接口:
RouterFunction(路由處理)
和 HandlerFunction(處理函數(shù))
5、SpringWebflux(基于注解編程模型)
SpringWebflux 實(shí)現(xiàn)方式有兩種:注解編程模型和函數(shù)式編程模型使用注解編程模型方式匀归,和之前SpringMVC 使用相似的坑资,只需要把相關(guān)依賴配置到項(xiàng)目中,SpringBoot 自動配置相關(guān)運(yùn)行容器穆端,默認(rèn)情況下使用 Netty 服務(wù)器
? 第一步 創(chuàng)建 SpringBoot 工程袱贮,引入 Webflux 依賴
創(chuàng)建boot工程
引入依賴
? 第二步 配置啟動端口號
配端口
? 第三步 創(chuàng)建包和相關(guān)類
? ? 實(shí)體類
實(shí)體類
? ? 創(chuàng)建接口定義操作的方法
接口方法
? ? 接口實(shí)現(xiàn)類
public class UserServiceImpl implements UserService { //創(chuàng)建 map 集合存儲數(shù)據(jù) private final Map<Integer, User> users = new HashMap<> ( ); public UserServiceImpl() { this.users.put (1, new User ("lucy", "nan", 20)); this.users.put (2, new User ("mary", "nv", 30)); this.users.put (3, new User ("jack", "nv", 50)); } //根據(jù) id 查詢 @Override public Mono<User> getUserById(int id) { return Mono.justOrEmpty (this.users.get (id)); } //查詢多個(gè)用戶 @Override public Flux<User> getAllUser() { return Flux.fromIterable (this.users.values ( )); } //添加用戶 @Override public Mono<Void> saveUserInfo(Mono<User> userMono) { return userMono.doOnNext (person -> { //向 map 集合里面放值 int id = users.size ( ) + 1; users.put (id, person); }).thenEmpty (Mono.empty ( )); } }
? ? 創(chuàng)建Controller:
Contriller
? ? 說明:
SpringMVC 方式實(shí)現(xiàn),同步阻塞的方式体啰,基于 SpringMVC+Servlet+Tomcat
SpringWebflux 方式實(shí)現(xiàn)攒巍,異步非阻塞 方式,基于 SpringWebflux+Reactor+Netty
6荒勇、SpringWebflux(基于函數(shù)式編程模型)
(1)在使用函數(shù)式編程模型操作時(shí)候柒莉,需要自己初始化服務(wù)器
(2)基于函數(shù)式編程模型時(shí)候,有兩個(gè)核心接口:RouterFunction(實(shí)現(xiàn)路由功能沽翔,請求轉(zhuǎn)發(fā)
給對應(yīng)的 handler)和 HandlerFunction(處理請求生成響應(yīng)的函數(shù))兢孝。核心任務(wù)定義兩個(gè)函數(shù)
式接口的實(shí)現(xiàn)并且啟動需要的服務(wù)器窿凤。
( 3 ) SpringWebflux 請 求 和 響 應(yīng) 不 再 是 ServletRequest 和 ServletResponse ,而是
ServerRequest 和 ServerResponse
第一步 把注解編程模型工程復(fù)制一份 跨蟹,保留 entity 和 service 內(nèi)容
第二步 創(chuàng)建 Handler(具體實(shí)現(xiàn)方法)
暫無--------
第三步 初始化服務(wù)器雳殊,編寫 Router
? 創(chuàng)建路由的方法
? 創(chuàng)建服務(wù)器完成適配
? 最終調(diào)用
(4)使用 WebClient 調(diào)用