spring AOP常用註解介紹使用(實現四種通知的小案例)

Aop 簡介:

Aop(Aspect Oriented Programming)面向切面編程,是OOP面向對象編程的一種補充將程序中交叉業務(事務,日誌)代碼提取出來,封裝成切面,由AOP容器在適當時機位置將封裝成的切面動態的織入到具體業務邏輯中。

AOP不是spring特有的。
應用場合:適用於具有橫切邏輯的場合,如事務管理,日誌記錄,性能檢測,異常通知,訪問控制等。
作用:不改變原有代碼的基礎動態添加新的功能。
模塊化。

術語:

**連接點(JoinPoint)**程序執行的某個特定位置,如方法調用前,方法調用後,方法拋出異常時,方法調用前後等。

**切入點(PointCut)**定位位置找到需要切入的連接點,即切點。

增強Advice也稱爲通知 在切點上執行的一段代碼用來實現某些功能。

目標對象 Target 將執行增強處理的目標類。

織入(Weaving) 將增強添加到目標類具體切入點的過程。

代理(Proxy) 一個類被織入增強後,會產生一個代理類。

切面 Aspect 切點和增強的組合。

引介 Introduction 也稱爲引入。

實現原理

代理模式:

概念:爲其他對象提供一種代理,以控制對這個對象的訪問,起到中介的作用,通過代理對象訪問目標對象,可以增強額外的操作,擴展目標對象的功能。
分類:
**靜態代理:**代理類是程序員創建或工具生成的,所謂靜態代理就是程序運行之前就已經存在代理類的字節碼文件。缺點:代理對象需要和目標對象實現相同的接口,如果接口增加方法,目標對象和代理對象都要維護。
動態代理:代理類是程序在運行期間由JVM根據反射等機制動態生成的,自動生成代理類和代理對象,所謂動態就是在程序運行之前不存在代理類的字節碼文件。

代理三要素:

一:實現目標類的接口。

二:初始化目標類的實例。

三:交叉業務,要執行的操作。

動態代理:

JDK技術:

proxy.newProxyInstance(
classloader  目標類的類加載器
interfaces    目標類得到接口列表
InvocationHandler   交叉業務邏輯

)

缺點:目標對象必須實現一個或多個接口,如果沒有實現接口,則無法使用JDK的動態代理,此時可以使用cglib技術
- cglib技術

  • 添加jar包maven依賴
-     <dependency>
          <groupId>cglib</groupId>
          <artifactId>cglib</artifactId>
      </dependency>
  • cglib技術,適用於無接口的使用
    通過繼承實現的cglib技術要
    (1):添加jar包
    (2):用法:
 Enhance.create(
  class,  目標類的對象
  InvocationHandler  交叉業務邏輯
  )

Aop原理

SpringAop 原理就是動態代理
對於實現接口的目標類使用的是jdk動態代理
對於沒有實現任何接口的目標類,使用的是cglib的動態代理
代理類是程序在運行期間由JVM根據反射等機制動態生成的自動生成代理類和代理對象。
所謂動態就是指在程序運行前不存在代理類的字節碼文件。

AOP基於註解的配置介紹

一:@Component 此註解使用在class上來聲明一個Spring組件(Bean), 將其加入到應用上下文中 ,添加到IOC容器中,不區分組件類型。
二:@Aspect 此註解使用在class類上表示這是一個切面
三: @Pointcut定義切點表達式,可以使用within語法,也可以使用execution語法。
例如: @Pointcut("execution(* aop.service.impl.*ServiceImpl.*(..))")

AspectJ表達式

簡介:切點表達式,一種表達式,用來定義切點位置。
用法:
within 語法:within(包名.類名) 匹配該類中的所有方法。
execution
匹配特定包中的特定類中特定返回值類型的特定參數的特定方法。
語法:execution(表達式)
表達式:返回值類型 包名.類名.方法名(參數類型)
通配符:和…
四:
@Before
此註解表示的前置通知在方法執行之前執行。
五:
@AfterReturning
此註解表示的是後置通知在方法執行之後執行該方法。
六: @AfterThrowing 此註解表示的是異常通知,在方法執行的時候出現異常時執行。
七:
@Around*此註解表示的是環繞通知,在方法執行前後都會執行,該通知而用於做性能的監測,比如方法執行的時間統計。
八: contxt:aspectj-autoproxy/ 表示自動創建註解並織入

使用aop註解開發的小案例進一步瞭解。

配置通知類並基於註解實現的通知(增強)此處根據自己需要實現通知

package aop.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import sun.awt.SunHints;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * package_name:aop.advice
 *
 * @author:徐亞遠 Date:2020/2/21 14:09
 * 項目名:springDemo01
 * Description:TODO
 * Version: 1.0
 **/
@Component
//表示這是一個切面
@Aspect
public class AnnotationAdvice {
    //定義切點表達式
    @Pointcut("execution(* aop.service.impl.*ServiceImpl.*(..))")
    public void pt() {
    }

    @Before("pt()")
    public void begoreAdvice(JoinPoint joinPoint) {
        //簽名
        Signature signature = joinPoint.getSignature();
        //方法名
        String methodName = signature.getName();
        //轉換爲方法簽名,本質上是方法簽名
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //參數
        Object[] args = joinPoint.getArgs();
        //目標類
        Object target = joinPoint.getThis();

        System.out.println("前置通知: " + "methodName: " + methodName + " " + "args:" + Arrays.toString(args) + " " +
                "target: " + target);
    }

    @AfterReturning(value = "pt()", returning = "value")
    public void afterAdvice(JoinPoint joinPoint, Object value) {
        //簽名
        Signature signature = joinPoint.getSignature();
        //方法名
        String methodName = signature.getName();
        //轉換爲方法簽名,本質上是方法簽名
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //參數
        Object[] args = joinPoint.getArgs();
        //目標類
        Object target = joinPoint.getThis();

        System.out.println("後置通知: " + "methodName: " + methodName + " " + "args:" + Arrays.toString(args) + " " +
                "target: " +
                "" + target);
    }

    @AfterThrowing(value = "pt()", throwing = "e")
    public void throwAdvice(JoinPoint joinPoint, Exception e) {

//簽名
        Signature signature = joinPoint.getSignature();
        //方法名
        String methodName = signature.getName();
        //轉換爲方法簽名,本質上是方法簽名
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //參數
        Object[] args = joinPoint.getArgs();
        //目標類
        Object target = joinPoint.getThis();

        System.out.println("異常通知: " + "methodName: " + methodName + " " + "args:" + Arrays.toString(args) + " " +
                "target: " +
                "" + target);
    }

    @Around("pt()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint){
         Long startTime = System.currentTimeMillis();
        Object proceed = null;
        try {
            System.out.println("開啓事務:");
            proceed = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("回滾事務:");
            return "出錯";
        }
        Long endTime = System.currentTimeMillis();
        System.out.println("方法執行時間:"+(endTime-startTime)+"ms");
         return proceed;
    }
}

書寫登錄的接口:

package aop.service;

/**
 * package_name:com.anno.service
 *
 * @author:徐亞遠 Date:2020/2/20 17:19
 * 項目名:springDemo01
 * Description:TODO
 * Version: 1.0
 **/
public interface UserService {
    /**
      * @Author : 徐亞遠 
      * @Date : 2020/2/21 14:08
      * @Description :
     * @param password
     * @param username
      */
      
    void login(String username, String password);
}

實現登錄接口。

package aop.service.impl;

import aop.service.UserService;
import org.springframework.stereotype.Service;

/**
 * package_name:com.anno.service.impl
 *
 * @author:徐亞遠 Date:2020/2/20 17:19
 * 項目名:springDemo01
 * Description:TODO
 * Version: 1.0
 **/
@Service()
public class UserServiceImpl implements UserService {
    /**
     * @param username
     * @param password
     * @Author : 徐亞遠
     * @Date : 2020/2/21 14:08
     * @Description :
     */
    @Override
    public void login(String username, String password) {
        int i = 5/0;
        System.out.println("loginUserServiceImpl登錄方法執行:" + username + "    " + password);
    }
}

配置配置文件(spring.xmll)

<?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:contxt="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">

    <!--掃包把使用註解的實體類配置到IOC容器中,交給容器管理-->
    <context:component-scan base-package="aop.service.impl"/>
    <context:component-scan base-package="aop.advice"/>
    <!--自動創建代理並織入切面
    proxy-target-class=""默認false 表示使用jdk動態代理,true表示使用cglib動態代理 使用cglib要加入依賴-->
    <contxt:aspectj-autoproxy/>
</beans>

書寫測試類:

package aop;

import aop.service.UserService;
import aop.service.impl.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * package_name:aop
 *
 * @author:徐亞遠 Date:2020/2/21 14:16
 * 項目名:springDemo01
 * Description:TODO
 * Version: 1.0
 **/

public class controller {
    public static void main(String [] args){
        ApplicationContext ac = new ClassPathXmlApplicationContext("aop/spring.xml");
 UserService userService = (UserService) ac.getBean("userServiceImpl");
       userService.login("12","123" );
        System.out.println(userService.getClass());
    }
}

測試結果如圖:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章