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

SpringBoot使用AOP實現(xiàn)統(tǒng)計全局接口訪問次數(shù)詳解

 更新時間:2022年06月16日 11:19:04   作者:Tamaxiii  
這篇文章主要介紹了SpringBoot通過AOP實現(xiàn)對全局接口訪問次數(shù)的統(tǒng)計,文章從相關(guān)問題展開全文內(nèi)容詳情,具有一定的參考價值,需要的小伙伴可以參考一下

AOP是什么

AOP(Aspect Oriented Programming),也就是面向切面編程,是通過預(yù)編譯方式和運行期間動態(tài)代理實現(xiàn)程序功能的傳統(tǒng)已維護的一種技術(shù)。

AOP的作用和優(yōu)勢

作用:在程序運行期間,在不修改源代碼的情況下對某些方法進行功能增強

優(yōu)勢:減少重復(fù)代碼,提高開發(fā)效率,并且便于維護

常見的動態(tài)代理技術(shù)

jdk代理:基于接口的動態(tài)代理技術(shù)

cglib代理:基于父類的動態(tài)代理技術(shù)

AOP相關(guān)概念

  • List item- Target(目標對象):代理的目標對象
  • Proxy(代理):一個類被AOP織入增強后,就產(chǎn)生一個結(jié)果代理類
  • Joinpoint(連接點):連接點是指那些被攔截到的點。在Spring中,這些點指的是方法,因為Spring只支持方法類型的連接點(可以被增強的方法叫連接點)
  • PointCut(切入點):切入點是指我們要對哪些Joinpoint進行攔截的定義
  • Advice(通知/增強):通知是指攔截到Joinpoint之后所要做的事情就是通知
  • Aspect(切面):是切入點和通知的結(jié)合
  • Weaving(織入):把增強應(yīng)用到目標對象來創(chuàng)建新的代理對象的過程。Spring采用動態(tài)代理織入,而AspectJ采用編譯器織入和類裝載器織入

實現(xiàn)

我在這里采用基于注解形式的的AOP開發(fā)。

開發(fā)步驟

加入依賴

		<!--引入AOP依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!--AOP-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.12</version>
        </dependency>

創(chuàng)建目標接口和目標類(內(nèi)部有切點)

創(chuàng)建切面類(內(nèi)部有增強方法)

將目標類和切面類的對象創(chuàng)建權(quán)交給Spirng

在切面類中使用注解配置織入關(guān)系

在配置文件中開啟組件掃描和AOP自動代理

因為我的項目是SpringBoot Web項目,在這里開啟注解就好了。

下面的代碼為使用到的原子計數(shù)類。

import java.util.concurrent.atomic.AtomicInteger;
public class AtomicCounter {
    private static final AtomicCounter atomicCounter = new AtomicCounter();
    /**
     * 單例,不允許外界主動實例化
     */
    private AtomicCounter() {
    }
    public static AtomicCounter getInstance() {
        return atomicCounter;
    }
    private static AtomicInteger counter = new AtomicInteger();
    public int getValue() {
        return counter.get();
    }
    public int increase() {
        return counter.incrementAndGet();
    }
    public int decrease() {
        return counter.decrementAndGet();
    }
    // 清零
    public void toZero(){
        counter.set(0);
    }
}

下面的代碼為實現(xiàn)的全局接口監(jiān)控。

我在項目中簡單使用了Redis作緩存,所有你可以看到有Redis相關(guān)的操作。

使用 @Before 用于配置前置通知。指定增強的方法在切入點方法之前執(zhí)行。

使用@ @AfterReturning 用于配置后置通知。指定增強的方法在切入點方法之后執(zhí)行。

使用@ @AfterThrowing 用于配置異常拋出通知。指定增強的方法在出現(xiàn)異常時執(zhí)行。

@Component
@Aspect
public class GlobalActuator {
    private static final Logger log = LoggerFactory.getLogger(GlobalActuator.class);
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    ThreadLocal<Long> startTime = new ThreadLocal<>();
    ConcurrentHashMap<Object, Object> countMap = new ConcurrentHashMap<Object, Object>();
    /**
     * 匹配控制層層通知 這里監(jiān)控controller下的所有接口
     */
    @Pointcut("execution(* com.sf.controller.*Controller.*(..))")
    private void controllerPt() {
    }
    /**
     * 在接口原有的方法執(zhí)行前,將會首先執(zhí)行此處的代碼
     */
    @Before("com.sf.actuator.GlobalActuator.controllerPt()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());
        //獲取傳入目標方法的參數(shù)
        Object[] args = joinPoint.getArgs();
    }
    /**
     * 只有正常返回才會執(zhí)行此方法
     * 如果程序執(zhí)行失敗,則不執(zhí)行此方法
     */
    @AfterReturning(returning = "returnVal", pointcut = "com.sf.actuator.GlobalActuator.controllerPt()")
    public void doAfterReturning(JoinPoint joinPoint, Object returnVal) throws Throwable {
        Signature signature = joinPoint.getSignature();
        String declaringName = signature.getDeclaringTypeName();
        String methodName = signature.getName();
        String mapKey = declaringName + methodName;
        // 執(zhí)行成功則計數(shù)加一
        int increase = AtomicCounter.getInstance().increase();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        synchronized (this) {
        //在項目啟動時,需要在Redis中讀取原有的接口請求次數(shù)
            if (countMap.size() == 0) {
                JSONObject jsonObject = RedisUtils.objFromRedis(StringConst.INTERFACE_ACTUATOR);
                if (jsonObject != null) {
                    Set<String> strings = jsonObject.keySet();
                    for (String string : strings) {
                        Object o = jsonObject.get(string);
                        countMap.putIfAbsent(string, o);
                    }
                }
            }
        }
        // 如果此次訪問的接口不在countMap,放入countMap
        countMap.putIfAbsent(mapKey, 0);
        countMap.compute(mapKey, (key, value) -> (Integer) value + 1);
        synchronized (this) {
            // 內(nèi)存計數(shù)達到30 更新redis
            if (increase == 30) {
                RedisUtils.objToRedis(StringConst.INTERFACE_ACTUATOR, countMap, Constants.AVA_REDIS_TIMEOUT);
                //刪除過期時間
                stringRedisTemplate.persist(StringConst.INTERFACE_ACTUATOR);
                //計數(shù)器置為0
                AtomicCounter.getInstance().toZero();
            }
        }
        //log.info("方法執(zhí)行次數(shù):" + mapKey + "------>" + countMap.get(mapKey));
        //log.info("URI:[{}], 耗費時間:[{}] ms", request.getRequestURI(), System.currentTimeMillis() - startTime.get());
    }
    /**
     * 當(dāng)接口報錯時執(zhí)行此方法
     */
    @AfterThrowing(pointcut = "com.sf.actuator.GlobalActuator.controllerPt()")
    public void doAfterThrowing(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        log.info("接口訪問失敗,URI:[{}], 耗費時間:[{}] ms", request.getRequestURI(), System.currentTimeMillis() - startTime.get());
    }
}

這里再給出Controller層代碼。

    @GetMapping("/interface/{intCount}")
    @ApiOperation(value = "查找接口成功訪問次數(shù)(默認倒序)")
    public Result<List<InterfaceDto>> findInterfaceCount(
            @ApiParam(name = "intCount", value = "需要的接口數(shù)") @PathVariable Integer intCount
    ) {
        HashMap<String, Integer> hashMap = new HashMap<>();
        JSONObject jsonObject = RedisUtils.objFromRedis(StringConst.INTERFACE_ACTUATOR);
        if (jsonObject != null) {
            Set<String> strings = jsonObject.keySet();
            for (String string : strings) {
                Integer o = (Integer) jsonObject.get(string);
                hashMap.putIfAbsent(string, o);
            }
        }
        //根據(jù)value倒序
        Map<String, Integer> sortedMap = hashMap.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
        //返回列表
        List<InterfaceDto> resultList = new ArrayList<>();
        //排序后中的map中所有的key
        Object[] objects = sortedMap.keySet().toArray();
        for (int i = 0; i < intCount; i++) {
            InterfaceDto interfaceDto = new InterfaceDto();
            interfaceDto.setName((String) objects[i]);
            interfaceDto.setCount(sortedMap.get((String) objects[i]));
            resultList.add(interfaceDto);
        }
        return Result.success(resultList);
    }

項目運行一段時間后,在Redis中可以看到接口的請求次數(shù)。

Web最終效果圖如下:

到此這篇關(guān)于SpringBoot使用AOP實現(xiàn)統(tǒng)計全局接口訪問次數(shù)詳解的文章就介紹到這了,更多相關(guān)SpringBoot AOP內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring?Boot項目集成Knife4j接口文檔的實例代碼

    Spring?Boot項目集成Knife4j接口文檔的實例代碼

    Knife4j就相當(dāng)于是swagger的升級版,對于我來說,它比swagger要好用得多<BR>,這篇文章主要介紹了Spring?Boot項目集成Knife4j接口文檔的示例代碼,需要的朋友可以參考下
    2021-12-12
  • spring-boot-maven-plugin:打包時排除provided依賴問題

    spring-boot-maven-plugin:打包時排除provided依賴問題

    這篇文章主要介紹了spring-boot-maven-plugin:打包時排除provided依賴問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • 如何用Spring發(fā)送電子郵件

    如何用Spring發(fā)送電子郵件

    這篇文章主要介紹了如何用Spring發(fā)送電子郵件,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2021-02-02
  • Java web項目中的強制登錄功能實現(xiàn)代碼

    Java web項目中的強制登錄功能實現(xiàn)代碼

    本文給大家分享Java web項目中的強制登錄功能實現(xiàn)代碼,為了避免直接進入項目中存在的頁面,使用filter過濾器,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-11-11
  • java中 IO 常用IO操作類繼承結(jié)構(gòu)分析

    java中 IO 常用IO操作類繼承結(jié)構(gòu)分析

    本篇文章小編為大家介紹,java中 IO 常用IO操作類繼承結(jié)構(gòu)分析。需要的朋友參考下
    2013-04-04
  • Springboot快速入門教程

    Springboot快速入門教程

    今天給大家?guī)淼氖顷P(guān)于Springboot基礎(chǔ)的相關(guān)知識,文章圍繞著Springboot的基礎(chǔ)知識及用法展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Java實現(xiàn)ArrayList自動擴容

    Java實現(xiàn)ArrayList自動擴容

    ArrayList的擴容規(guī)則是非常簡單的,它會根據(jù)需要自動擴容,本文就來介紹一下Java實現(xiàn)ArrayList自動擴容,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • Java內(nèi)部類和異常類的概念以及使用

    Java內(nèi)部類和異常類的概念以及使用

    這篇文章主要介紹了Java內(nèi)部類和異常類的概念以及使用,文中有非常詳細的代碼以及注釋,適合正在學(xué)習(xí)java基礎(chǔ)的同學(xué)們使用,需要的朋友可以參考下
    2021-04-04
  • Mybatis打印SQL語句的幾種常用方式

    Mybatis打印SQL語句的幾種常用方式

    當(dāng)我們動態(tài)sql拼接的塊很多的時候,我們要想從*mapper.xml中直接找出完整的sql就會非常的難,這個時候經(jīng)常會需要把組合之后的完整SQL語句打印出來,對調(diào)試非常有幫助的,所以本文小編給大家介紹了幾種Mybatis打印SQL語句的常用方式,需要的朋友可以參考下
    2023-11-11
  • Java基于SpringBoot和tk.mybatis實現(xiàn)事務(wù)讀寫分離代碼實例

    Java基于SpringBoot和tk.mybatis實現(xiàn)事務(wù)讀寫分離代碼實例

    這篇文章主要介紹了Java基于SpringBoot和tk.mybatis實現(xiàn)事務(wù)讀寫分離代碼實例,讀寫分離,基本的原理是讓主數(shù)據(jù)庫處理事務(wù)性增、改、刪操作,而從數(shù)據(jù)庫處理SELECT查詢操作,數(shù)據(jù)庫復(fù)制被用來把事務(wù)性操作導(dǎo)致的變更同步到集群中的從數(shù)據(jù)庫,需要的朋友可以參考下
    2023-10-10

最新評論