上一篇文章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 中引入相應的依賴模塊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<!-- 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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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服務實現類,接口的定義我就不展示代碼了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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" ,如果一切順利的話,你將會得到類似下面這樣的響應:
1
2
3
4
5
6
|
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 |
使用自定義注解來統計方法的執行時間
先定義一個用來統計方法執行時間的注解。
1
2
3
4
5
6
7
8
9
10
11
12
|
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 "" ; } |
然后定義一個切面,來處理剛剛定義的注解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
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 注解就行了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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 命令隨便調用一下,如果順利的話就可以觀察到切面打印的日志了。
1
2
|
... cy666 statistic time:4, note:獲取聊天信息方法 |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://crane-yuan.github.io/2018/01/11/spring-boot-aop-custom-annotation