Spring(三)——AOP
概念
什么是AOP
(1)面向切面編程(方面),利用 AOP 可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率,
(2)通俗描述:不通過修改源代碼方式,在主干功能里面添加新功能
AOP底層原理
- JDK動態代理(有介面情況):創建介面實作類代理物件,增強類的方法
- CGLIB動態代理(無介面情況):創建子類的代理物件,增強類的方法
JDK動態代理
1.使用 JDK 動態代理,使用 Proxy 類里面的方法創建代理物件
呼叫newProxyInstance 方法
- 第一引數,類加載器
- 第二引數,增強方法所在的類,這個類實作的介面,支持多個介面
- 第三引數,實作這個介面 InvocationHandler,創建代理物件,寫增強的部分
2.撰寫JDK動態代理代碼
(1)創建介面,定義方法
package com.atguigu.spring5;
public interface UserDao {
public int add(int a,int b);
public String update(String id);
}
(2)創建介面實作類,實作方法
package com.atguigu.spring5;
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 類創建介面代理物件
package com.atguigu.spring5;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
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;
// }
// });
UserDao userDao=new UserDaoImpl();
UserDao dao=(UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
int res=dao.add(1,2);
System.out.println(res);
}
}
//創建代理物件代碼
class UserDaoProxy implements InvocationHandler{
//創建誰的代理物件就把誰傳進來
//有參構造傳遞
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術語
1.連接點
類中哪些方法可以被增強,這些方法被稱為連接點
2.切入點
實際被增強的方法叫切入點
3.通知(增強)
被增強的邏輯部分叫通知,下面是5種通知型別
- 前置通知
- 后置通知(回傳通知)
- 環繞通知
- 例外通知
- 最終通知
4.切面
是一個動作,把通知應用到切入點的程序
AOP操作-準備作業
Spring框架一般基于AspectJ實作AOP操作
AspectJ 不是 Spring 組成部分,獨立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,進行 AOP 操作
基于AspectJ實作AOP操作
(1)基于 xml 組態檔實作
(2)基于注解方式實作(使用)
引入AOP相關依賴
spring-aop-5.3.22.jar
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.7.2.RELEASE.jar
切入點運算式
作用:知道對哪個類里面的哪個方法進行增強
語法結構: execution([權限修飾符] [回傳型別] [類全路徑] [方法名稱] ([引數串列]) )
舉例1:對 com.atguigu.dao.BookDao 類里面的 add 進行增強
//*表示任意修飾符public、private......(*后有空格)
//..表示任意入參的方法
execution(* com.atguigu.dao.BookDao.add(..))
舉例2:對 com.atguigu.dao.BookDao 類里面的所有的方法進行增強
execution(* com.atguigu.dao.BookDao.* (..))
舉例 3:對 com.atguigu.dao 包里面所有類,類里面所有方法進行增強
execution(* com.atguigu.dao.*.* (..))
AspectJ注解操作
1.創建類,在類里定義方法
2.創建增強類,撰寫增強邏輯
在增強類里面,創建方法,讓不同方法代表不同通知型別
3.進行通知配置
(1)在 spring 組態檔中引入名稱空間,開啟注解掃描
(2)使用注解創建 User 和 UserProxy 物件
(3)在增強類上面添加注解 @Aspect,生成代理物件
(4)在 spring 組態檔中開啟生成代理物件
4.配置不同型別的通知
在增強類的里面,在作為通知方法上面添加通知型別注解,使用切入點運算式配置
User.java
package com.atguigu.spring5.aopanno;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
public class User {
public void add(){
System.out.println("User add..........");
}
}
UserProxy.java
package com.atguigu.spring5.aopanno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//增強類
@Component
@Aspect
public class UserProxy {
//前置通知
//@Before 注解表示作為前置通知
@Before(value = "https://www.cnblogs.com/LoginX/archive/2022/09/05/execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void before() {
System.out.println("before.........");
}
//后置通知(回傳通知)
@AfterReturning(value = "https://www.cnblogs.com/LoginX/archive/2022/09/05/execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最終通知
@After(value = "https://www.cnblogs.com/LoginX/archive/2022/09/05/execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after.........");
}
//例外通知
@AfterThrowing(value = "https://www.cnblogs.com/LoginX/archive/2022/09/05/execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//環繞通知
@Around(value = "https://www.cnblogs.com/LoginX/archive/2022/09/05/execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("環繞之前.........");
//被增強的方法執行
proceedingJoinPoint.proceed();
System.out.println("環繞之后.........");
}
}
TestAop.java
package com.atguigu.spring5.test;
import com.atguigu.spring5.aopanno.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAop {
@Test
public void testAopanno(){
ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
User user=context.getBean("user", User.class);
user.add();
}
}
bean1.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: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>
5.相同切入點抽取
多個方法execution運算式相同時使用
//相同切入點抽取
@Pointcut(value = "https://www.cnblogs.com/LoginX/archive/2022/09/05/execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {}
@Before(value = "https://www.cnblogs.com/LoginX/archive/2022/09/05/pointdemo()")
public void before() {
System.out.println("before.........");
}
6.有多個增強類對同一個方法進行增強,設定增強類優先級先后執行
在增強類上面添加注解 @Order(數字型別值),數字型別值越小優先級越高
7.完全注解開發
創建配置類
@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {}
組態檔操作
1、創建兩個類,增強類和被增強類,創建方法
2、在spring組態檔中創建兩個類物件
<bean id="book" ></bean>
<bean id="bookProxy" ></bean>
3、在spring組態檔中配置切入點
<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>
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/504291.html
標籤:其他
