SpringMVC利用AOP实现自定义注解记录日志
本文抛砖引玉,并没有详细的介绍更全面的内容,通过一个例子让初次使用的人能够快速入门,简单的介绍一下。
第一 ,注解 :
-
@Before – 目标方法执行前执行
-
@After – 目标方法执行后执行
-
@AfterReturning – 目标方法返回后执行,如果发生异常不执行
-
@AfterThrowing – 异常时执行
-
@Around – 在执行上面其他操作的同时也执行这个方法
第二,SpringMVC如果要使用AOP注解,必须将
<aop:aspectj-autoproxy proxy-target-class="true"/>
放在spring-servlet.xml(配置MVC的XML)中
第三,execution表达式请参考Spring官网http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
代码下载:http://pan.baidu.com/s/1gdeopW3
项目截图
首先是Maven依赖
<properties> <springframework>4.0.5.RELEASE</springframework> <aspectj>1.8.5</aspectj> <servlet>3.1.0</servlet> </properties> <dependencies> <!-- servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${servlet}</version> <scope>compile</scope> </dependency> <!-- Spring web mvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${springframework}</version> </dependency> <!-- Spring AOP --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${springframework}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${aspectj}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj}</version> </dependency> </dependencies>
spring-context.xml配置,基本无内容
<!-- 配置扫描路径 --> <context:component-scan base-package="org.xdemo.example.springaop"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan>
spring-mvc.xml配置
<!-- 最重要:::如果放在spring-context.xml中,这里的aop设置将不会生效 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <!-- 启用MVC注解 --> <mvc:annotation-driven /> <!-- 指定Sping组件扫描的基本包路径 --> <context:component-scan base-package="org.xdemo.example.springaop"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
Web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>Archetype Created Web Application</display-name> <!-- WebAppRootKey --> <context-param> <param-name>webAppRootKey</param-name> <param-value>org.xdemo.example.springaop</param-value> </context-param> <!-- Spring Context --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-context.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- SpringMVC --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
注解Log
package org.xdemo.example.springaop.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface Log { String name() default ""; }
日志AOP,写法一LogAop_1
package org.xdemo.example.springaop.aop; import java.lang.reflect.Method; import java.util.UUID; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import org.xdemo.example.springaop.annotation.Log; @Aspect @Component public class LogAop_1 { ThreadLocal<Long> time=new ThreadLocal<Long>(); ThreadLocal<String> tag=new ThreadLocal<String>(); /** * 在所有标注@Log的地方切入 * @param joinPoint */ @Before("@annotation(org.xdemo.example.springaop.annotation.Log)") public void beforeExec(JoinPoint joinPoint){ time.set(System.currentTimeMillis()); tag.set(UUID.randomUUID().toString()); info(joinPoint); MethodSignature ms=(MethodSignature) joinPoint.getSignature(); Method method=ms.getMethod(); System.out.println(method.getAnnotation(Log.class).name()+"标记"+tag.get()); } @After("@annotation(org.xdemo.example.springaop.annotation.Log)") public void afterExec(JoinPoint joinPoint){ MethodSignature ms=(MethodSignature) joinPoint.getSignature(); Method method=ms.getMethod(); System.out.println("标记为"+tag.get()+"的方法"+method.getName()+"运行消耗"+(System.currentTimeMillis()-time.get())+"ms"); } @Around("@annotation(org.xdemo.example.springaop.annotation.Log)") public void aroundExec(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("我是Around,来打酱油的"); pjp.proceed(); } private void info(JoinPoint joinPoint){ System.out.println("--------------------------------------------------"); System.out.println("King:\t"+joinPoint.getKind()); System.out.println("Target:\t"+joinPoint.getTarget().toString()); Object[] os=joinPoint.getArgs(); System.out.println("Args:"); for(int i=0;i<os.length;i++){ System.out.println("\t==>参数["+i+"]:\t"+os[i].toString()); } System.out.println("Signature:\t"+joinPoint.getSignature()); System.out.println("SourceLocation:\t"+joinPoint.getSourceLocation()); System.out.println("StaticPart:\t"+joinPoint.getStaticPart()); System.out.println("--------------------------------------------------"); } }
日志AOP,写法二LogAop_2
package org.xdemo.example.springaop.aop; import java.lang.reflect.Method; import java.util.UUID; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import org.xdemo.example.springaop.annotation.Log; @Aspect @Component public class LogAop_2 { ThreadLocal<Long> time=new ThreadLocal<Long>(); ThreadLocal<String> tag=new ThreadLocal<String>(); @Pointcut("@annotation(org.xdemo.example.springaop.annotation.Log)") public void log(){ System.out.println("我是一个切入点"); } /** * 在所有标注@Log的地方切入 * @param joinPoint */ @Before("log()") public void beforeExec(JoinPoint joinPoint){ time.set(System.currentTimeMillis()); tag.set(UUID.randomUUID().toString()); info(joinPoint); MethodSignature ms=(MethodSignature) joinPoint.getSignature(); Method method=ms.getMethod(); System.out.println(method.getAnnotation(Log.class).name()+"标记"+tag.get()); } @After("log()") public void afterExec(JoinPoint joinPoint){ MethodSignature ms=(MethodSignature) joinPoint.getSignature(); Method method=ms.getMethod(); System.out.println("标记为"+tag.get()+"的方法"+method.getName()+"运行消耗"+(System.currentTimeMillis()-time.get())+"ms"); } @Around("log()") public void aroundExec(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("我是Around,来打酱油的"); pjp.proceed(); } private void info(JoinPoint joinPoint){ System.out.println("--------------------------------------------------"); System.out.println("King:\t"+joinPoint.getKind()); System.out.println("Target:\t"+joinPoint.getTarget().toString()); Object[] os=joinPoint.getArgs(); System.out.println("Args:"); for(int i=0;i<os.length;i++){ System.out.println("\t==>参数["+i+"]:\t"+os[i].toString()); } System.out.println("Signature:\t"+joinPoint.getSignature()); System.out.println("SourceLocation:\t"+joinPoint.getSourceLocation()); System.out.println("StaticPart:\t"+joinPoint.getStaticPart()); System.out.println("--------------------------------------------------"); } }
用到的一个用户类User
package org.xdemo.example.springaop.bean; public class User { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
一个测试的Controller
package org.xdemo.example.springaop.controller; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.xdemo.example.springaop.annotation.Log; import org.xdemo.example.springaop.bean.User; import org.xdemo.example.springaop.service.IUserService; @Controller @RequestMapping("/aop") public class SpringController { @Resource IUserService userService; @Log(name="您访问了aop1方法") @ResponseBody @RequestMapping(value="aop1") public String aop1(){ return "AOP"; } @Log(name="您访问了aop2方法") @ResponseBody @RequestMapping(value="aop2") public String aop2(String string) throws InterruptedException{ Thread.sleep(1000L); User user=new User(); user.setName(string); userService.save(user); return string; } }
一个测试的接口实现类(接口类略)
package org.xdemo.example.springaop.service; import org.springframework.stereotype.Service; import org.xdemo.example.springaop.annotation.Log; import org.xdemo.example.springaop.bean.User; @Service public class UserServiceImpl implements IUserService { @Log(name = "您访问了保存用户信息") public void save(User user) { System.out.println(user.getName()); } }
在地址栏输入地址测试:http://localhost:8080/springaop/aop/aop2?string=sxxxxx
结果如下: