Spring-AOP
1、AOP 基本概念
(1)面向切面編程(方面),利用 AOP 可以對業務邏輯的各個部分進行隔離,從而使得 業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
(2)通俗描述:不通過修改源代碼方式,在主幹功能裏面添加新功能
(3)使用登錄例子說明 AOP
2、AOP(底層原理)
a)AOP 底層使用動態代理 ,動態代理有兩種情況:
第一種 有接口情況,使用 JDK 動態代理 ;創建接口實現類代理對象,增強類的方法
第二種 沒有接口情況,使用 CGLIB 動態代理;創建子類的代理對象,增強類的方法
3、AOP(JDK 動態代理)
1)使用 JDK 動態代理,使用 Proxy 類裏面的方法創建代理對象
調用 newProxyInstance 方法,方法有三個參數:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
第一參數,類加載器
第二參數,增強方法所在的類,這個類實現的接口,支持多個接口
第三參數,實現這個接口 InvocationHandler,創建代理對象,寫增強的部分
2)編寫 JDK 動態代理代碼
//(1)創建接口,定義方法
public interface UserDao {
public int add(int a,int b);
public String update(String id);
}
//(2)創建接口實現類,實現方法
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 類創建接口代理對象
public class JDKProxy {
public static void main(String[] args) {
//創建接口實現類代理對象
Class[] interfaces = {UserDao.class};
UserDaoImpl userDao = new UserDaoImpl();
/** 第一參數,類加載器
第二參數,增強方法所在的類,這個類實現的接口,(支持多個接口)
第三參數,實現這個接口 InvocationHandler,創建代理對象,寫增強的部分 */
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;
}
}
4、AOP(術語)
a)連接點:類裏面哪些方法可以被增強,這些方法稱爲連接點
b)切入點:實際被真正增強的方法稱爲切入點
c)通知(增強):實際增強的邏輯部分稱爲通知,且分爲以下五種類型:
1)前置通知 2)後置通知 3)環繞通知 4)異常通知 5)最終通知
d)切面:把通知應用到切入點過程
5、AOP操作
a)Spring 框架一般都是基於 AspectJ 實現 AOP 操作,AspectJ 不是 Spring 組成部分,獨立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,進行 AOP 操作
b)基於 AspectJ 實現 AOP 操作:1)基於 xml 配置文件實現 (2)基於註解方式實現(使用)
c)引入相關jar包
d)切入點表達式,如下:
(1)切入點表達式作用:知道對哪個類裏面的哪個方法進行增強
(2)語法結構: execution([權限修飾符] [返回類型] [類全路徑] [方法名稱]([參數列表]) )
(3)例子如下:
例 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.*.* (..))
6、AOP 操作(AspectJ 註解)
//1、創建類,在類裏面定義方法
public class User {
public void add() {
System.out.println("add.......");
}
}
//2、創建增強類(編寫增強邏輯)
//(1)在增強類裏面,創建方法,讓不同方法代表不同通知類型
//增強的類
public class UserProxy {
public void before() {//前置通知
System.out.println("before......");
}
}
<!--3、進行通知的配置-->
<?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.atguigu.spring5.aopanno"></context:component-scan>
<!-- 開啓Aspect生成代理對象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
//增強的類
@Component
@Aspect //生成代理對象
public class UserProxy {}
//被增強的類
@Component
public class User {}
//4、配置不同類型的通知
@Component
@Aspect //生成代理對象
public class UserProxy {
//相同切入點抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before註解表示作爲前置通知
@Before(value = "pointdemo()")//相同切入點抽取使用!
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("環繞之後.........");
}
}
7、有多個增強類對同一個方法進行增強,設置增強類優先級
//(1)在增強類上面添加註解 @Order(數字類型值),數字類型值越小優先級越高
@Component
@Aspect
@Order(1)
public class PersonProxy{ }
8、AOP 操作(AspectJ 配置文件)
<!--1、創建兩個類,增強類和被增強類,創建方法(同上一樣)-->
<!--2、在 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>