spring AOP自定義注解方式實(shí)現(xiàn)日志管理的實(shí)例講解
今天繼續(xù)實(shí)現(xiàn)AOP,到這里我個人認(rèn)為是最靈活,可擴(kuò)展的方式了,就拿日志管理來說,用Spring AOP 自定義注解形式實(shí)現(xiàn)日志管理。廢話不多說,直接開始?。?!
關(guān)于配置我還是的再說一遍。
在applicationContext-mvc.xml中要添加的
<mvc:annotation-driven /> <!-- 激活組件掃描功能,在包c(diǎn)om.gcx及其子包下面自動掃描通過注解配置的組件 --> <context:component-scan base-package="com.gcx" /> <!-- 啟動對@AspectJ注解的支持 --> <!-- proxy-target-class等于true是強(qiáng)制使用cglib代理,proxy-target-class默認(rèn)是false,如果你的類實(shí)現(xiàn)了接口 就走JDK代理,如果沒有,走cglib代理 --> <!-- 注:對于單利模式建議使用cglib代理,雖然JDK動態(tài)代理比cglib代理速度快,但性能不如cglib --> <!--如果不寫proxy-target-class="true"這句話也沒問題--> <aop:aspectj-autoproxy proxy-target-class="true"/> <!--切面--> <bean id="systemLogAspect" class="com.gcx.annotation.SystemLogAspect"></bean>
接下來開始編寫代碼。
創(chuàng)建日志類實(shí)體
public class SystemLog {
private String id;
private String description;
private String method;
private Long logType;
private String requestIp;
private String exceptioncode;
private String exceptionDetail;
private String params;
private String createBy;
private Date createDate;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id == null ? null : id.trim();
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description == null ? null : description.trim();
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method == null ? null : method.trim();
}
public Long getLogType() {
return logType;
}
public void setLogType(Long logType) {
this.logType = logType;
}
public String getRequestIp() {
return requestIp;
}
public void setRequestIp(String requestIp) {
this.requestIp = requestIp == null ? null : requestIp.trim();
}
public String getExceptioncode() {
return exceptioncode;
}
public void setExceptioncode(String exceptioncode) {
this.exceptioncode = exceptioncode == null ? null : exceptioncode.trim();
}
public String getExceptionDetail() {
return exceptionDetail;
}
public void setExceptionDetail(String exceptionDetail) {
this.exceptionDetail = exceptionDetail == null ? null : exceptionDetail.trim();
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params == null ? null : params.trim();
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy == null ? null : createBy.trim();
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
}
編寫dao接口
package com.gcx.dao;
import com.gcx.entity.SystemLog;
public interface SystemLogMapper {
int deleteByPrimaryKey(String id);
int insert(SystemLog record);
int insertSelective(SystemLog record);
SystemLog selectByPrimaryKey(String id);
int updateByPrimaryKeySelective(SystemLog record);
int updateByPrimaryKey(SystemLog record);
}
編寫service層
package com.gcx.service;
import com.gcx.entity.SystemLog;
public interface SystemLogService {
int deleteSystemLog(String id);
int insert(SystemLog record);
int insertTest(SystemLog record);
SystemLog selectSystemLog(String id);
int updateSystemLog(SystemLog record);
}
編寫service實(shí)現(xiàn)類serviceImpl
package com.gcx.service.impl;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.gcx.annotation.Log;
import com.gcx.dao.SystemLogMapper;
import com.gcx.entity.SystemLog;
import com.gcx.service.SystemLogService;
@Service("systemLogService")
public class SystemLogServiceImpl implements SystemLogService {
@Resource
private SystemLogMapper systemLogMapper;
@Override
public int deleteSystemLog(String id) {
return systemLogMapper.deleteByPrimaryKey(id);
}
@Override
public int insert(SystemLog record) {
return systemLogMapper.insertSelective(record);
}
@Override
public SystemLog selectSystemLog(String id) {
return systemLogMapper.selectByPrimaryKey(id);
}
@Override
public int updateSystemLog(SystemLog record) {
return systemLogMapper.updateByPrimaryKeySelective(record);
}
@Override
public int insertTest(SystemLog record) {
return systemLogMapper.insert(record);
}
}
到這里基本程序編寫完畢
下面開始自定義注解
package com.gcx.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/** 要執(zhí)行的操作類型比如:add操作 **/
public String operationType() default "";
/** 要執(zhí)行的具體操作比如:添加用戶 **/
public String operationName() default "";
}
下面編寫切面
package com.gcx.annotation;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.UUID;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
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.stereotype.Component;
import com.gcx.entity.SystemLog;
import com.gcx.entity.User;
import com.gcx.service.SystemLogService;
import com.gcx.util.JsonUtil;
/**
* @author 楊建
* @E-mail: email
* @version 創(chuàng)建時間:2015-10-19 下午4:29:05
* @desc 切點(diǎn)類
*/
@Aspect
@Component
public class SystemLogAspect {
//注入Service用于把日志保存數(shù)據(jù)庫
@Resource //這里我用resource注解,一般用的是@Autowired,他們的區(qū)別如有時間我會在后面的博客中來寫
private SystemLogService systemLogService;
private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class);
//Controller層切點(diǎn)
@Pointcut("execution (* com.gcx.controller..*.*(..))")
public void controllerAspect() {
}
/**
* 前置通知 用于攔截Controller層記錄用戶的操作
*
* @param joinPoint 切點(diǎn)
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint) {
System.out.println("==========執(zhí)行controller前置通知===============");
if(logger.isInfoEnabled()){
logger.info("before " + joinPoint);
}
}
//配置controller環(huán)繞通知,使用在方法aspect()上注冊的切入點(diǎn)
@Around("controllerAspect()")
public void around(JoinPoint joinPoint){
System.out.println("==========開始執(zhí)行controller環(huán)繞通知===============");
long start = System.currentTimeMillis();
try {
((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
if(logger.isInfoEnabled()){
logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
}
System.out.println("==========結(jié)束執(zhí)行controller環(huán)繞通知===============");
} catch (Throwable e) {
long end = System.currentTimeMillis();
if(logger.isInfoEnabled()){
logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
}
}
}
/**
* 后置通知 用于攔截Controller層記錄用戶的操作
*
* @param joinPoint 切點(diǎn)
*/
@After("controllerAspect()")
public void after(JoinPoint joinPoint) {
/* HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpSession session = request.getSession(); */
//讀取session中的用戶
// 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);
//*========數(shù)據(jù)庫日志=========*//
SystemLog log = new SystemLog();
log.setId(UUID.randomUUID().toString());
log.setDescription(operationName);
log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);
log.setLogType((long)0);
log.setRequestIp(ip);
log.setExceptioncode( null);
log.setExceptionDetail( null);
log.setParams( null);
log.setCreateBy(user.getName());
log.setCreateDate(new Date());
//保存數(shù)據(jù)庫
systemLogService.insert(log);
System.out.println("=====controller后置通知結(jié)束=====");
} catch (Exception e) {
//記錄本地異常日志
logger.error("==后置通知異常==");
logger.error("異常信息:{}", e.getMessage());
}
}
//配置后置返回通知,使用在方法aspect()上注冊的切入點(diǎn)
@AfterReturning("controllerAspect()")
public void afterReturn(JoinPoint joinPoint){
System.out.println("=====執(zhí)行controller后置返回通知=====");
if(logger.isInfoEnabled()){
logger.info("afterReturn " + joinPoint);
}
}
/**
* 異常通知 用于攔截記錄異常日志
*
* @param joinPoint
* @param e
*/
@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(); */
//獲取用戶請求方法的參數(shù)并序列化為JSON格式字符串
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("請求參數(shù):" + params);
/*==========數(shù)據(jù)庫日志=========*/
SystemLog log = new SystemLog();
log.setId(UUID.randomUUID().toString());
log.setDescription(operationName);
log.setExceptioncode(e.getClass().getName());
log.setLogType((long)1);
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);
//保存數(shù)據(jù)庫
systemLogService.insert(log);
System.out.println("=====異常通知結(jié)束=====");
} catch (Exception ex) {
//記錄本地異常日志
logger.error("==異常通知異常==");
logger.error("異常信息:{}", ex.getMessage());
}
/*==========記錄本地異常日志==========*/
logger.error("異常方法:{}異常代碼:{}異常信息:{}參數(shù):{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);
}
}
我這里寫的比較全,前置通知,環(huán)繞通知,后置通知,異常通知,后置飯后通知,都寫上了,在我們實(shí)際編寫中不寫全也沒事,我習(xí)慣上把記錄日志的邏輯寫在后置通知里面,我看網(wǎng)上也有些在前置通知里面的,但我感覺寫在后置通知里比較好。
下面開始在controller中加入自定義的注解!!
package com.gcx.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.gcx.annotation.Log;
import com.gcx.service.UserService;
@Controller
@RequestMapping("userController")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("testAOP")
@Log(operationType="add操作:",operationName="添加用戶")
public void testAOP(String userName,String password){
userService.addUser(userName, password);
}
}
下面編寫測試類
@Test
public void testAOP1(){
//啟動Spring容器
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext-mvc.xml","classpath:applicationContext-dataSource.xml"});
//獲取service或controller組件
UserController userController = (UserController) ctx.getBean("userController");
userController.testAOP("zhangsan", "123456");
}

數(shù)據(jù)庫數(shù)據(jù):

我原本想寫兩個切點(diǎn),一個是service層,一個是controller層,service層是用來記錄異常信息的日志,而controller層的是用來記錄功能的日志,運(yùn)行結(jié)果如下。 
這樣做的話不知道在實(shí)際的項(xiàng)目中運(yùn)行效率好不好,在這里請看到博客的大牛給點(diǎn)建議??!
以上這篇spring AOP自定義注解方式實(shí)現(xiàn)日志管理的實(shí)例講解就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決報錯:java:讀取jar包時出錯:error in opening zip 
文章總結(jié):解決Java讀取jar包時出錯的問題,通過下載源碼并刷新項(xiàng)目解決了問題,希望對大家有所幫助2024-11-11
Java正則表達(dá)式_動力節(jié)點(diǎn)Java學(xué)院整理
什么是正則表達(dá)式,正則表達(dá)式的作用是什么?這篇文章主要為大家詳細(xì)介紹了Java正則表達(dá)式的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05
解讀Spring事務(wù)是如何實(shí)現(xiàn)的
這篇文章主要介紹了Spring事務(wù)是如何實(shí)現(xiàn)的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
SpringBoot搭建多數(shù)據(jù)源的實(shí)現(xiàn)方法
說起多數(shù)據(jù)源,一般都來解決那些問題呢,主從模式或者業(yè)務(wù)比較復(fù)雜需要連接不同的分庫來支持業(yè)務(wù)。本文主要介紹了SpringBoot搭建多數(shù)據(jù)源的實(shí)現(xiàn)方法,感興趣的可以了解一下,感興趣的可以額了解一下2021-12-12
idea設(shè)置在包里面在創(chuàng)建一個包方式
這篇文章主要介紹了idea設(shè)置在包里面在創(chuàng)建一個包方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05

