Spring AOP 實現寫事件日志功能


什么是AOP?AOP使用場景?AOP相關概念?Spring AOP組件?如何使用Spring AOP?等等這些問題請參考博文:Spring AOP 實現原理

下面重點介紹如何寫事件日志功能,把日志保存到數據庫中。 
事件日志是與主業務功能無關的邏輯,用AOP實現是再好不過了,其中因為有些數據庫日志表中的字段參數需要傳遞,所以會用到自定義注解,將這些參數用自定義注解傳遞過來。

1.自定義注解Operation

package com.jykj.demo.filter; 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; /** * @Descrption該注解描述方法的操作類型和方法的參數意義 */ @Target(value = ElementType.METHOD) @Retention(value = RetentionPolicy.RUNTIME) @Documented public @interface Operation { /** * @Description描述操作類型 為必填項,1:登錄日志2:操作日志 */ int type(); /** * @Description描述操作意義 比如申報通過或者不通過等 */ String desc() default ""; /** * @Description描述操作方法的參數意義 數組長度需與參數長度一致,否則會參數與描述不一致的情況 */ String[] arguDesc() default {}; } 

 

2.切面類EventLogAspect

package com.jykj.demo.filter; import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import com.jykj.demo.service.SysEventService; //事件日志 切面,凡是帶有 @Operation 注解的service方法都將會寫日志 public class EventLogAspect { @Autowired SysEventService sysEventService; public void doAfterReturning(JoinPoint jp) { System.out.println( "log Ending method: " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()); Method proxyMethod = ((MethodSignature) jp.getSignature()).getMethod(); Method soruceMethod; try { soruceMethod = jp.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes()); Operation oper = soruceMethod.getAnnotation(Operation.class); if (oper != null) { System.out.println(oper.desc()); // 解析參數 sysEventService.insertEventLog(oper.type(),oper.desc()+"("+extractParam(jp.getArgs(),oper.arguDesc())+") 成功"); } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); }catch (Exception e) { e.printStackTrace(); } } public void doAfterThrowing(JoinPoint jp, Throwable ex) { Method proxyMethod = ((MethodSignature) jp.getSignature()).getMethod(); Method soruceMethod; try { soruceMethod = jp.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes()); Operation oper = soruceMethod.getAnnotation(Operation.class); if (oper != null) { System.out.println(oper.desc()); sysEventService.insertEventLog(oper.type(),oper.desc()+ "("+extractParam(jp.getArgs(),oper.arguDesc())+" 出現異常:"+ex.getMessage()); } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } } private String extractParam(Object[] objParam, String[] arguDesc) { StringBuilder sb = new StringBuilder(); int len = objParam.length<arguDesc.length?objParam.length:arguDesc.length;//取最小值 for (int i = 0; i < len; i++) { //空字符串將不解析 if(arguDesc[i]!=null && !arguDesc[i].isEmpty()){ sb.append(arguDesc[i]+":"+objParam[i]+","); } } String rs = sb.toString(); return rs.substring(0,rs.length()-1); } } 

 

3.切入點 主業務邏輯類FrmAppsService

package com.jykj.demo.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.jykj.demo.dao.FrmAppsMapper; import com.jykj.demo.entity.FrmApps; import com.jykj.demo.filter.Operation; import com.jykj.demo.mapper.AbstractService; @Service public class FrmAppsService { @Autowired FrmAppsMapper mapper; @Operation(type=1,desc="更新框架應用",arguDesc={"","操作類型"}) public int access(FrmApps app,String action){ return mapper.access(app,action); } }

 

4.Spring xml對AOP的配置

    <bean id="aspectEventLog" class="com.jykj.demo.filter.EventLogAspect" /> <!-- <aop:aspectj-autoproxy /> --> <!--配置service包下所有類或接口的所有方法--> <aop:config proxy-target-class="true"> <aop:aspect id="EventLogAspect" ref="aspectEventLog"> <aop:pointcut id="myService" expression="execution(* com.jykj.demo.service.*.*(..)) " /> <aop:after-returning pointcut-ref="myService" method="doAfterReturning"/> <aop:after-throwing pointcut-ref="myService" method="doAfterThrowing" throwing="ex"/> </aop:aspect> </aop:config> <!-- 控制器 --> <context:component-scan base-package="com.jykj.demo.controller" /> <!-- service --> <context:component-scan base-package="com.jykj.demo.service" /> <context:component-scan base-package="com.jykj.demo.filter" />

 

需要注意的是 proxy-target-class=”true” ,如果沒有這個,目標類若是實現了接口,將會以JDK代理的方式,否則采用的是CGLib代理的方式,如果啟動項目報錯了,會報代理類無法轉換的問題,這時候把這個配置寫上就可以了。

這樣就實現了寫日志的功能,只要在需要寫日志的service類的方法上用自定義注解Operation注解即可

5.輸出 (編輯一個記錄時)

log Ending method: com.jykj.demo.service.FrmAppsService.access 更新框架應用 log Ending method: com.jykj.demo.service.SysEventService.insertEventLog

 

這里寫圖片描述

另外再附加 在未登錄時禁止訪問頁面的功能 采用攔截器 
SecurityInterceptor類

package com.jykj.demo.filter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.jykj.demo.util.Helper; public class SecurityInterceptor implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); if (session.getAttribute(Helper.SESSION_USER) == null) { throw new AuthorizationException(); } else { return true; } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub } } 

 

對應的攔截器的配置 spring-mvc.xml

<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/*"/> <mvc:exclude-mapping path="/login"/> <mvc:exclude-mapping path="/signIn"/> <mvc:exclude-mapping path="/register"/> <bean class="com.jykj.demo.filter.SecurityInterceptor"> </bean> </mvc:interceptor> </mvc:interceptors> <!-- bean 處理未登錄重定向到登錄界面 --> <bean id="handlerExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="com.jykj.demo.filter.AuthorizationException">redirect:login</prop> </props> </property> </bean>

 

 

異常類AuthorizationException

package com.jykj.demo.filter; public class AuthorizationException extends Exception{ private static final long serialVersionUID = 1L; } 

 

參考博文:Spring Aop 日志攔截應用 
參考博文:基於配置的Spring AOP 
參考博文:Spring AOP 詳解

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM