Spring MVC AOP通过自定义注解方式拦截Controller等实现日志管理
之前一直写.net,没玩过spring,一直没用过aop(面向切面编程)这类功能,当然不是说.net里面没有这类框架,企业库就可以微软企业库官网
开始上代码:
注解定义
package com.jiankunking.common;
import java.lang.annotation.*;
/** * @author jiankunking * @Date: 2016/8/15 * @Time: 11:09 * @annotation OperationLogger */
@Retention(RetentionPolicy.RUNTIME)//注解会在class中存在,运行时可通过反射获取
@Target(ElementType.METHOD)//目标是方法
@Documented//文档生成时,该注解将被包含在javadoc中,可去掉
public @interface OperationLogger {
/** * 模块名字 */
String modelName() default "";
/** * 操作类型 */
String option();
}
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
@interface是用来自定义注释类型的。
注释的声明用@符号后面跟上这个注释类型的名字,再后面跟上括号,括号中列出这个注释中元 素/方法的key-value对。值必须是常量。
AOP拦截部分
package com.jiankunking.common;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/** * @author jiankunking * @Date: 2016/8/15 * @Time: 11:11 * @annotation SysLogAspect */
@Aspect
@Component
public class SysLogAspect {
private static final Logger logger = Logger.getLogger(SysLogAspect.class);
/** * 定义Pointcut,Pointcut的名称,此方法不能有返回值,该方法只是一个标示 */
@Pointcut("@annotation(com.jiankunking.common.OperationLogger)")
public void controllerAspect()
{
System.out.println("我是一个切入点");
}
/** * 前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。 * @param joinPoint */
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint)
{
System.out.println("=====SysLogAspect前置通知开始=====");
//handleLog(joinPoint, null);
}
/** * 后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。 * @param joinPoint */
@AfterReturning(pointcut = "controllerAspect()")
public void doAfter(JoinPoint joinPoint)
{
System.out.println("=====SysLogAspect后置通知开始=====");
//handleLog(joinPoint, null);
}
/** * 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。 * @param joinPoint * @param e */
@AfterThrowing(value = "controllerAspect()", throwing = "e")
public void doAfter(JoinPoint joinPoint, Exception e)
{
System.out.println("=====SysLogAspect异常通知开始=====");
//handleLog(joinPoint, e);
}
/** * 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。 * @param joinPoint */
@Around("controllerAspect()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable
{
System.out.println("=====SysLogAspect 环绕通知开始=====");
//handleLog(joinPoint, null);
Object obj= joinPoint.proceed();
System.out.println("=====SysLogAspect 环绕通知结束=====");
return obj;
}
/** * 日志处理 * * @param joinPoint * @param e */
private void handleLog(JoinPoint joinPoint, Exception e)
{
try
{
//获得注解
OperationLogger logger = giveController(joinPoint);
if (logger == null)
{
return;
}
String signature = joinPoint.getSignature().toString(); // 获取目标方法签名
String methodName = signature.substring(signature.lastIndexOf(".") + 1,
signature.indexOf("("));
String longTemp = joinPoint.getStaticPart().toLongString();
String classType = joinPoint.getTarget().getClass().getName();
Class<?> clazz = Class.forName(classType);
Method[] methods = clazz.getDeclaredMethods();
System.out.println("methodName: " + methodName);
for (Method method : methods)
{
if (method.isAnnotationPresent(OperationLogger.class)
&& method.getName().equals(methodName))
{
//OpLogger logger = method.getAnnotation(OpLogger.class);
String clazzName = clazz.getName();
System.out.println("clazzName: " + clazzName + ", methodName: "
+ methodName);
}
}
} catch (Exception exp)
{
logger.error("异常信息:{}", exp);
exp.printStackTrace();
}
}
/** * 获得注解 * @param joinPoint * @return * @throws Exception */
private static OperationLogger giveController(JoinPoint joinPoint) throws Exception
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
return method.getAnnotation(OperationLogger.class);
}
return null;
}
}
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
Aspect通常用于将必要的但和业务无关的逻辑和业务逻辑分离。
Spring使用的AOP注解分为三个层次:
前提条件是在xml中放开了
<!-- 开启切面编程功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
1
2
- @Aspect放在类头上,把这个类作为一个切面。
- @Pointcut放在方法头上,定义一个可被别的方法引用的切入点表达式。
- 5种通知。
1. @Before,前置通知,放在方法头上。
2. @After,后置【finally】通知,放在方法头上。
3. @AfterReturning,后置【try】通知,放在方法头上,使用returning来引用方法返回值。
4. @AfterThrowing,后置【catch】通知,放在方法头上,使用throwing来引用抛出的异常。
5. @Around,环绕通知,放在方法头上,这个方法要决定真实的方法是否执行,而且必须有返回值。
在Maven中加入以下以依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mkyong.common</groupId>
<artifactId>spring-mvc-log4j</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>SpringMVC + Log4j</name>
<properties>
<jdk.version>1.7</jdk.version>
<spring.version>4.3.2.RELEASE</spring.version>
<log4j.version>2.6.2</log4j.version>
<jstl.version>1.2</jstl.version>
<servletapi.version>3.1.0</servletapi.version>
<org.aspectj-version>1.7.4</org.aspectj-version>
<cglib.version>3.1</cglib.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Log4j start-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- Log4j end-->
<!-- jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servletapi.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
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
104
105
106
在spring-*.xml中加入spring支持,打开aop功能
<?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:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd ">
<!-- aop -->
<bean id="logService" class="com.jiankunking.common.SysLogAspect"></bean>
<!--Spring MVC使用ViewResolver来根据controller中返回的view名关联到具体的View对象。使用View对象来渲染返回值以生成最终的视图,如html,json或pdf等-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 启用MVC注解 -->
<mvc:annotation-driven/>
<!-- 指定Sping组件扫描的基本包路径 -->
<context:component-scan base-package="com.jiankunking.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--静态文件处理-->
<mvc:resources location="/resources/" mapping="/resources/**"/>
</beans>
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
注解也写好了,spring也配置好了,在controller里面怎么用呢?
Controller应用
package com.jiankunking.controller;
import com.jiankunking.common.OperationLogger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping(value = "/Welcome", produces = "text/html;charset=UTF-8")
public class WelcomeController
{
@OperationLogger(modelName = "WelcomeController", option = "getWelcome")
@RequestMapping(value = "/getWelcome", method = RequestMethod.POST)
public void getWelcome()
{
//异常拦截测试
//int i = 9 / 0;
System.out.println("controller方法执行!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
如何测试呢?
从前端发起ajax请求controller,即可:
$.ajax({
type: "POST",
url: "/Welcome/getWelcome",
contentType: "application/json",
data: null,
success: function () {
// alert("22");
},
error: function () {
// alert("失败!");
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
效果如下:
由于这里是通过Spring的@Aspect注解实现的AOP,所以同一个类中的某个方法A(该方法没有注解标识)调用另一个有注解标识的方法B时,方法B上的注解是不会起作用的。
演示demo:
http://download.csdn.net/detail/xunzaosiyecao/9609918
xml方式实现aop拦截及aop基础知识参考:
xml实现aop拦截
Spring Boot 自定义注解方式拦截Controller等实现日志管理:
Spring Boot 自定义注解
还没有评论,来说两句吧...