ssm springaop 自定義註解加日誌資料到資料庫
弄得過程中出現過好多問題。(報錯時候切入方法不正常執行,)
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.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;
@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