【Spring系列】IOC控制反轉
【Spring系列】IOC操作bean管理(一)——bean管理執行個體詳解
【Spring系列】IOC操作bean管理(二)——bean的生命周期、作用域
【Spring系列】IOC操作bean管理(三)——xml自動裝配
【Spring系列】IOC操作bean管理(四)——引入外部屬性檔案
【Spring系列】IOC操作bean管理(五)——bean管理注解
【Spring系列】AOP詳解
【Spring系列】JdbcTemplate操作資料庫詳解
【Spring系列】spring事務配置詳解
【Spring系列】spring5架構新特性
文章目錄
- 一、AOP概念
- 二、AOP底層原理
-
- (一)動态代理
-
- (1)動态代理有兩種情況
-
- 第一種:有接口情況,使用JDK動态代理
- 第二種:沒有接口情況,使用CGLIB動态代理
- (2)AOP使用JDK動态代理
- 三、AOP術語
-
- (1)連接配接點
- (2)切入點
- (3)增強(通知)
- (4)切面
- 四、AOP操作
-
- (1)AspectJ介紹
-
- 附加:切入點表達式
- (2)基于注解方式使用AspectJ的aop操作
-
- 第一步:導入AspectJ相關的jar包
- 第二步:準備工作,建立被增強的類和方法、增強的類和方法
- 第三步:在spring配置檔案中進行aop相關配置
- 第四步:在增強類使用注解實作五種通知
-
- (1)先在增強類和被增強類上添加注解
- (2)五種通知的實作
- (3)xml進行aop細節
-
- 1)抽取切入點表達式
- 2)增強類優先級設定
- 3)完全注解開發
- (4)基于XML方式使用AspectJ的aop操作
一、AOP概念
- 面向切面(/方面)程式設計,利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式可重用性,提高開發效率。
- 通俗來講,就是不通過修改源代碼方式,在主幹功能裡面添加新功能。
二、AOP底層原理
(一)動态代理
(1)動态代理有兩種情況
第一種:有接口情況,使用JDK動态代理
- 建立接口實作類代理對象,增強類的方法
第二種:沒有接口情況,使用CGLIB動态代理
- 建立子類的代理對象,增強類的方法
(2)AOP使用JDK動态代理
- 在java.lang有類 Proxy,這個類裡面有靜态方法
,使用這個方法建立接口實作類代理對象newProxyInstance
- 該方法有3個參數:
ClassLoader loader
:類加載器
Class<?>[] interfaces
:增強方法所在的類,這個類實作的接口,支援多個接口
InvocationHandler h
:實作這個接口InvocationHandler,建立代理對象,寫增強的部分
- 傳回一個指定接口的代理類執行個體,該接口可以将方法調用指派到指定的調用處理程式。
- 下面是一個jdk動态代理的執行個體:
接口:
package com.springlearn.proxy;
public interface UserDao {
public void add(int a,int b);
}
實作類:
package com.springlearn.proxy;
public class UserDaoImpl implements UserDao {
@Override
public void add(int a, int b) {
int sum=a+b;
System.out.println("sum:"+sum);
}
}
代理類:
實作了在不改變源代碼的情況下,增強了實作類裡的方法,在方法前後分别進行操作。
package com.springlearn.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class jdbcProxy {
public static void main(String[] args) {
UserDao userDaoImpl = new UserDaoImpl();
//使用jdbc動态代理方式建立UserDao接口實作類代理對象
//有三個參數:
//第一個參數:類加載器
//第二個參數:接口class類型,數組形式
//第三個參數:InvocationHandler是接口,在接口裡面invoke寫增強部分(匿名内部類)
Class<?>[] interfaces = {UserDao.class};
UserDao userDao = (UserDao) Proxy.newProxyInstance(jdbcProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
//增強的部分
/*
* proxy:代理對象
* method:被增強的方法
* args:方法裡面參數
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前輸出
System.out.println("方法準備執行.....");
//讓被增強的方法執行 add執行
//Method代表類裡面的方法,Method類有invoke,讓類的方法執行(和上面的invoke不同)
Object result = method.invoke(userDaoImpl, args);
//方法之後輸出
System.out.println("方法執行完成了.....");
return result;
}
});
//調用測試
userDao.add(1,2);
}
}
輸出結果:
三、AOP術語
(1)連接配接點
- 類裡面哪些方法可以被增強,這些方法稱為連接配接點
(2)切入點
- 實際增強的類裡面的方法,稱為切入點
(3)增強(通知)
- 實際增強的邏輯部分(新添加功能部分)稱為增強
- 通知有五種類型
前置通知:在方法之前執行
後置(傳回)通知:在方法之後執行
環繞通知:在方法前後都執行
異常通知:在發生異常時執行
最終(後置)通知:在最後執行,有沒有異常都會執行
(4)切面
- 切面是具體操作或動作
- 是把增強作用到方法(切入點)的過程
四、AOP操作
- Spring架構一般都是基于AspectJ實作AOP操作
(1)AspectJ介紹
(1)spring進行aop操作,一般依賴第三方元件實作 AspectJ;
(2)AspectJ本身是AOP架構,不是spring的組成部分,可以單獨使用,一般經常與spring一起使用,進行aop操作;
(3)spring基于AspectJ實作AOP有兩種方式:
第一種 基于注解方式實作
第二種 基于xml配置檔案方式實作
附加:切入點表達式
(1)切入點表達式作用:知道對哪個類裡面的哪個方法進行增強
(2)文法結構:
*
:代表所有的權限修飾/所有的類/所有的方法
傳回類型
:如果是void可以省略
..
:代表方法中的所有參數
舉例1:對com.atguigu.dao.BookDao類裡面的add進行增強
舉例2:對com.atguigu.dao.BookDao類裡面的所有的方法進行增強
舉例3:對com.atguigu.dao包裡面所有類,類裡面所有方法進行增強
(2)基于注解方式使用AspectJ的aop操作
第一步:導入AspectJ相關的jar包
-
spring-aop-5.2.6.RELEASE.jar
- AspectJ相關的三個jar包
第二步:準備工作,建立被增強的類和方法、增強的類和方法
被增強的類和方法:
package com.springlearn.aop;
/**
* @program: spring03
* @description: 被增強的類
* @author: txg
* @create: 2021-08-26 11:52
**/
public class User {
public void add(){
System.out.println("add方法執行了……");
}
}
增強的類和方法:
package com.springlearn.aop;
/**
* @program: spring03
* @description: 增強的類
* @author: txg
* @create: 2021-08-26 11:53
**/
public class UserProxy {
//增強的邏輯部分
}
第三步:在spring配置檔案中進行aop相關配置
<?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:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.springlearn"></context:component-scan>
<!--開啟Aspect生成代理對象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
第四步:在增強類使用注解實作五種通知
(1)先在增強類和被增強類上添加注解
增強類:
package com.springlearn.aop;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
}
被增強類:
import org.springframework.stereotype.Component;
@Component
public class User {
public void add(){
System.out.println("add方法執行了……");
}
}
(2)五種通知的實作
package com.springlearn.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
//前置通知
@Before(value = "execution(* com.springlearn.aop.User.add(..))")
public void before(){
System.out.println("前置通知……");
}
//後置通知(傳回通知)
@AfterReturning(value = "execution(* com.springlearn.aop.User.add(..))")
public void afterReturning() {
System.out.println("後置通知.............");
}
//最終通知
//總會被執行
@After(value = "execution(* com.springlearn.aop.User.add(..))")
public void after() {
System.out.println("最終通知.............");
}
//異常通知
//被增強的方法出現異常,這個通知執行
@AfterThrowing(value = "execution(* com.springlearn.aop.User.add(..))")
public void afterThrowing() {
System.out.println("異常通知...............");
}
//環繞通知
@Around(value = "execution(* com.springlearn.aop.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//add方法之前執行
System.out.println("環繞通知——add方法之前執行……");
//add方法執行
proceedingJoinPoint.proceed();
//add方法之後執行
System.out.println("環繞通知——add方法之後執行……");
}
}
結果(無異常):
結果(有異常):
注意: 這五種通知的執行順序不是絕對不變的,是相對不變的,前置肯定在前面後置肯定在後面,但其他的不一定一直不變。
(3)xml進行aop細節
1)抽取切入點表達式
- 我們發現,上面的通知注解,每次使用都要寫一遍value的值,也就是包名+方法名;
- 是以我們可以将這個切入點表達式抽取出來,這樣每次隻引入抽取的這個方法名就可以。
//抽取切入點表達式
@Pointcut(value = "execution(* com.springlearn.aop.User.add(..))")
public void pointdemo() {
}
//前置通知
//使用方法引用就可以
@Before(value = "pointdemo()")
public void before() {
System.out.println("前置通知............");
}
2)增強類優先級設定
- 有多個增強類多同一個方法進行增強,設定增強類優先級
- 在增強類上面添加注解
,數字類型值越小優先級越高@Order(數字類型值)
@Component
@Aspect
@Order(1)
public class PersonProxy
3)完全注解開發
無需使用xml檔案配置,添加一個配置類
配置類:
@Configuration
@ComponentScan(basePackages = {"com.springlearn"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}
(4)基于XML方式使用AspectJ的aop操作
被增強方法:
package com.springlearn.aop;
public class Book {
public void buy(){
System.out.println("bug…………");
}
}
增強方法:
package com.springlearn.aop;
public class BookProxy {
public void before(){
System.out.println("前置通知…………");
}
}
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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.springlearn"></context:component-scan>
<!--開啟Aspect生成代理對象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean id="book" class="com.springlearn.aop.Book"></bean>
<bean id="bookProxy" class="com.springlearn.aop.BookProxy"></bean>
<!--配置aop增強-->
<aop:config>
<aop:pointcut id="p" expression="execution(* com.springlearn.aop.Book.buy(..))"/>
<!--配置切面-->
<aop:aspect ref="bookProxy">
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>
</beans>
aop部分就介紹到這裡啦,你學廢了嘛?
感謝閱讀 ♪(・ω・)ノ