【Spring】面向切面程式設計AOP
阿新 • • 發佈:2020-08-07
AOP(概念)
- 什麼是 AOP
- 面向切面程式設計(方面),利用 AOP 可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
- 通俗描述:不通過修改原始碼方式,在主幹功能裡面新增新功能
- 使用登入例子說明 AOP
AOP(底層原理)
- AOP 底層使用動態代理
有兩種情況動態代理第一種 有介面情況,使用 JDK 動態代理
建立介面實現類代理物件,增強類的方法
第二種 沒有介面情況,使用 CGLIB 動態代理
建立子類的代理物件,增強類的方法
AOP(JDK 動態代理)
- 使用 JDK 動態代理,使用 Proxy 類裡面的方法建立代理物件
- 呼叫 newProxyInstance 方法
方法有三個引數:- 第一引數,類載入器
- 第二引數,增強方法所在的類,這個類實現的介面,支援多個介面
- 第三引數,實現這個介面 InvocationHandler,建立代理物件,寫增強的部分
- 呼叫 newProxyInstance 方法
- 編寫 JDK 動態代理程式碼
建立介面,定義方法
public interface UserDao { public int add(int a,int b); public String update(String id); }
建立介面實現類,實現方法
public class UserDaoImpl implements UserDao { @Override public int add(int a, int b) { return a+b; } @Override public String update(String id) { return id; } }
使用 Proxy 類建立介面代理物件
public class JDKProxy { public static void main(String[] args) { //建立介面實現類代理物件 Class[] interfaces = {UserDao.class}; // 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); } }
//建立代理物件程式碼 class UserDaoProxy implements InvocationHandler { //1 把建立的是誰的代理物件,把誰傳遞過來 //有引數構造傳遞 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("方法之前執行...."+method.getName()+" :傳遞的引數..."+ Arrays.toString(args)); //被增強的方法執行 Object res = method.invoke(obj, args); //方法之後 System.out.println("方法之後執行...."+obj); return res; } }
AOP(術語)
- 連線點:類裡面哪些方法可以被增強,這些方法稱為連線點
- 切入點:實際被真正增強的方法,稱為切入點
- 通知(增強):
- 實際增強的邏輯部分稱為通知(增強)
- 通知有多種型別
- 前置通知
- 後置通知
- 環繞通知
- 異常通知
- 最終通知
- 切面:是動作,把通知應用到切入點的過程
AOP 操作(準備工作)
- Spring 框架一般都是基於 AspectJ 實現 AOP 操作
- AspectJ 不是 Spring 組成部分,獨立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,進行 AOP 操作
- 基於 AspectJ 實現 AOP 操作
- 基於 xml 配置檔案實現
- 基於註解方式實現(使用)
- 在專案工程裡面引入 AOP 相關依賴
- 切入點表示式
- 切入點表示式作用:知道對哪個類裡面的哪個方法進行增強
語法結構: 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.. (..))
AOP 操作(AspectJ 註解)
建立類,在類裡面定義方法
public class User { public void add() { System.out.println("add......."); } }
- 建立增強類(編寫增強邏輯)
在增強類裡面,建立方法,讓不同方法代表不同通知型別
//增強的類 public class UserProxy { public void before() {//前置通知 System.out.println("before......"); } }
- 進行通知的配置
在 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 basepackage="com.atguigu.spring5.aopanno"></context:component-scan>
- 使用註解建立 User 和 UserProxy 物件
在增強類上面添加註解 @Aspect
//增強的類 @Component @Aspect //生成代理物件 public class UserProxy {
在 spring 配置檔案中開啟生成代理物件
<!-- 開啟 Aspect 生成代理物件--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 配置不同型別的通知
在增強類的裡面,在作為通知方法上面新增通知型別註解,使用切入點表示式配置
//增強的類 @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.atguigu.spring5.aopanno.User.add(..))") public void afterReturning() { System.out.println("afterReturning........."); } //最終通知 @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void after() { System.out.println("after........."); } //異常通知 @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 proceedingJoinPoint) throws Throwable { System.out.println("環繞之前........."); //被增強的方法執行 proceedingJoinPoint.proceed(); System.out.println("環繞之後........."); } }
相同的切入點抽取
//相同切入點抽取 @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void pointdemo() { } //前置通知 //@Before 註解表示作為前置通知 @Before(value = "pointdemo()") public void before() { System.out.println("before........."); }
- 有多個增強類多同一個方法進行增強,設定增強類優先順序
在增強類上面添加註解 @Order(數字型別值),數字型別值越小優先順序越高
@Component @Aspect @Order(1) public class PersonProxy
- 完全使用註解開發
建立配置類,不需要建立 xml 配置檔案
@Configuration @ComponentScan(basePackages = {"com.atguigu"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class ConfigAop { }
AOP 操作(AspectJ 配置檔案)
- 建立兩個類,增強類和被增強類,建立方法
在 spring 配置檔案中建立兩個類物件
<!--建立物件--> <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
3、在 spring 配置檔案中配置切入點
<!--配置 aop 增強--> <aop:config> <!--切入點--> <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/> <!--配置切面--> <aop:aspect ref="bookProxy"> <!--增強作用在具體的方法上--> <aop:before method="before" pointcut-ref="p"/> </aop:aspect> </aop:config>