Spring切面简记及应用

概念

面向切面编程:把逻辑代码和处理琐碎事务的代码分离开,以便能够分离复杂度。

切面(AOP)术语
  1. 连接点(Joinpoint)
  2. 切点(Pointcut)
  3. 增强(Advice)
    • Before advice
    • After returning advice
    • After throwing advice
    • After(finally) advice
    • Around advice
  4. 目标对象(Target)
  5. 引入(Introduction)
  6. 织入(Weaving)
  7. 代理(Proxy)
  8. 切面(Aspect)
优点
  • 更清晰的代码逻辑,业务逻辑只关注自己本身,不用去管琐碎的事情,比如:安全,日志,事务等等。

  • 可以减少代码量

切面配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 启动注解驱动 -->
<mvc:annotation-driven/>
<!-- 扫描指定包下的类,并注册相应组件 -->
<context:component-scan base-package="com.aspect"/>
<!-- 开启动态代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
切面应用:日志

注解定义

1
2
3
4
5
6
7
8
9
10
package com.aspect.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ControllerLog {
String description() 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.aspect.aspect;
import com.aspect.annotation.ControllerLog;
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.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Aspect
@Component
public class SystemLogAspect {
@Pointcut("@annotation(com.aspect.annotation.ControllerLog)")
public void controllerAspect(){
}
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint){
System.out.println("------defore------");
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String ip = request.getRemoteAddr();
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = null;
try {
targetClass = Class.forName(targetName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method[] methods = targetClass.getMethods();
String description = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
description = method.getAnnotation(ControllerLog.class).description();
break;
}
}
}
System.out.println(ip+" | "+description);
}
@After("@annotation(com.aspect.annotation.ControllerLog)")
public void deAfter(JoinPoint joinPoint){
System.out.println("------after------");
}
}

使用切面注解

1
2
3
4
5
6
7
8
9
10
11
12
@Controller
@RequestMapping("/hello")
public class TestServlet {
@RequestMapping(value = "/user",method = RequestMethod.GET,produces = "application/json;charset=utf-8")
@ControllerLog(description = "welcome user")
@ResponseBody
public String getMessage(@RequestParam("name") String value){
System.out.println(value);
String output="Hello "+value;
return output;
}
}

测试结果

1
2
3
------defore------
0:0:0:0:0:0:0:1 | welcome user
------after------
切面应用:计算方法调用耗时

注解定义

1
2
3
4
5
6
7
8
9
package com.aspect.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExecuteTimeAnnotation {
}

切面定义

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
package com.aspect.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.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@Aspect
@Component
public class ExecuteTimeLogAspect {
@Pointcut("@annotation(com.aspect.annotation.ExecuteTimeAnnotation)")
public void executeTimeAspect(){
}
@Around(value = "executeTimeAspect()")
public Object executeTime(ProceedingJoinPoint joinPoint) throws Throwable {
Signature signature=joinPoint.getSignature();
MethodSignature methodSignature=(MethodSignature)signature;
StopWatch stopWatch=new StopWatch(String.format("方法名: %s",methodSignature.getName()));
System.out.println("StopWatch start");
stopWatch.start();
Object ret=joinPoint.proceed();
stopWatch.stop();
System.out.println(stopWatch.toString());
return ret;
}
}

使用切面注解

1
2
3
4
5
6
7
8
9
10
11
12
@Controller
@RequestMapping("/hello")
public class TestServlet {
@RequestMapping(value = "/user",method = RequestMethod.GET,produces = "application/json;charset=utf-8")
@ExecuteTimeAnnotation
@ResponseBody
public String getMessage(@RequestParam("name") String value){
System.out.println(value);
String output="Hello "+value;
return output;
}
}

测试结果

1
2
StopWatch start
StopWatch '方法名: getMessage': running time (millis) = 14; [] took 14 = 100%

注意点:

当同时以上两个切面时,输出结果为

1
2
3
4
5
StopWatch start
------defore------
0:0:0:0:0:0:0:1 | welcome user
------after------
StopWatch '方法名: getMessage': running time (millis) = 12; [] took 12 = 100%

可以发现以@Around的joinPoint.proceed();为分界,进入该方法时调用@Before,退出该方法时调用@After。

参考文献:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html