使用Spring Boot的AOP處理自定義註解

前言

上一篇文章Java 註解介紹講解了下Java註解的基本使用方式,並且通過自定義註解實現了一個簡單的測試工具;本篇文章將介紹如何使用Spring Boot的AOP來簡化處理自定義註解,並將通過實現一個簡單的方法執行時間統計工具爲樣例來講解這些內容。

AOP概念

面向側面的程序設計(aspect-oriented programming,AOP,又譯作面向方面的程序設計、觀點導向編程、剖面導向程序設計)是計算機科學中的一個術語,指一種程序設計範型。該範型以一種稱爲側面(aspect,又譯作方面)的語言構造爲基礎,側面是一種新的模塊化機制,用來描述分散在對象、類或函數中的橫切關注點(crosscutting concern)。

側面的概念源於對面向對象的程序設計的改進,但並不只限於此,它還可以用來改進傳統的函數。與側面相關的編程概念還包括元對象協議、主題(subject)、混入(mixin)和委託。

註釋:以上定義源自中文維基百科(如果訪問不了,可以通過修改系統的hosts文件訪問,198.35.26.96 zh.wikipedia.org #中文維基百科,只能幫到這了,如果還是上不了,那就麻煩上網搜索下怎麼修改系統的hosts文件,不同系統下hosts文件位置不一樣,如果是Linux或者Mac系統,我就直接告訴你吧,一般文件路徑是/etc/hosts),AOP這個詞的翻譯有點和國內主流叫法不一致,國內主流都把AOP譯做「面向切面編程」,大家不要拘泥於叫法,知道指的是同一個東西即可。

估計,你看了這個定義也是懵的,如果想深入瞭解可以去知乎看看大佬們是如何掰扯的什麼是面向切面編程AOP?。我這邊還是就直接上例子了吧。

Spring Boot的AOP環境準備

pom.xml中引入相應的依賴模塊

<!-- Spring Boot依賴包 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.1.RELEASE</version>
</parent>
<dependencies>
    <!-- AOP依賴模塊 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <!-- Web依賴模塊 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

先實現一個簡單的Web請求處理

一個簡單的處理Web請求的Controller。

package com.craneyuan.controller;

import com.craneyuan.service.IHelloWorldService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldController {
    @Autowired
    private IHelloWorldService helloWorldService;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello(String name) {
        return helloWorldService.getHelloMessage(name);
    }
}

一個簡單的HelloWorld服務實現類,接口的定義我就不展示代碼了。

package com.craneyuan.service.impl;

import com.craneyuan.annotation.AnalysisActuator;
import com.craneyuan.service.IHelloWorldService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class HelloWorldServiceImpl implements IHelloWorldService {

    public String getHelloMessage(String name) {
        return "Hello " + Optional.ofNullable(name).orElse("World!");
    }

}

這樣一個簡單的Web服務就弄好了,你可以啓動項目用curl命令調用試下,例如:curl -XGET -i "http://127.0.0.1:8080/hello?name=Java",如果一切順利的話,你將會得到類似下面這樣的響應:

HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 11
Date: Thu, 11 Jan 2018 09:45:38 GMT

Hello Java

使用自定義註解來統計方法的執行時間

先定義一個用來統計方法執行時間的註解。

package com.craneyuan.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnalysisActuator {
    String note() default "";
}

然後定義一個切面,來處理剛剛定義的註解。

package com.craneyuan.aspect;

import com.craneyuan.annotation.AnalysisActuator;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AnalysisActuatorAspect {
    final static Logger log = LoggerFactory.getLogger(AnalysisActuatorAspect.class);

    ThreadLocal<Long> beginTime = new ThreadLocal<>();

    @Pointcut("@annotation(analysisActuator)")
    public void serviceStatistics(AnalysisActuator analysisActuator) {
    }

    @Before("serviceStatistics(analysisActuator)")
    public void doBefore(JoinPoint joinPoint, AnalysisActuator analysisActuator) {
        // 記錄請求到達時間
        beginTime.set(System.currentTimeMillis());
        log.info("cy666 note:{}", analysisActuator.note());
    }

    @After("serviceStatistics(analysisActuator)")
    public void doAfter(AnalysisActuator analysisActuator) {
        log.info("cy666 statistic time:{}, note:{}", System.currentTimeMillis() - beginTime.get(), analysisActuator.note());
    }

}

最後,只要在需要統計執行時間的方法上加上@AnalysisActuator註解就行了。

package com.craneyuan.service.impl;

import com.craneyuan.annotation.AnalysisActuator;
import com.craneyuan.service.IHelloWorldService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class HelloWorldServiceImpl implements IHelloWorldService {

    @AnalysisActuator(note = "獲取聊天信息方法")
    public String getHelloMessage(String name) {
        return "Hello " + Optional.ofNullable(name).orElse("World!");
    }

}

啓動項目,用curl命令隨便調用一下,如果順利的話就可以觀察到切面打印的日誌了。

...
cy666 statistic time:4, note:獲取聊天信息方法

參考文章

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