介绍:现在做的项目,需要通过aop实现添加日志功能,在需要的方法上加注解即可对该方法执行拦截并添加日志
1. 添加相关依赖
<spring.version>4.1.7.RELEASE</spring.version> <aspectj.version>1.6.8</aspectj.version> <!—springaop相关—> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</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> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <!—aspectj相关—> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency>2.编写自定义注解类
package com.sen.log; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /**方法描述*/ public String methodDesc() default ""; } 3.编写日志实体类,用于保存到数据库 package com.sen.pojo; import java.util.Date; /** * ClassName: SystemLog * * @Description: 日志实体类 * @author shaosen * @date 2018年5月9日 */ public class SystemLog { /**唯一标识*/ private String id; /**方法描述*/ private String description; /**方法名*/ private String method; /**请求ip*/ private String requestIp; /**异常code*/ private String exceptioncode; /**异常描述*/ private String exceptionDetail; /**创建日期*/ private Date createDate; /**请求参数*/ private String params; public String getParams() { return params; } public void setParams(String params) { this.params = params; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public String getRequestIp() { return requestIp; } public void setRequestIp(String requestIp) { this.requestIp = requestIp; } public String getExceptioncode() { return exceptioncode; } public void setExceptioncode(String exceptioncode) { this.exceptioncode = exceptioncode; } public String getExceptionDetail() { return exceptionDetail; } public void setExceptionDetail(String exceptionDetail) { this.exceptionDetail = exceptionDetail; } public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } @Override public String toString() { return "SystemLog [id=" + id + ", description=" + description + ", method=" + method + ", requestIp=" + requestIp + ", exceptioncode=" + exceptioncode + ", exceptionDetail=" + exceptionDetail + ", createDate=" + createDate + "]"; } } 4.编写日志切面 package com.sen.log; import java.lang.reflect.Method; import java.util.Date; import java.util.UUID; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import com.sen.pojo.SystemLog; /** * ClassName: LogAdvice * * @Description: 日志切面 * @author shaosen * @date 2018年5月9日 */ @Aspect @Component public class LogAdvice { private Logger log = Logger.getLogger(LogAdvice.class); /** 配置切入点 */ @Pointcut("execution(* com.sen.controller..*.*(..))") public void controllerAspect() { } /** * @Description: 后置通知 * @param @param joinPoint * @param @throws ClassNotFoundException * @return void * @throws @author shaosen * @date 2018年5月9日 */ @After("controllerAspect()") public void after(JoinPoint joinPoint) { try { String ip = "127.0.0.1"; String username = "peter"; String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String methodDesc = ""; String params = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { if (!method.isAnnotationPresent(Log.class)) return; // 获取注解字段 methodDesc = method.getAnnotation(Log.class).methodDesc(); // 获取参数 for (int i = 0; i < arguments.length; i++) { params = params + arguments[i] + (i == arguments.length - 1 ? "" : ","); } break; } } } // 控制台输出 log.info("*******Log start*******"); log.info("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); log.info("方法描述:" + methodDesc); log.info("请求人:" + username); log.info("请求IP:" + ip); log.info("请求参数:" + params); // 写入数据库 log.info("*写入数据库*"); SystemLog syslog = new SystemLog(); syslog.setId(UUID.randomUUID().toString().replaceAll("-", "")); syslog.setMethod(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"); syslog.setDescription(methodDesc); syslog.setRequestIp(ip); syslog.setExceptioncode(null); syslog.setExceptionDetail(null); syslog.setCreateDate(new Date()); syslog.setParams(params); // TODO log.info("*写入数据库完成*"); log.info("*******Log end*******"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @Description: 异常通知 * @param @param joinPoint * @param @param e * @return void * @throws ClassNotFoundException * @throws @author shaosen * @date 2018年5月9日 */ @AfterThrowing(pointcut = "controllerAspect()", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { /* * HttpServletRequest request = ((ServletRequestAttributes) * RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession * session = request.getSession(); //读取session中的用户 User user = (User) * session.getAttribute(WebConstants.CURRENT_USER); //获取请求ip String ip = * request.getRemoteAddr(); */ // 获取用户请求方法的参数并序列化为JSON格式字符串 String ip = "127.0.0.1"; String username = "peter"; try { String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String methodDesc = ""; String params = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { if (!method.isAnnotationPresent(Log.class)) return; methodDesc = method.getAnnotation(Log.class).methodDesc(); // 获取参数 for (int i = 0; i < arguments.length; i++) { params = params + arguments[i] + (i == arguments.length - 1 ? "" : ","); } break; } } } // 控制台数据 log.info("*******ExceptionLog start*******"); log.info("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); log.info("方法描述:" + methodDesc); log.info("异常代码:" + e.getClass().getName()); log.info("异常信息:" + e.getMessage()); log.info("请求人:" + username); log.info("请求IP:" + ip); log.info("请求参数:" + params); // 写入数据库 log.info("*写入数据库*"); SystemLog syslog = new SystemLog(); syslog.setId(UUID.randomUUID().toString().replaceAll("-", "")); syslog.setMethod( joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"); syslog.setDescription(methodDesc); syslog.setRequestIp(ip); syslog.setExceptioncode(e.getClass().getName()); syslog.setExceptionDetail(e.getMessage()); syslog.setCreateDate(new Date()); syslog.setParams(params); // save log.info("*写入数据库完成*"); log.info("*******ExceptionLog end*******"); } catch (ClassNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (SecurityException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }6.spring配置文件,需要开启aop注解扫描
<!-- 开启aop包扫描 --> <aop:aspectj-autoproxy proxy-target-class="true"/>7.controller如下
package com.sen.controller; import org.springframework.stereotype.Controller; import com.sen.log.Log; import com.sen.log.MyLog; @Controller public class UserController { // private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(UserController.class); @Log(methodDesc="添加用户") public void addUser(String username,String password) { System.out.println("用户保存了。。。"); // MyLog.info("test save"); } @Log(methodDesc="查询用户") public void findUser(String id) { int i = 1/0; System.out.println("查询用户。。。"); } public void deleteUser() { System.out.println("user already deleted..."); } }8.编写测试类
package com.sen.test; import org.apache.log4j.Logger; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.sen.controller.UserController; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:spring/spring*") public class LogTest { @Autowired private UserController userController; @Test public void test() { userController.addUser("zhangsan","18"); userController.findUser("11"); userController.deleteUser(); } }9.测试结果如下:
16:58:00,964 INFO DefaultTestContextBootstrapper:256 - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] 16:58:00,980 INFO DefaultTestContextBootstrapper:182 - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@29e54f45, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@db75080, org.springframework.test.context.support.DirtiesContextTestExecutionListener@7e060a88, org.springframework.test.context.transaction.TransactionalTestExecutionListener@3e9da75b, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@6056677a] 16:58:01,083 INFO XmlBeanDefinitionReader:317 - Loading XML bean definitions from file [D:\workspace\aoptest\target\classes\spring\spring-core.xml] 16:58:02,662 INFO XmlBeanDefinitionReader:317 - Loading XML bean definitions from file [D:\workspace\aoptest\target\classes\spring\spring-mvc.xml] 16:58:02,691 INFO GenericApplicationContext:510 - Refreshing org.springframework.context.support.GenericApplicationContext@129e76f3: startup date [Fri May 11 16:58:02 CST 2018]; root of context hierarchy 用户保存了。。。 16:58:03,038 INFO LogAdvice:76 - *******Log start******* 16:58:03,038 INFO LogAdvice:77 - 请求方法:com.sen.controller.UserController.addUser() 16:58:03,038 INFO LogAdvice:79 - 方法描述:添加用户 16:58:03,038 INFO LogAdvice:80 - 请求人:peter 16:58:03,039 INFO LogAdvice:81 - 请求IP:127.0.0.1 16:58:03,039 INFO LogAdvice:82 - 请求参数:zhangsan,18 16:58:03,039 INFO LogAdvice:85 - *写入数据库* 16:58:03,050 INFO LogAdvice:97 - *写入数据库完成* 16:58:03,050 INFO LogAdvice:98 - *******Log end******* 16:58:03,051 INFO LogAdvice:76 - *******Log start******* 16:58:03,051 INFO LogAdvice:77 - 请求方法:com.sen.controller.UserController.findUser() 16:58:03,052 INFO LogAdvice:79 - 方法描述:查询用户 16:58:03,052 INFO LogAdvice:80 - 请求人:peter 16:58:03,052 INFO LogAdvice:81 - 请求IP:127.0.0.1 16:58:03,052 INFO LogAdvice:82 - 请求参数:11 16:58:03,052 INFO LogAdvice:85 - *写入数据库* 16:58:03,052 INFO LogAdvice:97 - *写入数据库完成* 16:58:03,053 INFO LogAdvice:98 - *******Log end******* 16:58:03,053 INFO LogAdvice:159 - *******ExceptionLog start******* 16:58:03,053 INFO LogAdvice:160 - 请求方法:com.sen.controller.UserController.findUser() 16:58:03,053 INFO LogAdvice:162 - 方法描述:查询用户 16:58:03,053 INFO LogAdvice:163 - 异常代码:java.lang.ArithmeticException 16:58:03,053 INFO LogAdvice:164 - 异常信息:/ by zero 16:58:03,053 INFO LogAdvice:165 - 请求人:peter 16:58:03,053 INFO LogAdvice:166 - 请求IP:127.0.0.1 16:58:03,053 INFO LogAdvice:167 - 请求参数:11 16:58:03,054 INFO LogAdvice:170 - *写入数据库* 16:58:03,054 INFO LogAdvice:183 - *写入数据库完成* 16:58:03,054 INFO LogAdvice:184 - *******ExceptionLog end******* 16:58:03,081 INFO GenericApplicationContext:862 - Closing org.springframework.context.support.GenericApplicationContext@129e76f3: startup date [Fri May 11 16:58:02 CST 2018]; root of context hierarchy注意:使用AOP拦截不到SpringMVC的Controller层的解决方法
最后找到一种方法, 将Controller的代理权交给cglib, 解决了这个问题,如下: ApplicatonContext.xml中启动@AspectJ注解支持springMVC.xml中通知spring使用cglib而不是jdk来生成代理方法 <!-- 启动对@AspectJ注解的支持 --> < aop:aspectj-autoproxy /> <!-- 通知spring使用 cglib 而不是 jdk 的来生成代理方法,这样 AOP可以拦截到Controller --> < aop:aspectj-autoproxy proxy-target-class = "true" />