弄得过程中出现过好多问题。(报错时候切入方法不正常执行,)
1、需要导的包:slf4j-log4j12-1.6.2.jar slf4j-api-1.6.2.jar (这两个jar包版本最好保持一致,之前我使用不同版本报错)
aspectjweaver-1.6.8_2.jar log4j-1.2.15.jar aopalliance-1.0.jar
2、新建一个log.java 自定义注解类
package com.user.sessionFilter;
import java.lang.annotation.*;
//https://blog.csdn.net/erlian1992/article/details/53402519
//自定义注解 相关知识参考上面链接
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
//自定义注解
/** 要执行的操作类型比如:add操作 **/
public String operationType() default "";
/** 要执行的具体操作比如:添加用户 **/
public String operationName() default "";
}
3、新建一个SystemLogAspect .java
执行顺序如下:
//正常 device : -->around-->before-->method-->after-->afterReturn
//报错 device : -->around-->before-->method-->after-->afterthrowing
package com.user.sessionFilter;
import java.lang.reflect.Method;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.user.entity.SystemLog;
import com.user.entity.User;
import com.user.service.SystemLogService;
import org.springframework.web.servlet.ModelAndView;
@Aspect
@Component
public class SystemLogAspect {
//正常 device : -->around-->before-->method-->after-->afterReturn
//报错 device : -->around-->before-->method-->after-->afterthrowing
//注入Service用于把日志保存数据库
@Autowired //这里我用resource注解,一般用的是@Autowired,他们的区别如有时间我会在后面的博客中来写
private SystemLogService systemLogService;
@Autowired
WebContextHolder webContextHolder;
private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
//Controller层切点
@Pointcut("execution(* com.user.service.AopService.*(..))")
//@Pointcut("execution(* com.user.action.AopController.*(..))")
public void controllerAspect() {
}
/* joinPoint.getArgs();//輸入的參數列表
joinPoint.getTarget().getClass().getName();//類全路徑
joinPoint.getSignature().getDeclaringTypeName();//接口全路徑
joinPoint.getSignature().getName();//調用的方法
*/ /**
* 前置通知 用于拦截Controller层记录用户的操作
*
* @param joinPoint 切点
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint) {
System.out.println("==========执行controller前置通知===============");
if (logger.isInfoEnabled()) {
logger.info("before " + joinPoint);
logger.info("before start");
joinPoint.getArgs();//輸入的參數列表
joinPoint.getTarget().getClass().getName();//類全路徑
joinPoint.getSignature().getDeclaringTypeName();//接口全路徑
joinPoint.getSignature().getName();//調用的方法
logger.info("before end");
}
//配置controller环绕通知,使用在方法aspect()上注册的切入点
@Around("controllerAspect()")
public void around(ProceedingJoinPoint pjp) {
System.out.println("==========开始执行controller环绕通知===============");
long start = System.currentTimeMillis();
try {
pjp.proceed();
// ((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
if (logger.isInfoEnabled()) {
logger.info("around " + pjp + "\tUse time : " + (end - start) + " ms!");
}
System.out.println("==========结束执行controller环绕通知===============");
} catch (Throwable e) {
long end = System.currentTimeMillis();
if (logger.isInfoEnabled()) {
logger.info("around " + pjp + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
}
}
}
/**
*
*Project:my
*Description:
*@author Krystal
* 2018年5月13日上午2:43:47
* @param joinPoint
*/
@After("controllerAspect()")
public void after(JoinPoint joinPoint) {
System.out.println("=====after=====");
}
//配置后置返回通知,使用在方法aspect()上注册的切入点
@AfterReturning("controllerAspect()")
public void afterReturn(JoinPoint joinPoint) {
HttpServletRequest request = webContextHolder.getRequest();
//读取session中的用户
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
//请求的IP
String ip = request.getRemoteAddr();
// User user =new User();
// user.setId(1);
// user.setName("张三");
// String ip = "127.0.0.1";
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 operationType = "";
String operationName = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
operationType = method.getAnnotation(Log.class).operationType();
operationName = method.getAnnotation(Log.class).operationName();
break;
}
}
}
//*========控制台输出=========*//
System.out.println("=====controller后置通知开始=====");
System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
System.out.println("方法描述:" + operationName);
System.out.println("请求人:" + user.getName());
System.out.println("请求IP:" + ip);
//*========数据库日志=========*//
SystemLog log = new SystemLog();
log.setDescription(operationName);
log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
log.setRequestIp(ip);
log.setExceptioncode("Exceptioncode");
log.setExceptionDetail("ExceptionDetail");
log.setParams("setParams");
log.setCreateBy(user.getName());
log.setCreateDate(new Date());
//保存数据库
systemLogService.insertSystemLog(log);
System.out.println("=====controller后置通知结束=====");
} catch(Exception e){
//记录本地异常日志
logger.error("==后置通知异常==");
logger.error("异常信息:{}", e.getMessage());
}
}
/**
* 异常通知 用于拦截记录异常日志
*
* @param joinPoint
* @param e
*/
@AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
// HttpSession session = request.getSession();
//读取session中的用户
// User user = (User) session.getAttribute("user");
// User user = (User) request.getSession().getAttribute("user");
//获取请求ip
//获取用户请求方法的参数并序列化为JSON格式字符串
// HttpServletRequest request = webContextHolder.getRequest();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//读取session中的用户
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
String ip = request.getRemoteAddr();
//User user = new User();
// user.setId(1);
// user.setName("张三");
// String ip = "127.0.0.1";
String params = "";
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0){
for (int i = 0; i < joinPoint.getArgs().length; i++) {
//params += JsonUtil.getJsonStr(joinPoint.getArgs()[i]) + ";";
}
}
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 operationType = "";
String operationName = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
operationType = method.getAnnotation(Log.class).operationType();
operationName = method.getAnnotation(Log.class).operationName();
break;
}
}
}
/*========控制台输出=========*/
System.out.println("=====异常通知开始=====");
System.out.println("异常代码:" + e.getClass().getName());
System.out.println("异常信息:" + e.getMessage());
System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
System.out.println("方法描述:" + operationName);
System.out.println("请求人:" + user.getName());
System.out.println("请求IP:" + ip);
System.out.println("请求参数:" + params);
/*==========数据库日志=========*/
SystemLog log = new SystemLog();
log.setDescription(operationName);
log.setExceptioncode(e.getClass().getName());
log.setExceptionDetail(e.getMessage());
log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
log.setParams(params);
log.setCreateBy(user.getName());
log.setCreateDate(new Date());
log.setRequestIp(ip);
//保存数据库
systemLogService.insertSystemLog(log);
System.out.println("=====异常通知结束=====");
} catch(Exception ex){
//记录本地异常日志
logger.error("==异常通知异常==");
logger.error("异常信息:{}", ex.getMessage());
}
/*==========记录本地异常日志==========*/
logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName());
}
}
4、controller---》service
@Controller
@RequestMapping("SystemLog")
public class AopController {
@Autowired
private AopService userService;
@RequestMapping("testAOP")
// @Log(operationType="addOperation:",operationName="添加用户")
public ModelAndView testAOP(HttpServletRequest request, HttpServletResponse response, Model model) {
String success = "0";
try {
User user = new User();
user.setName("sy");
user.setPsd("22");
success = userService.insertUser(user);
System.out.println("dsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsd");
} catch (Exception e) {
success = "0";
}
if ("0".equals(success)) {
return new ModelAndView("user/aopFail");
}
return new ModelAndView("user/aopSuccess");
}
service:(具体切入方法在此)
operationType与operationName和log.java中定义的一致
@Log(operationType="addOperation:",operationName="添加用户正常")
public String insertUser(User user){
iUserMapper.insertUser(user);
return "1";
}
5、springMvc.xml配置 红色为配置
<context:component-scan base-package="com.user" >
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> -->
</context:component-scan>
<aop:aspectj-autoproxy />
<!-- <aop:aspectj-autoproxy proxy-target-class="true" /> -->
<!-- </aop:aspectj-autoproxy> -->
<!--开启aopproxy-target-class="true"默认是false,更改为true时使用的是cglib动态代理,这样只能实现对Controller层的日志记录
proxy-target-class等于true是强制使用cglib代理,proxy-target-class默认是false,如果你的类实现了接口 就走JDK代理,如果没有,走cglib代理
注:对于单利模式建议使用cglib代理,虽然JDK动态代理比cglib代理速度快,但性能不如cglib
https://blog.csdn.net/bobozai86/article/details/79229455 -->
6、最后附上表
CREATE TABLE `systemlog` (
`id` int(20) NOT NULL auto_increment,
`description` varchar(100) default NULL,
`method` varchar(100) default NULL,
`requestIp` varchar(100) default NULL,
`exceptioncode` varchar(100) default NULL,
`exceptionDetail` varchar(100) default NULL,
`params` varchar(100) default NULL,
`createBy` varchar(100) default NULL,
`createDate` timestamp NULL default NULL COMMENT '系统日志',
PRIMARY KEY (`id`),
KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8