慕課網《探秘Spring AOP》學習總結
慕課網《探秘Spring AOP》學習總結
時間:2017年09月03日星期日
說明:本文部分內容均來自慕課網。@慕課網:
教學原始碼:
學習原始碼:
課程章節
概覽
AOP使用
AOP原理
AOP開源運用
課程實戰
課程總結
面向切面程式設計是一種程式設計正規化
程式設計正規化概覽
程式導向程式設計
物件導向程式設計
面向函式程式設計(函數語言程式設計)
事件驅動程式設計(GUI開發中比較常見)
面向切面程式設計
AOP是什麼
是一種程式設計正規化,不是程式語言
解決特定問題,不能解決所有問題
是OOP的補充,不是替代
AOP的初衷
DRY:Don’t Repeat Yourself程式碼重複性問題
SOC:Separation of Concerns關注點分離
-水平分離:展示層->服務層->持久層
-垂直分離:模組劃分(訂單、庫存等)
-切面分離:分離功能性需求與非功能性需求
使用AOP的好處
集中處理某一關注點/橫切邏輯
可以很方便地新增/刪除關注點
侵入性少,增強程式碼可讀性及可維護性
AOP的應用場景
許可權控制
快取控制
事務控制
審計日誌
效能監控
分散式追蹤
異常處理
支援AOP的程式語言
Java
.NET
C/C++
Ruby
Python
PHP
…
案例背景
產品管理的服務
產品新增、刪除的操作只能管理員才能進行
普通實現VS AOP實現
建立一個名為springaopguide的maven專案pom如下
4.0.0 com.myimooc springaopguide 0.0.1-SNAPSHOT jar springaop org.springframework.boot spring-boot-starter-parent 1.5.1.RELEASE <!-- lookup parent from repository -->UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin
完成後的專案結構如下
程式碼編寫
1.編寫Product類
package com.myimooc.springaopguide.domain;
/**
* @title 產品領域模型
* @describe 產品實體物件
* @author zc
* @version 1.0 2017-09-03
*/
public class Product {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.編寫CurrentUserHolder類
package com.myimooc.springaopguide.security;
/**
* @title 獲取使用者資訊
* @describe 模擬使用者的切換,將使用者資訊存入當前執行緒
* @author zc
* @version 1.0 2017-09-03
*/
public class CurrentUserHolder {
private static final ThreadLocal holder = new ThreadLocal();
public static String get(){
return holder.get() == null ? "unkown" : holder.get();
}
public static void set(String user){
holder.set(user);
}
}
3.編寫AdminOnly類
package com.myimooc.springaopguide.security;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @title 管理員許可權註解
* @describe 被該註解宣告的方法需要管理員許可權
* @author zc
* @version 1.0 2017-09-03
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AdminOnly {
}
4.編寫SecurityAspect類
package com.myimooc.springaopguide.security;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.myimooc.springaopguide.service.AuthService;
/**
* @title 許可權校驗切面類
* @describe
* @author zc
* @version 1.0 2017-09-03
*/
// 宣告為一個切面
@Aspect
@Component
public class SecurityAspect {
@Autowired
private AuthService authService;
// 使用要攔截標註有AdminOnly的註解進行操作
@Pointcut("@annotation(AdminOnly)")
public void adminOnly(){
}
@Before("adminOnly()")
public void check(){
authService.checkAccess();
}
}
5.編寫AuthService類
package com.myimooc.springaopguide.service;
import java.util.Objects;
import org.springframework.stereotype.Service;
import com.myimooc.springaopguide.security.CurrentUserHolder;
/**
* @title 許可權校驗類
* @describe 對使用者許可權進行校驗
* @author zc
* @version 1.0 2017-09-03
*/
@Service
public class AuthService {
public void checkAccess(){
String user = CurrentUserHolder.get();
if(!Objects.equals("admin", user)){
throw new RuntimeException("operation not allow");
}
}
}
6.編寫ProductService類
package com.myimooc.springaopguide.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.myimooc.springaopguide.domain.Product;
/**
* @title 產品服務類
* @describe 產品相關業務服務-傳統方式實現許可權校驗
* @author zc
* @version 1.0 2017-09-03
*/
@Service
public class ProductService {
@Autowired
private AuthService AuthService;
public void insert(Product product){
AuthService.checkAccess();
System.out.println("insert product");
}
public void delete(Long id){
AuthService.checkAccess();
System.out.println("delete product");
}
}
7.編寫ProductServiceAop類
package com.myimooc.springaopguide.service;
import org.springframework.stereotype.Service;
import com.myimooc.springaopguide.domain.Product;
import com.myimooc.springaopguide.security.AdminOnly;
/**
* @title 產品服務類
* @describe 產品相關業務服務-AOP方式實現許可權校驗
* @author zc
* @version 1.0 2017-09-03
*/
@Service
public class ProductServiceAop {
@AdminOnly
public void insert(Product product){
System.out.println("insert product");
}
@AdminOnly
public void delete(Long id){
System.out.println("delete product");
}
}
8.編寫AopGuideApplicationTests類
package com.myimooc.springaopguide;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.myimooc.springaopguide.security.CurrentUserHolder;
import com.myimooc.springaopguide.service.ProductService;
import com.myimooc.springaopguide.service.ProductServiceAop;
/**
* @title 單元測試類
* @describe 測試許可權校驗服務是否生效
* @author zc
* @version 1.0 2017-09-03
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class AopGuideApplicationTests {
@Autowired
private ProductService productService;
@Test(expected = Exception.class)
public void annoInsertTest(){
CurrentUserHolder.set("tom");
productService.delete(1L);
}
@Test
public void adminInsertTest(){
CurrentUserHolder.set("admin");
productService.delete(1L);
}
@Autowired
private ProductServiceAop productServiceAop;
@Test(expected = Exception.class)
public void annoInsertAopTest(){
CurrentUserHolder.set("tom");
productServiceAop.delete(1L);
}
@Test
public void adminInsertAopTest(){
CurrentUserHolder.set("admin");
productServiceAop.delete(1L);
}
}
第二章:使用詳解 Spring AOP使用方式
XML配置+Pointcut expression【不推薦使用方式】
註解方式+ Pointcut expression【推薦使用該方式】
Aspectj註解
@Aspect:用於宣告當前類是一個切面
@Pointcut:用於描述在哪些類、哪些方法上執行切面的程式碼
Advice:描述想要在這些方法執行的什麼時機進行攔截
本章內容
Pointcut express:切面表示式
5種Advice:建言的五種細分怎麼使用
切面表示式
1.designators(指示器)
execution()
描述透過什麼樣的方式去匹配哪些類、哪些方法
2.wildcards(萬用字元)
* .. +
使用萬用字元進行描述
3.operators(運算子)
&& || !
使用運算子進行多條件的判斷
Designators(指示器)
匹配方法 execution()
匹配註解 @target() @args() @within() @annotation()
匹配包/型別 @within()
匹配物件 this() bean() target()
匹配引數 args()
Wildcards(萬用字元)
* 匹配任意數量的字元
+ 匹配指定類及其子類
.. 一般用於匹配任意引數的子包或引數
Operators(運算子)
&& 與運算子
|| 或運算子
! 非運算子
// 匹配 ProductServiceAop 類裡面的所有方法
@Pointcut("within(com.myimooc.springaopguide.service.ProductServiceAop)")
public void matchType(){}
// 匹配 com.myimooc.springaopguide.service 包及子包下所有類的方法
@Pointcut("within(com.myimooc.springaopguide.service..*)")
public void matchPackage(){}
// 匹配AOP物件的目標物件為指定型別的方法,即DemoDao的aop代理物件的方法
@Pointcut("this(com.myimooc.springaopguide.dao.DemoDao)")
public void testDemo(){}
// 匹配實現IDao介面的目標物件(而不是aop代理後的物件)的方法,這裡即DemoDao的方法
@Pointcut("target(com.myimooc.springaopguide.dao.IDao)")
public void targetDemo(){}
// 匹配所有以Service結尾的bean裡面的方法
@Pointcut("bean(*Service)")
public void beanDemo(){}
// 匹配任何以find開頭而且只有一個Long引數的方法
@Pointcut("execution(* *..find*(Long))")
public void argsDemo1(){}
// 匹配任何只有一個Long引數的方法
@Pointcut("args(Long)")
public void argsDemo2(){}
// 匹配任何以find開頭而且第一個引數為Long型的方法
@Pointcut("execution(* *..find*(Long,..))")
public void argsDemo3(){}
// 匹配第一個引數為Long型的方法
@Pointcut("args(Long,..))")
public void argsDemo4(){}
// 匹配方法標註有AdminOnly的註解的方法
@Pointcut("@annotation(com.myimooc.springaopguide.security.AdminOnly)")
public void annoDemo(){}
// 匹配標註有Beta的類底下的方法,要求的annotation的RetentionPolicy級別為CLASS
@Pointcut("@within(com.google.common.annotations.Beta)")
public void annoWithDemo(){}
// 匹配標註有Repository的類底下的方法,要求的RetentionPolicy級別為RUNTIME
@Pointcut("@target(org.springframework.stereotype.Repository)")
public void annoTargetDemo(){}
// 匹配傳入的引數類標註有Repository註解的方法
@Pointcut("@args(org.springframework.stereotype.Repository)")
public void annoArgsDemo(){}
execution()格式
execution(
modifier-pattern? // 修飾符匹配
ret-type-pattern // 返回值匹配
declaring-type-pattern? // 描述值包名
name-pattern(param-pattern) // 方法名匹配(引數匹配)
throws-pattern?// 丟擲異常匹配
)
execution()例項
// 匹配 使用public修飾符 任意返回值 在com.myimooc.springaopguide.service包及子下
// 以Service結尾的類 任意方法(任意引數)
@Pointcut("execution(public * com.myimooc.springaopguide.service..*Service.*(..))")
public void matchCondition(){}
5中Advice(建言)註解
@Before,前置通知
@After(finally),後置通知,方法執行完之後
@AfterReturning,返回通知,成功執行之後
@AfterThrowing,異常通知,丟擲異常之後
@Around,環繞通知
5中Advice(建言)例項
// 定義切點,攔截使用NeedSecured註解修飾的方法
@Pointcut("@within(com.myimooc.demo.security.NeedSecured)")
public void annoTargetVsWithinDemo(){}
// 使用NeedSecured註解修飾 且 在com.myimooc包下的方法
@Before("annoTargetVsWithinDemo() && within(com.myimooc..*)")
public void beforeDemo(){
System.out.println("被攔截方法執行之前執行");
}
@After("annoTargetVsWithinDemo() && within(com.myimooc..*)")
public void afterDemo(){
System.out.println("被攔截方法執行之後執行");
}
@AfterReturning("annoTargetVsWithinDemo() && within(com.myimooc..*)")
public void afterReturning(){
System.out.println("程式碼成功之後執行");
}
@AfterThrowing("annoTargetVsWithinDemo() && within(com.myimooc..*)")
public void afterThrowing(){
System.out.println("程式碼執行丟擲異常之後執行");
}
@Around("annoTargetVsWithinDemo() && within(com.myimooc..*)")
public Object aroundDemo(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("相當於@Before");
try{
Object result = pjp.proceed(pjp.getArgs());
System.out.println("相當於@AfterReturning");
return result;
}catch (Throwable throwable) {
System.out.println("相當於@AfterThrowing");
throw throwable;
}finally {
System.out.println("相當於@After");
}
}
Advice中的引數及結果繫結
@Before("annoTargetVsWithinDemo() && within(com.myimooc..*) && args(userId)")
public void beforeWithArgs(JoinPoint joinPoint,Long userId){
System.out.println("被攔截方法執行之前執行,args:"+userId);
}
@AfterReturning(value="annoTargetVsWithinDemo() && within(com.myimooc..*)",returning="returnValue")
public void getResult(Object returnValue){
if(returnValue != null){
System.out.println("程式碼成功之後執行,result:"+returnValue);
}
}
上節回顧
Pointcut expression的組成部分
各種designators的區別
5中advice及引數、結果繫結
實現原理
概述
設計:代理模式、責任鏈模式
實現:JDK實現、cglib實現
原理概述:植入的時機
1.編譯期(AspectJ)
2.類載入時(Aspectj 5+)
3.執行時(Spring AOP)【本節課講解內容】
執行時植入
執行時植入是怎麼實現的
從靜態代理到動態代理
基於介面代理與基於繼承代理
代理AOP物件
Caller:呼叫方
Proxy:AOP代理物件
Target:目標物件
代理模式類圖
客戶端透過介面來引用目標物件
代理物件把真正的方法委託目標物件來執行,自己執行額外的邏輯
程式碼編寫
1.編寫Subject類
2.編寫RealSubject類
3.編寫Proxy類
4.編寫Client類
受篇幅限制,原始碼請到我的github地址檢視
靜態代理與動態代理
靜態代理的缺點:每當需要代理的方法越多的時候,重複的邏輯就越多
動態代理的兩類實現:基於介面代理與基於繼承代理
兩類實現的代表技術:JDK代理與Cglib代理
JDK實現要點
類:java.lang.reflect.Proxy
介面:InvocationHandler
只能基於介面進行動態代理
程式碼編寫
1.編寫JdkSubject類
2.編寫Client類
受篇幅限制,原始碼請到我的github地址檢視
JDK代理原始碼解析
Proxy.newProxyInstance(首先,呼叫該方法)
getProxyClass0、ProxyClassFactory、ProxyGenerator(然後,分別呼叫方法,生成位元組碼)
newInstance(最後,利用反射根據位元組碼生成例項)
程式碼編寫
1.編寫DemoMethodInterceptor類
2.編寫Client類
受篇幅限制,原始碼請到我的github地址檢視
JDK與Cglib代理對比
JDK只能針對有介面的類的介面方法進行動態代理
Cglib基於繼承來實現代理,無法對static、final類進行代理
Cglib基於繼承來實現代理,無法對private、static方法進行代理
Spring建立代理bean時序圖
SpringAOP對兩種實現的選擇
如果目標物件實現了介面,則預設採用JDK動態代理
如果目標物件沒有實現介面,則採用Cglib進行動態代理
如果目標物件實現了介面,但設定強制cglib代理,則使用cglib代理
在SpringBoot中,透過@EnableAspectJAutoProxy(proxyTargetClass=true)設定
當多個AOP作用到同一個目標物件時,採用責任鏈模式
責任鏈模式類圖
程式碼編寫
1.編寫Handler類
2.編寫Client類
3.編寫Chain類
4.編寫ChainHandler類
5.編寫ChainClient類
受篇幅限制,原始碼請到我的github地址檢視
上節回顧
靜態代理與動態代理
JDK代理與Cglib代理區別及侷限
代理模式與責任鏈模式
Spring AOP在開源專案裡面的應用:三個例子
事務:@Transactional:Spring如何利用Transaction進行事務控制
安全:@PreAuthorize:Spring Security如何利用PreAuthorize進行安全控制
快取:@Cacheable:Spring Cache如何利用Cacheable進行快取控制
透過案例來講解,原始碼可到我的github地址檢視
實戰案例背景
商家產品管理系統
記錄產品修改的操作記錄
什麼人在什麼時間修改了哪些產品的哪些欄位修改為什麼值
實現思路
利用aspect去攔截增刪改方法
利用反射獲取物件的新舊值
利用@Around的advice去記錄操作記錄
建立名為mydatalog的maven專案pom如下
4.0.0 com.myimooc mydatalog 0.0.1-SNAPSHOT jar mydatalog org.springframework.boot spring-boot-starter-parent 1.5.2.RELEASE <!-- lookup parent from repository -->UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-data-jpa <!--
org.springframework.boot
spring-boot-starter-data-mongodb
-->mysql mysql-connector-java org.springframework.boot spring-boot-starter-test test commons-beanutils commons-beanutils com.alibaba fastjson 1.2.36
完成後的專案結構圖如下
受篇幅限制,原始碼請到我的github地址檢視
第六章:課程總結要點清單
AOP的適用範圍及優劣勢
AOP的概念及Spring切面表示式
AOP的實現原理及運用
使用SpringAOP的注意事項
不宜把重要的業務邏輯放到AOP中處理
無法攔截static、final、private方法
無法攔截內部方法呼叫
課程小結
合理利用面向切面程式設計提高程式碼質量
掌握SpringAOP概念及實現原理
瞭解AOP的優缺點及SpringAOP的使用侷限
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1817/viewspace-2808096/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- WebView學習的總結————慕課網WebView
- 慕課網《Netty入門之WebSocket初體驗》學習總結NettyWeb
- 慕課網_《iOS基礎教程之介面初體驗》學習總結iOS
- 慕課網-LinuxC語言結構體-學習筆記LinuxC語言結構體筆記
- Spring AOP總結Spring
- 慕課網的中國式線上“慕課”模式模式
- thinkphp開發 網易雲課堂-線上IT學習|視訊教程|慕課網PHP
- 慕課網Python入門練習題---Python
- 慕課網玩轉資料結構課程之陣列資料結構陣列
- 神經網路與深度學習 課程複習總結神經網路深度學習
- (七) Spring學習總結Spring
- Spring AOP學習筆記01:AOP概述Spring筆記
- 《文獻管理與資訊分析》慕課學習心得
- JavaScript(轉載自 計科學院 慕課網)JavaScript
- 慕課網《前端JavaScript面試技巧》學習筆記(2)-原型和原型鏈前端JavaScript面試筆記原型
- spring:spring再總結(ioc、aop、DI等)Spring
- Spring Boot + JPA學習總結Spring Boot
- Spring Cloud學習總結(一)SpringCloud
- 慕課網招聘golang講師Golang
- Spring AOP學習筆記02:如何開啟AOPSpring筆記
- Spring-boot整合AOP及AOP相關學習Springboot
- ❀ Spring5學習大總結Spring
- Spring AOP學習筆記05:AOP失效的罪因Spring筆記
- UFLDL:史丹佛大學深度學習課程總結深度學習
- 死磕Spring之AOP篇 - Spring AOP總覽Spring
- Spring學習總結(24)——Spring配置檔案載入路徑總結Spring
- 慕課平臺
- 深入學習Spring框架(三)- AOP面向切面Spring框架
- Spring學習筆記五: AOP入門Spring筆記
- Java課後習題總結Java
- 慕課網大資料開發工程師課程大資料工程師
- 慕課網go語言體系課搶先體驗Go
- 深度學習DeepLearning.ai系列課程學習總結:7. 深層神經網路理論學習深度學習AI神經網路
- SSM框架學習之Spring的AOP學習以及資料整理SSM框架Spring
- 網路、HTTP相關學習總結HTTP
- 初入探秘:大型網站架構設計與方法總結網站架構
- 學習總結
- Spring之Aop練習Spring