介绍:现在做的项目,需要通过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"
/>