Spring - AOP


AOP

AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期間動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。html

AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。不經過修改源代碼方式,在主幹功能裏面添加新功能。java

官方文檔:點我傳送spring

AOP - 概念

  • 鏈接點: 類裏能夠被加強的方法稱爲鏈接點express

  • 切入點: 實際被加強的方法稱爲切入點編程

  • 通知(加強):加強的邏輯部分稱爲通知api

    • 通知類型: 前置通知、後置通知、環繞通知、異常通知、最終通知
  • 切面: 通知應用到切入點的過程稱爲切面markdown

AOP - 原理

AOP 底層使用動態代理,有兩種狀況動態代理:oracle

方式一: JDK動態代理 (有接口狀況)
方式二: CGLIB 動態代理 (沒有接口狀況)框架

JDK動態代理

  • JDK 動態代理,使用 Proxy 類裏面的方法建立代理對象。
    • 調用 newProxyInstance 方法

JDK1.8官方文檔:點我傳送ide

public class Proxy extends Object implements Serializable{

    /**
    *   loader:類加載器
    *   interfaces:加強方法所在的類,這個類實現的接口,支持多個接口
    *   InvocationHandler:實現這個接口 InvocationHandler,建立代理對象,寫加強的部分
    *
    *   Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.
    */
    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
}
  • JDK 動態代理代碼
    • 建立接口,定義方法
    public interface UserDao {
        public int add(int a,int b);
        public String update(String str);
    }
    • 建立接口實現類,實現方法
    public class UserDaoImpl  implements UserDao {
        @Override
        public int add(int a, int b) {
            return a+b;
        }
    
        @Override
        public String update(String str) {
            return str;
        }
    }
    • 使用 Proxy 類建立接口代理對象
    public class JDKProxy {
    
        public static void main(String[] args) {
            //建立 接口實現類代理對象
            /**
             *      @CallerSensitive
             *     public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
             *
             *     public interface InvocationHandler {}
             *
             */
            Class[] interfaces = {UserDao.class};
            UserDaoImpl userDao = new UserDaoImpl();
            UserDao proxyInstance = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
            int result = proxyInstance.add(1, 2);
            String abc = proxyInstance.update("abc");
        }
    }
    
    //建立代理對象代碼
    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 invoke = null;
            if (method.getName().equals("add")) {
                System.out.println("UserDaoProxy add ...");
                invoke = method.invoke(obj, args);
            }
            if(method.getName().equals("update")){
                System.out.println("UserDaoProxy update ...");
                invoke = method.invoke(obj, args);
            }
    
            //方法以後
            System.out.println("方法以後執行: " + obj);
            return invoke;
        }
    }

AOP - 操做

  • Spring 框架通常都是基於 AspectJ 實現 AOP 操做。
AspectJ 不是 Spring 組成部分,獨立 AOP 框架,通常把 AspectJ 和 Spirng 框架一塊兒使用,進行 AOP 操做。

= 基於 AspectJ 實現 AOP 操做

- 基於 xml 配置文件實現
- 基於註解方式實現
  • 引入 AOP 相關依賴
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.1.jar
druid-1.1.9.jar
spring-aop-5.2.6.RELEASE.jar
spring-aspects-5.2.6.RELEASE.jar
spring-beans-5.2.6.RELEASE.jar
spring-context-5.2.6.RELEASE.jar
spring-core-5.2.6.RELEASE.jar
spring-expression-5.2.6.RELEASE.jar

202103311.png

  • 切入點表達式
# 切入點表達式語法結構: execution([權限修飾符] [返回類型] [類全路徑] [方法名稱]([參數列表]) )

舉例 1:對 com.hosystem.dao.BookDao 類裏面的 add 進行加強
execution(* [返回類型可省略] com.hosystem.dao.BookDao.add(..))

舉例 2:對 com.hosystem.dao.BookDao 類裏面的全部的方法進行加強
execution(* com.hosystem.dao.BookDao.* (..))

舉例 3:對 com.hosystem.dao 包裏面全部類,類裏面全部方法進行加強
execution(* com.hosystem.dao.*.* (..))

AOP - AspectJ-註解

  • 建立類

User.java

public class User {

    public void add(){
        System.out.println("User add ...");
    }
}

UserProxy.java

//加強類
public class UserProxy {
    //前置通知
    public void before(){
        System.out.println("UserProxy 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 base-package="com.hosystem.spring5.dao.aopannotation"></context:component-scan>
</beans>

使用註解建立 User 和 UserProxy 對象

//加強類
@Component
public class UserProxy {...}

@Component
public class User {...}

加強類上面添加註解 @Aspect

//加強類
@Component
@Aspect //生成代理對象
public class UserProxy {
    //前置通知
    public void before(){
        System.out.println("UserProxy before ... ");
    }
}

在 spring 配置文件中開啓生成代理對象

<!--    開啓Aspect生成代理對象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  • 配置不一樣類型的通知

在加強類的裏面,在做爲通知方法上面添加通知類型註解,使用切入點表達式配置

//加強類
@Component
@Aspect //生成代理對象
public class UserProxy {

    //前置通知
    //@Before註解表示做爲前置通知
    @Before(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))")
    public void before() {
        System.out.println("UserProxy before ... ");
    }

    //最終通知
    @After(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))")
    public void after() {
        System.out.println("UserProxy after ... ");
    }

    //後置通知(返回通知)
    @AfterReturning(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))")
    public void afterReturning() {
        System.out.println("UserProxy afterReturning ... ");
    }

    //異常通知
    @AfterThrowing(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))")
    public void afterThrowing() {
        System.out.println("UserProxy afterThrowing ... ");
    }

    //環繞通知
    //public interface ProceedingJoinPoint extends JoinPoint{}
    @Around(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("UserProxy aroud 環繞以前... ");

        //被加強的方法執行
        proceedingJoinPoint.proceed();

        System.out.println("UserProxy aroud 環繞以後... ");
    }
}
  • 相同切入點抽取
//加強類
@Component
@Aspect //生成代理對象
public class UserProxy {

    //相同切入點抽取
    @Pointcut(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))")
    public void pointdemo(){}

    //前置通知
    //@Before註解表示做爲前置通知
    @Before(value = "pointdemo()")
    public void before() {
        System.out.println("UserProxy before ... ");
    }
}
  • 測試
public class TestAop {

    /**
     * 正常執行流程:
     * UserProxy aroud 環繞以前...
     * UserProxy before ...
     * User add ...
     * UserProxy aroud 環繞以後...
     * UserProxy after ...
     * UserProxy afterReturning ...
     *
     *  有error錯誤流程:
     * UserProxy aroud 環繞以前...
     * UserProxy before ...
     * UserProxy after ...
     * UserProxy afterThrowing ...
     */
    @Test
    public void testAopAnno(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("bean1.xml");
        User user = classPathXmlApplicationContext.getBean("user", User.class);
        user.add();
    }
}

wechant_20210331142521.png

  • 加強類優先級

有多個加強類多同一個方法進行加強,設置加強類優先級。

在加強類上面添加註解 @Order(數字類型值),數字類型值越小優先級越高

@Aspect
@Component
@Order(1)
public class PersonProxy {

    //後置通知(返回通知)
    @Before(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))")
    public void before() {
        System.out.println("PersonProxy before ... ");
    }
}
  • 註解開發

建立配置類,不須要建立 xml 配置文件

@Configuration
@ComponentScan(basePackages = {"com.hosystem"})
@EnableAspectJAutoProxy(proxyTargetClass = true) //<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
public class ConfigAop {
}

AOP - AspectJ-Xml文件

  • 建立類

建立兩個類,加強類和被加強類,建立方法

Book.java

public class Book {
    public void buy(){
        System.out.println("book buy ...");
    }
}

BookProxy.java

public class BookProxy {
    public void befor(){
        System.out.println("BookProxy before ...");
    }
}
  • xml配置文件
<!--    建立對象-->
    <bean id="book" class="com.hosystem.spring5.dao.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.hosystem.spring5.dao.aopxml.BookProxy"></bean>

    <!--    配置aop加強-->
    <aop:config>
        <!--        切入點-->
        <aop:pointcut id="pcut" expression="execution(* com.hosystem.spring5.dao.aopxml.Book.buy(..))"/>
        <!--        配置切面-->
        <aop:aspect ref="bookProxy">
            <!--            加強做用在具體的方法上-->
            <aop:before method="before" pointcut-ref="pcut"></aop:before>
        </aop:aspect>
    </aop:config>
相關文章
相關標籤/搜索