欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot Aop 詳解和多種使用場景解析

 更新時間:2021年08月02日 15:43:25   作者:kenx  
aop面向切面編程,是編程中一個很重要的思想本篇文章主要介紹的是SpringBoot切面Aop的使用和案例,對SpringBoot Aop相關(guān)知識感興趣的朋友跟隨小編一起看看吧

前言

aop面向切面編程,是編程中一個很重要的思想本篇文章主要介紹的是SpringBoot切面Aop的使用和案例

什么是aop

AOP(Aspect OrientedProgramming):面向切面編程,面向切面編程(也叫面向方面編程),是目前軟件開發(fā)中的一個熱點,也是Spring框架中的一個重要內(nèi)容。利用AOP可以對業(yè)務邏輯的各個部分進行隔離,從而使得業(yè)務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率。

使用場景

利用AOP可以對我們邊緣業(yè)務進行隔離,降低無關(guān)業(yè)務邏輯耦合性。提高程序的可重用性,同時提高了開發(fā)的效率。一般用于日志記錄,性能統(tǒng)計,安全控制,權(quán)限管理,事務處理,異常處理,資源池管理。使用場景

為什么需要面向切面編程

面向?qū)ο缶幊蹋∣OP)的好處是顯而易見的,缺點也同樣明顯。當需要為多個不具有繼承關(guān)系的對象添加一個公共的方法的時候,例如日志記錄、性能監(jiān)控等,如果采用面向?qū)ο缶幊痰姆椒?,需要在每個對象里面都添加相同的方法,這樣就產(chǎn)生了較大的重復工作量和大量的重復代碼,不利于維護。面向切面編程(AOP)是面向?qū)ο缶幊痰难a充,簡單來說就是統(tǒng)一處理某一“切面”的問題的編程思想。如果使用AOP的方式進行日志的記錄和處理,所有的日志代碼都集中于一處,不需要再每個方法里面都去添加,極大減少了重復代碼。

技術(shù)要點

  • 通知(Advice)包含了需要用于多個應用對象的橫切行為,完全聽不懂,沒關(guān)系,通俗一點說就是定義了“什么時候”和“做什么”。
  • 連接點(Join Point)是程序執(zhí)行過程中能夠應用通知的所有點。
  • 切點(Poincut)是定義了在“什么地方”進行切入,哪些連接點會得到通知。顯然,切點一定是連接點。
  • 切面(Aspect)是通知和切點的結(jié)合。通知和切點共同定義了切面的全部內(nèi)容——是什么,何時,何地完成功能。
  • 引入(Introduction)允許我們向現(xiàn)有的類中添加新方法或者屬性。
  • 織入(Weaving)是把切面應用到目標對象并創(chuàng)建新的代理對象的過程,分為編譯期織入、類加載期織入和運行期織入。

整合使用

導入依賴

在springboot中使用aop要導aop依賴

 <!--aop 切面-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

注意這里版本依賴于spring-boot-start-parent父pom中的spring-boot-dependencies

編寫攔截的bean

這里我們定義一個controller用于攔截所有請求的記錄

@RestController
public class AopController {

    @RequestMapping("/hello")
    public String sayHello(){
        System.out.println("hello");
        return "hello";
    }
}

定義切面

SpringBoot在使用切面的時候采用@Aspect注解對POJO進行標注,該注解表明該類不僅僅是一個POJO,還是一個切面容器

定義切點

切點是通過@Pointcut注解和切點表達式定義的。

@Pointcut注解可以在一個切面內(nèi)定義可重用的切點。

由于Spring切面粒度最小是達到方法級別,而execution表達式可以用于明確指定方法返回類型,類名,方法名和參數(shù)名等與方法相關(guān)的部件,并且實際中,大部分需要使用AOP的業(yè)務場景也只需要達到方法級別即可,因而execution表達式的使用是最為廣泛的。如圖是execution表達式的語法:

execution表示在方法執(zhí)行的時候觸發(fā)。以“”開頭,表明方法返回值類型為任意類型。然后是全限定的類名和方法名,“”可以表示任意類和任意方法。對于方法參數(shù)列表,可以使用“..”表示參數(shù)為任意類型。如果需要多個表達式,可以使用“&&”、“||”和“!”完成與、或、非的操作。

定義通知

通知有五種類型,分別是:

  • 前置通知(@Before):在目標方法調(diào)用之前調(diào)用通知
  • 后置通知(@After):在目標方法完成之后調(diào)用通知
  • 環(huán)繞通知(@Around):在被通知的方法調(diào)用之前和調(diào)用之后執(zhí)行自定義的方法
  • 返回通知(@AfterReturning):在目標方法成功執(zhí)行之后調(diào)用通知
  • 異常通知(@AfterThrowing):在目標方法拋出異常之后調(diào)用通知

代碼中定義了三種類型的通知,使用@Before注解標識前置通知,打印“beforeAdvice...”,使用@After注解標識后置通知,打印“AfterAdvice...”,使用@Around注解標識環(huán)繞通知,在方法執(zhí)行前和執(zhí)行之后分別打印“before”和“after”。這樣一個切面就定義好了,代碼如下:

@Aspect
@Component
public class AopAdvice {

    @Pointcut("execution (* com.shangguan.aop.controller.*.*(..))")
    public void test() {

    }

    @Before("test()")
    public void beforeAdvice() {
        System.out.println("beforeAdvice...");
    }

    @After("test()")
    public void afterAdvice() {
        System.out.println("afterAdvice...");
    }

    @Around("test()")
    public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("before");
        try {
            proceedingJoinPoint.proceed();
        } catch (Throwable t) {
            t.printStackTrace();
        }
        System.out.println("after");
    }

}

運行結(jié)果

案例場景

這里我們通過一個日志記錄場景來完整的使用Aop切面業(yè)務層只需關(guān)心代碼邏輯實現(xiàn)而不用關(guān)心請求參數(shù)和響應參數(shù)的日志記錄

那么首先我們需要自定義一個全局日志記錄的切面類GlobalLogAspect

然后在該類添加@Aspect注解,然后在定義一個公共的切入點(Pointcut),指向需要處理的包,然后在定義一個前置通知(添加@Before注解),后置通知(添加@AfterReturning)和環(huán)繞通知(添加@Around)方法實現(xiàn)即可

日志信息類

package cn.soboys.core;

import lombok.Data;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/18 18:48
 * 日志信息
 */
@Data
public class LogSubject {
    /**
     * 操作描述
     */
    private String description;

    /**
     * 操作用戶
     */
    private String username;

    /**
     * 操作時間
     */
    private String startTime;

    /**
     * 消耗時間
     */
    private String spendTime;

    /**
     * URL
     */
    private String url;

    /**
     * 請求類型
     */
    private String method;

    /**
     * IP地址
     */
    private String ip;

    /**
     * 請求參數(shù)
     */
    private Object parameter;

    /**
     * 請求返回的結(jié)果
     */
    private Object result;

    /**
     * 城市
     */
    private String city;

    /**
     * 請求設(shè)備信息
     */
    private String device;

}

全局日志攔截

package cn.soboys.core;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/18 14:52
 * 切面
 */
public class BaseAspectSupport {
    public Method resolveMethod(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature)point.getSignature();
        Class<?> targetClass = point.getTarget().getClass();

        Method method = getDeclaredMethod(targetClass, signature.getName(),
                signature.getMethod().getParameterTypes());
        if (method == null) {
            throw new IllegalStateException("無法解析目標方法: " + signature.getMethod().getName());
        }
        return method;
    }

    private Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                return getDeclaredMethod(superClass, name, parameterTypes);
            }
        }
        return null;
    }
}

GlobalLogAspect

package cn.soboys.core;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.json.JSONUtil;
import cn.soboys.core.utils.HttpContextUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/18 15:22
 * 全局日志記錄器
 */
@Slf4j
@Aspect
@Component
public class GlobalLogAspect extends BaseAspectSupport {
    /**
     * 定義切面Pointcut
     */
    @Pointcut("execution(public * cn.soboys.mallapi.controller.*.*(..))")
    public void log() {

    }


    /**
     * 環(huán)繞通知
     *
     * @param joinPoint
     * @return
     */
    @Around("log()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

        LogSubject logSubject = new LogSubject();
        //記錄時間定時器
        TimeInterval timer = DateUtil.timer(true);
        //執(zhí)行結(jié)果
        Object result = joinPoint.proceed();
        logSubject.setResult(result);
        //執(zhí)行消耗時間
        String endTime = timer.intervalPretty();
        logSubject.setSpendTime(endTime);
        //執(zhí)行參數(shù)
        Method method = resolveMethod(joinPoint);
        logSubject.setParameter(getParameter(method, joinPoint.getArgs()));

        HttpServletRequest request = HttpContextUtil.getRequest();
        // 接口請求時間
        logSubject.setStartTime(DateUtil.now());
        //請求鏈接
        logSubject.setUrl(request.getRequestURL().toString());
        //請求方法GET,POST等
        logSubject.setMethod(request.getMethod());
        //請求設(shè)備信息
        logSubject.setDevice(HttpContextUtil.getDevice());
        //請求地址
        logSubject.setIp(HttpContextUtil.getIpAddr());
        //接口描述
        if (method.isAnnotationPresent(ApiOperation.class)) {
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
            logSubject.setDescription(apiOperation.value());
        }

        String a = JSONUtil.toJsonPrettyStr(logSubject);
        log.info(a);
        return result;

    }


    /**
     * 根據(jù)方法和傳入的參數(shù)獲取請求參數(shù)
     */
    private Object getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        Map<String, Object> map = new HashMap<>();
        for (int i = 0; i < parameters.length; i++) {
            //將RequestBody注解修飾的參數(shù)作為請求參數(shù)
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            //將RequestParam注解修飾的參數(shù)作為請求參數(shù)
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            String key = parameters[i].getName();
            if (requestBody != null) {
                argList.add(args[i]);
            } else if (requestParam != null) {
                map.put(key, args[i]);
            } else {
                map.put(key, args[i]);
            }
        }
        if (map.size() > 0) {
            argList.add(map);
        }
        if (argList.size() == 0) {
            return null;
        } else if (argList.size() == 1) {
            return argList.get(0);
        } else {
            return argList;
        }
    }
}

到此這篇關(guān)于SpringBoot Aop 詳解和多種使用場景的文章就介紹到這了,更多相關(guān)SpringBoot Aop使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring?IOC中對象的創(chuàng)建、策略及銷毀時機和生命周期詳解

    Spring?IOC中對象的創(chuàng)建、策略及銷毀時機和生命周期詳解

    這篇文章主要介紹了Spring?IOC中對象的創(chuàng)建、策略及銷毀時機和生命周期詳解,Spring默認使用類的空參構(gòu)造方法創(chuàng)建bean,假如類沒有空參構(gòu)造方法,將無法完成bean的創(chuàng)建,需要的朋友可以參考下
    2023-08-08
  • springboot之如何獲取項目目錄路徑

    springboot之如何獲取項目目錄路徑

    這篇文章主要介紹了springboot之如何獲取項目目錄路徑問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 基于web項目log日志指定輸出文件位置配置方法

    基于web項目log日志指定輸出文件位置配置方法

    下面小編就為大家分享一篇基于web項目log日志指定輸出文件位置配置方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-04-04
  • springMVC實現(xiàn)文件上傳和下載

    springMVC實現(xiàn)文件上傳和下載

    這篇文章主要為大家詳細介紹了springMVC實現(xiàn)文件上傳和下載,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • java 用itext設(shè)置pdf紙張大小操作

    java 用itext設(shè)置pdf紙張大小操作

    這篇文章主要介紹了java 用itext設(shè)置pdf紙張大小操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • 完美解決因數(shù)據(jù)庫一次查詢數(shù)據(jù)量過大導致的內(nèi)存溢出問題

    完美解決因數(shù)據(jù)庫一次查詢數(shù)據(jù)量過大導致的內(nèi)存溢出問題

    今天小編就為大家分享一篇完美解決因數(shù)據(jù)庫一次查詢數(shù)據(jù)量過大導致的內(nèi)存溢出問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06
  • Java中抓取 Thread Dumps 的方式匯總

    Java中抓取 Thread Dumps 的方式匯總

    Thread dumps(線程轉(zhuǎn)儲)能幫助我們判斷 CPU 峰值、死鎖、內(nèi)存異常、應用反應遲鈍、響應時間變長和其他系統(tǒng)問題。在這篇文章當中,總結(jié)了7中抓取 Java Thread Dumps 文件的方式,分享給大家,希望對大家學習Java能夠有所幫助。
    2016-06-06
  • 深入理解spring的AOP機制原理

    深入理解spring的AOP機制原理

    本篇文章主要介紹了深入理解spring的AOP機制原理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • 使用Feign調(diào)用第三方http接口

    使用Feign調(diào)用第三方http接口

    這篇文章主要介紹了使用Feign調(diào)用第三方http接口,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 詳解Java中字符流與字節(jié)流的區(qū)別

    詳解Java中字符流與字節(jié)流的區(qū)別

    這篇文章主要為大家詳細介紹了Java中字符流與字節(jié)流的區(qū)別,這兩個的概念易混淆,今天就為大家進行詳細區(qū)分,感興趣的小伙伴們可以參考一下
    2016-04-04

最新評論