您好,登錄后才能下訂單哦!
本篇文章的誕生離不開這篇文章的作者:http://blog.csdn.net/czmchen/article/details/42392985。
操作日志在javaWeb的業務系統中是在是太常見的功能了,主要記錄用戶再什么時間,什么位置進行了什么操作。如果每新增一個功能都要寫一個插入代碼的話,是非常不容易維護的。加一個字段就要在每個插入語句上加入這個字段。所以AOP+注解的優勢就顯現了出來,不僅如此,當我們有了這套代碼以后,可以通用在該系統的wap端或者其他的系統中,不必修改太多的代碼。針對日志這種實時性不是很高的功能,這里用了異步的方式進行,這樣日志系統獨立出來,不會影響業務。下面是我整理的代碼,歡迎留下寶貴意見。這里再次感謝原作者提供良好的思路。
/** * * 自定義注解 攔截Controller * @see [相關類/方法] * @since [產品/模塊版本] */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SystemControllerLog { String description() default "";//描述 String moduleType() default "";//模塊代碼 String operateValue() default "";//操作類型 boolean firstParamName() default false; }
/** * * 自定義注解 攔截service * @see [相關類/方法] * @since [產品/模塊版本] */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SystemServiceLog { String description() default "";// 描述 String moduleType() default "";// 模塊代碼 String operateValue() default "";// 操作類型 }
spring.xml中加入下面這句話
<aop:aspectj-autoproxy proxy-target-class="true" />
這里用的是返回通知,用來接收成功或失敗。
/** * * AOP記錄操作&異常日志-切點類 * @see [相關類/方法] * @since [產品/模塊版本] */ @Aspect @Component public class SystemLogAspect { private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class); // 隊列 private static BlockingQueue<Log> queue = new LinkedBlockingQueue<Log>(); // 緩存線程池 private static ExecutorService threadPool = Executors.newFixedThreadPool(3); // 任務數 private static int taskSize = 6; // 線程是否已啟動 boolean isStartThread = false; // 用來啟動或停止線程 static boolean run = true; @Autowired private LogService logService; @Autowired private UserService userService; // Service層切點 @Pointcut("@annotation(com.rzzl.wap.log.annotation.SystemServiceLog)") public void serviceAspect() { } // Controller層切點 @Pointcut("@annotation(com.rzzl.wap.log.annotation.SystemControllerLog)") public void controllerAspect() { } public static BlockingQueue<Log> getQueue() { return queue; } public static void setQueue(BlockingQueue<Log> queue) { SystemLogAspect.queue = queue; } public static boolean isRun() { return run; } public static void setRun(boolean run) { SystemLogAspect.run = run; } /** * * 返回通知 用于攔截Controller層記錄用戶的操作 * @param joinPoint 切點 * @param result 返回值 * @see [類、類#方法、類#成員] */ @AfterReturning(value = "controllerAspect()", returning = "result") public void afterReturn(JoinPoint joinPoint, Object result) { // 請求的IP User user = WebUtils.getSessionValue(LoginContact.SESSION_USER); String params = ""; WebResult webResult = new WebResult(); webResult.setCode(FlagContact.BACK_SUCCESS); try { if (WebResult.class.isInstance(result)) { webResult = (WebResult)result; } String loginName = ""; InnnerBean innnerBean = getControllerMethodDescription(joinPoint); Object[] arguments = innnerBean.getArguments(); String remark = innnerBean.getDescription(); Log log = new Log.Builder().type(LogTypes.type.operate) .moduleType(innnerBean.getModuleType()) .operateCode(joinPoint.getSignature().getName()) .operateValue(innnerBean.getOperateValue()) .remark(remark) .operateStatus(webResult.getCode().equals(FlagContact.BACK_SUCCESS) ? LogTypes.operateStatus.Y : LogTypes.operateStatus.N)// 返回值1操作成功,否則失敗 .method((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")) .param(params) .loginName(user.getAccountNo()) .fullName(user.getUserName()) .build(); // 放入隊列 queue.put(log); if (!isStartThread) { for (int i = 0; i < taskSize; i++) { threadPool.execute(new saveLogThread()); } isStartThread = true; } } catch (Exception e) { logger.error("異常信息:{}", e.toString()); } } /** * 異常通知 用于攔截service層記錄異常日志 * @param joinPoint * @param e * @see [類、類#方法、類#成員] */ @AfterThrowing(pointcut = "serviceAspect()", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { // 讀取session中的用戶 User user = WebUtils.getSessionValue(LoginContact.SESSION_USER); String params = ""; try { if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) { for (int i = 0; i < joinPoint.getArgs().length; i++) { params += JSONUtils.valueToString(joinPoint.getArgs()[i].toString()) + ";"; } } InnnerBean innnerBean = getServiceMthodDescription(joinPoint); String loginName = ""; Log log = new Log.Builder().type(LogTypes.type.exception) .moduleType(innnerBean.getModuleType()) .operateCode(joinPoint.getSignature().getName()) .operateValue(innnerBean.getOperateValue()) .remark(innnerBean.getDescription()) .operateStatus(LogTypes.operateStatus.N) .method( (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")) .param(params) .exceptionDetail(e.toString()) .build(); // 放入隊列 queue.put(log); if (!isStartThread) { new Thread(new saveLogThread()).start(); isStartThread = true; } } catch (Exception ex) { logger.error("異常信息:{}", ex.toString()); } finally { logger.error("異常方法:{" + joinPoint.getTarget().getClass().getName() + "}異常代碼:{" + joinPoint.getSignature().getName() + "}異常信息:{" + e.toString() + "}參數:{" + params + "}"); } } /** * 獲取注解中對方法的描述信息 用于service層注解 * @param joinPoint 切點 * @return 方法描述 * @throws Exception * @see [類、類#方法、類#成員] */ @SuppressWarnings("rawtypes") public static InnnerBean getServiceMthodDescription(JoinPoint joinPoint) throws Exception { 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 moduleType = ""; String operateValue = ""; String description = ""; InnnerBean innnerBean = new InnnerBean(moduleType, operateValue, description); for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { SystemServiceLog annotation = method.getAnnotation(SystemServiceLog.class); moduleType = annotation.moduleType(); operateValue = annotation.operateValue(); description = annotation.description(); innnerBean = new InnnerBean(moduleType, operateValue, description); break; } } } innnerBean.setArguments(arguments); return innnerBean; } /** * 獲取注解中對方法的描述信息 用于Controller層注解 * @param joinPoint 切點 * @return 方法描述 * @throws Exception * @see [類、類#方法、類#成員] */ @SuppressWarnings("rawtypes") public static InnnerBean getControllerMethodDescription(JoinPoint joinPoint) throws Exception { 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 moduleType = ""; String operateValue = ""; String description = ""; boolean firstParamName = false; InnnerBean innnerBean = new InnnerBean(moduleType, operateValue, description); for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { SystemControllerLog annotation = method.getAnnotation(SystemControllerLog.class); moduleType = annotation.moduleType(); operateValue = annotation.operateValue(); description = annotation.description(); firstParamName = annotation.firstParamName(); innnerBean = new InnnerBean(moduleType, operateValue, description); innnerBean.setFirstParamName(firstParamName); break; } } } innnerBean.setArguments(arguments); return innnerBean; } /** * * 內部類封裝注入信息 * @see [相關類/方法] * @since [產品/模塊版本] */ static class InnnerBean { private String moduleType;// 模塊代碼 private String description;// 描述 private String operateValue;// 操作類型 private boolean firstParamName; private Object[] arguments; public InnnerBean(String moduleType, String operateValue, String description) { super(); this.moduleType = moduleType; this.description = description; this.operateValue = operateValue; } public String getOperateValue() { return operateValue; } public void setOperateValue(String operateValue) { this.operateValue = operateValue; } public String getModuleType() { return moduleType; } public void setModuleType(String moduleType) { this.moduleType = moduleType; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Object[] getArguments() { return arguments; } public void setArguments(Object[] arguments) { this.arguments = arguments; } public boolean isFirstParamName() { return firstParamName; } public void setFirstParamName(boolean firstParamName) { this.firstParamName = firstParamName; } } /** * * 異步保存日志 * @see [相關類/方法] * @since [產品/模塊版本] */ class saveLogThread implements Runnable { Lock lock = new ReentrantLock(); @Override public void run() { try { while (run) { while (queue.size() != 0) { // 如果對插入順序無要求,此處不需要同步可提升效率 lock.lock(); Log log = queue.take(); logService.insert(log); lock.unlock(); } Thread.sleep(3000); } } catch (InterruptedException e) { logger.error("saveLogThread被喚醒:" + e.toString()); } catch (Exception e) { logger.error("saveLogThread異常:" + e.toString()); } } } }
/** * * 日志系統中的定值 * * @see [相關類/方法] * @since [產品/模塊版本] */ public interface LogTypes { /** * * 操作狀態(成功與否Y\\N) * @see [相關類/方法] * @since [產品/模塊版本] */ static interface operateStatus { // 成功 final String Y = "Y"; // 失敗 final String N = "N"; } /** * * 日志類型 * @see [相關類/方法] * @since [產品/模塊版本] */ static interface type { // 操作日志 final String operate = "operate"; // 異常日志 final String exception = "exception"; } /** * * 模塊類型 * @see [相關類/方法] * @since [產品/模塊版本] */ static interface moduleType { // 登錄模塊 final String LOGIN = "LOGIN"; // 項目模塊 final String PROJECT = "PROJECT"; // 客戶模塊 final String CUSTOMER = "CUSTOMER"; // 用戶模塊 final String SYS_USER = "SYS_USER"; } /** * * 操作類型 * @see [相關類/方法] * @since [產品/模塊版本] */ static interface operateValue { // 查詢 final String select = "select"; // 登錄 final String login = "login"; // 保存 final String save = "save"; // 新增 final String add = "add"; // 修改 final String edit = "edit"; // 刪除 final String delete = "delete"; // 查看 final String view = "view"; // 修改密碼 final String editPassword = "editPassword"; // 上傳 final String upload = "upload"; // 下載 final String down = "down"; // 下載 final String packagedown = "packagedown"; } /** * * 保存描述的前綴 * 方便于批量修改該備注 * @see [相關類/方法] * @since [產品/模塊版本] */ static interface Prefix { // 查詢 final String savePrefix = "新增/編輯"; } }
應用了Builder設計模式
/** * * 操作日志&異常日志 * @see [相關類/方法] * @since [產品/模塊版本] */ public class Log implements Serializable { /** * serialVersionUID */ private static final long serialVersionUID = 1L; private static final String reqSource;// 請求來源,pc:pc端,wap:wap端 默認來源為pc private static final String localAddr;// 服務器IP private String ip;// 操作電腦ip private String fullName;// 操作人員名字 private String loginName;// 操作人員登錄賬號 private Date operateDateTime;// 操作時間 private Date createDateTime;// 創建時間 private Long id; private String type;// 日志類型,‘operate’:操作日志,‘exception’:異常日志 private String moduleType;// 模塊代碼 private String operateCode;// 操作代碼 private String operateValue;// 操作類型 private String remark;// 操作備注(記錄參數) private String operateStatus;// 操作狀態(成功與否Y\\N) private String method;// 調用方法 private String param;// 方法的請求參數 private String exceptionDetail;// 異常信息 static{ reqSource = "wap"; localAddr = WebUtils.getLocalAddr(); } public void init() { User user = WebUtils.getSessionValue(LoginContact.SESSION_USER); ip = WebUtils.getRemoteIP(); loginName = user != null ? user.getAccountNo() : getLoginName(); fullName = user != null ? user.getUserName() : getFullName(); operateDateTime = new Date(); createDateTime = new Date(); } public Log() { init(); } public Log(Log origin) { init(); this.id = origin.id; this.type = origin.type; this.moduleType = origin.moduleType; this.operateCode = origin.operateCode; this.operateValue = origin.operateValue; this.remark = origin.remark; this.operateStatus = origin.operateStatus; this.method = origin.method; this.param = origin.param; this.exceptionDetail = origin.exceptionDetail; this.fullName = origin.fullName; this.loginName = origin.loginName; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public Date getOperateDateTime() { return operateDateTime; } public void setOperateDateTime(Date operateDateTime) { this.operateDateTime = operateDateTime; } public Date getCreateDateTime() { return createDateTime; } public void setCreateDateTime(Date createDateTime) { this.createDateTime = createDateTime; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getModuleType() { return moduleType; } public void setModuleType(String moduleType) { this.moduleType = moduleType; } public String getOperateCode() { return operateCode; } public void setOperateCode(String operateCode) { this.operateCode = operateCode; } public String getOperateValue() { return operateValue; } public void setOperateValue(String operateValue) { this.operateValue = operateValue; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } public String getOperateStatus() { return operateStatus; } public void setOperateStatus(String operateStatus) { this.operateStatus = operateStatus; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public String getParam() { return param; } public void setParam(String param) { this.param = param; } public String getExceptionDetail() { return exceptionDetail; } public void setExceptionDetail(String exceptionDetail) { this.exceptionDetail = exceptionDetail; } public String getLocalAddr() { return localAddr; } public static class Builder { private Log target; public Builder() { target = new Log(); } public Builder id(Long id) { target.id = id; return this; } public Builder type(String type) { target.type = type; return this; } public Builder moduleType(String moduleType) { target.moduleType = moduleType; return this; } public Builder operateCode(String operateCode) { target.operateCode = operateCode; return this; } public Builder operateValue(String operateValue) { target.operateValue = operateValue; return this; } public Builder remark(String remark) { target.remark = remark; return this; } public Builder operateStatus(String operateStatus) { target.operateStatus = operateStatus; return this; } public Builder method(String method) { target.method = method; return this; } public Builder param(String param) { target.param = param; return this; } public Builder exceptionDetail(String exceptionDetail) { target.exceptionDetail = exceptionDetail; return this; } public Builder loginName(String loginName) { target.loginName = loginName; return this; } public Builder fullName(String fullName) { target.fullName = fullName; return this; } public Log build() { return new Log(target); } } }
/** * @ClassName: WebResult * @version 1.0 * @Desc: WEB返回JSON結果 * @history v1.0 */ public class WebResult implements Serializable { private static final long serialVersionUID = -4776437900752507269L; /** * 返回消息 */ private String msg; /** * 返回碼 */ private String code; /** * 返回數據 */ private Object data; private Map<?,?> map; private List<Map<String, Object>> list; public WebResult() { } public WebResult(String msg, String code) { super(); this.msg = msg; this.code = code; } public WebResult(String msg, String code, Object data) { super(); this.msg = msg; this.code = code; this.data = data; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Map<?, ?> getMap() { return map; } public void setMap(Map<?, ?> map) { this.map = map; } public List<Map<String, Object>> getList() { return list; } public void setList(List<Map<String, Object>> list) { this.list = list; } @Override public String toString() { return "WebResult [msg=" + msg + ", code=" + code + ", data=" + data + "]"; } /** * 初始失敗方法 * * @author cc HSSD0473 * @see [類、類#方法、類#成員] */ public void invokeFail(){ this.data = null; this.code = FlagContact.BACK_FAIL; this.msg = "操作失敗"; } public void invokeFail(String msg){ this.data = null; this.code = FlagContact.BACK_FAIL; if(msg != null && !msg.equals("")) { this.msg = msg; } } public void invokeSuccess() { this.code = FlagContact.BACK_SUCCESS; this.msg = "操作成功"; } public void invokeSuccess(String msg) { if(msg != null && !msg.equals("")) { this.msg = msg; } this.code = FlagContact.BACK_SUCCESS; } }
獲取登錄用戶,客戶ip主機ip等方法
** * @ClassName: WebUtils * @version 1.0 * @Desc: WebUtils * @history v1.0 */ public class WebUtils { /** * 描述:獲取request對象 * @return */ public static HttpServletRequest getRequest() { return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } /** * 描述:獲取responce對象 * @return */ public static HttpServletResponse getResponse() { return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); } /** * 描述:獲取session * @return */ public static HttpSession getSession() { return getRequest().getSession(); } /** * 描述:設置session值 * @param key * @param val */ public static <T> void setSessionValue(String key, T val) { getSession().setAttribute(key, val); } /** * 描述:獲取session值 * @param key * @return */ @SuppressWarnings("unchecked") public static <T> T getSessionValue(String key) { return (T) getSession().getAttribute(key); } /** * 描述:移除session * @param key * @return */ @SuppressWarnings("unchecked") public static <T> T removeSessionValue(String key) { Object obj = getSession().getAttribute(key); getSession().removeAttribute(key); return (T) obj; } /** * 描述:獲取客戶端ip * @param request * @return */ public static String getRemoteIP(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip; } /** * * 獲得本機IP * @return * @throws Exception * @see [類、類#方法、類#成員] */ public final static String getLocalAddr() { String hostAddress = ""; try { hostAddress = InetAddress.getLocalHost().getHostAddress(); } catch (Exception e) { } return hostAddress; } /** * 描述:獲取客戶端ip * @return */ public static String getRemoteIP() { HttpServletRequest request = getRequest(); return getRemoteIP(request); } }
/** * * tomcat停止前,異步日志的清理動作 * @see [相關類/方法] * @since [產品/模塊版本] */ @WebListener() public class BeforeDestoryListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { } @Override public void contextDestroyed(ServletContextEvent sce) { while (SystemLogAspect.getQueue().size() == 0) { SystemLogAspect.setRun(false); break; } } }
用的是mysql,其他數據庫請自行更改語句
CREATE TABLE `t_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `reqSource` varchar(10) DEFAULT 'pc' COMMENT '請求來源,pc:pc端,wap:wap端 默認來源為pc', `type` varchar(10) DEFAULT NULL COMMENT '日志類型,‘operate’:操作日志,‘exception’:異常日志', `ip` varchar(20) NOT NULL COMMENT '操作電腦ip', `fullName` varchar(50) NOT NULL COMMENT '操作人員名字', `loginName` varchar(50) NOT NULL COMMENT '操作人員登錄賬號', `moduleType` varchar(50) NOT NULL COMMENT '模塊代碼', `operateCode` varchar(50) NOT NULL COMMENT '操作代碼', `operateValue` varchar(50) DEFAULT NULL COMMENT '操作類型', `operateDateTime` datetime NOT NULL COMMENT '操作時間', `createDateTime` datetime NOT NULL COMMENT '創建時間', `remark` varchar(100) DEFAULT NULL COMMENT '操作備注(記錄參數)', `operateStatus` varchar(20) DEFAULT NULL COMMENT '操作狀態(成功與否Y\\N)', `localAddr` varchar(20) DEFAULT NULL COMMENT '服務器IP', `method` varchar(100) DEFAULT NULL COMMENT '調用方法', `param` varchar(2000) DEFAULT NULL COMMENT '方法的請求參數', `exceptionDetail` varchar(1000) DEFAULT NULL COMMENT '異常信息', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3430 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
/** * 登錄請求 * * @param userName 用戶名 * @param password 密碼 * @return WebResult msg:系統反饋消息 code:登錄標識碼 * @see [類、類#方法、類#成員] */ @ResponseBody @RequestMapping("login") @SystemControllerLog(moduleType=LogTypes.moduleType.LOGIN,operateValue=LogTypes.operateValue.login,description = "登錄動作") public WebResult userLogin(String accoutnNo, String password) { WebResult wt = new WebResult(); try { // 登錄邏輯 } catch(Exception e) { log.error("登錄異常:" + e.toString()); wt.invokeFail(); } return wt; }
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。