一直以来都知道Spring支持一种叫做面向切面编程(AOP)的东西,但是一直都没有自己尝试使用过. 直到最近为了Debug方法,记录使用时间猛然发现AOP正好适合使用在这个场景下.为了灵活的使用AOP,我选择了使用注解来作为标记,当某个特定的注解被使用的时候将会自动触发这个切面.
1.注解的编写
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package
org
.
jzbk
.
rssplus
.
aspect
.
annotation
;
import
java
.
lang
.
annotation
.
*
;
/**
* Created by Kotarou on 2017/1/11.
*/
@Inherited
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
{
ElementType
.
TYPE
,
ElementType
.
METHOD
}
)
public
@interface
Timed
{
boolean
displayArgs
(
)
default
false
;
}
|
将注解设置为运行时RetentionPolicy.RUNTIME, 在编译时不会丢失这个注解信息.
设置注解主体为方法和类.
注解内部保存一个displayArgs的boolean变量,用于判断是否输出传入参数.
2. 编写AOP类
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
package
org
.
jzbk
.
rssplus
.
aspect
;
import
org
.
aspectj
.
lang
.
ProceedingJoinPoint
;
import
org
.
aspectj
.
lang
.
Signature
;
import
org
.
aspectj
.
lang
.
annotation
.
Around
;
import
org
.
aspectj
.
lang
.
annotation
.
Aspect
;
import
org
.
aspectj
.
lang
.
annotation
.
Pointcut
;
import
org
.
aspectj
.
lang
.
reflect
.
MethodSignature
;
import
org
.
jzbk
.
rssplus
.
aspect
.
annotation
.
Timed
;
import
org
.
slf4j
.
Logger
;
import
org
.
slf4j
.
LoggerFactory
;
import
org
.
springframework
.
stereotype
.
Component
;
import
java
.
lang
.
reflect
.
Method
;
/**
* Created by Kotarou on 2017/1/11.
*/
@Aspect
@Component
public
class
TimedAOP
{
final
private
Logger
logger
=
LoggerFactory
.
getLogger
(
getClass
(
)
)
;
@Pointcut
(
"@annotation(org.jzbk.rssplus.aspect.annotation.Timed) ||
@target(org.jzbk.rssplus.aspect.annotation.Timed)"
)
public
void
annotationProcessor
(
)
{
}
@Pointcut
(
"execution(public * org.jzbk.rssplus..*.*(..))"
)
public
void
publicMethod
(
)
{
}
@Around
(
value
=
"publicMethod() && annotationProcessor()"
)
public
Object
count
(
ProceedingJoinPoint
proceedingJoinPoint
)
throws
Throwable
{
final
String
methodName
=
proceedingJoinPoint
.
getSignature
(
)
.
getName
(
)
;
Long
startTime
=
System
.
currentTimeMillis
(
)
;
Object
result
=
proceedingJoinPoint
.
proceed
(
)
;
Long
finishTime
=
System
.
currentTimeMillis
(
)
;
Signature
signature
=
proceedingJoinPoint
.
getSignature
(
)
;
String
[
]
packageName
=
signature
.
getDeclaringTypeName
(
)
.
split
(
"\\."
)
;
StringBuilder
stringBuilder
=
new
StringBuilder
(
)
;
for
(
int
i
=
0
;
i
<
packageName
.
length
;
++
i
)
{
if
(
i
<
packageName
.
length
-
1
)
{
stringBuilder
.
append
(
packageName
[
i
]
.
substring
(
0
,
1
)
)
;
}
else
{
stringBuilder
.
append
(
packageName
[
i
]
)
;
}
stringBuilder
.
append
(
"."
)
;
}
logger
.
info
(
"Executing: "
+
stringBuilder
+
signature
.
getName
(
)
+
" took: "
+
(
finishTime
-
startTime
)
+
" ms"
)
;
Method
method
=
(
(
MethodSignature
)
proceedingJoinPoint
.
getSignature
(
)
)
.
getMethod
(
)
;
if
(
method
.
getDeclaringClass
(
)
.
isInterface
(
)
)
{
method
=
proceedingJoinPoint
.
getTarget
(
)
.
getClass
(
)
.
getDeclaredMethod
(
methodName
,
method
.
getParameterTypes
(
)
)
;
}
// 方法上的注解优先级比类上的注解高,可以覆盖类上注解的值
Timed
timed
=
null
;
if
(
method
.
isAnnotationPresent
(
Timed
.
class
)
)
{
//处理方法上的注解
timed
=
method
.
getAnnotation
(
Timed
.
class
)
;
if
(
timed
.
displayArgs
(
)
)
{
logArgs
(
proceedingJoinPoint
.
getArgs
(
)
)
;
}
}
else
{
//处理类上面的注解
Object
target
=
proceedingJoinPoint
.
getTarget
(
)
;
if
(
target
.
getClass
(
)
.
isAnnotationPresent
(
Timed
.
class
)
)
{
timed
=
target
.
getClass
(
)
.
getAnnotation
(
Timed
.
class
)
;
if
(
timed
.
displayArgs
(
)
)
{
logArgs
(
proceedingJoinPoint
.
getArgs
(
)
)
;
}
}
}
return
result
;
}
private
void
logArgs
(
Object
[
]
args
)
{
StringBuilder
stringBuilder
=
new
StringBuilder
(
)
;
for
(
int
i
=
0
;
i
<
args
.
length
;
++
i
)
{
stringBuilder
.
append
(
"["
)
;
stringBuilder
.
append
(
i
)
;
stringBuilder
.
append
(
"]: "
)
;
stringBuilder
.
append
(
args
[
i
]
.
toString
(
)
)
;
if
(
i
<
args
.
length
-
1
)
{
stringBuilder
.
append
(
", "
)
;
}
}
if
(
!
stringBuilder
.
toString
(
)
.
isEmpty
(
)
)
logger
.
info
(
"Argument List: "
+
stringBuilder
)
;
else
logger
.
info
(
"Argument List: Empty"
)
;
}
}
|
AOP的切入点为使用了Timed的方法或者类.
方法上面的注解优先级比类上面的高,可以在方法上使用注解来覆盖掉类上注解的值.
演示:
在类上面增加注解,并设置displayArgs为true
在某个方式上覆盖注解冰将displayArgs设置为false
运行tomcat,查看日志
结果和期望中的一样.
https://www.jzbk.org/archives/423.html