利用Java Annotation 和 Spring AOP實現在Controller層面的操作日誌記錄
阿新 • • 發佈:2019-02-04
Annotation,Spring,AOP之類的概念這裡就不在介紹了,網上的相關知識一搜在大堆,而且也是各大公司面試之必考內容。目前AOP技術的應用場景中應該很大一部分是用來實現操作日誌記錄的,由於每個公司幾乎都有自己的開發框架,而且很多框架都對CRUD之類的操作進行了高度封裝,Service層面幾乎省去了90%的程式碼,這樣利用AOP記錄每個模組的CRUD操作變得有些麻煩,下面分享一種利用Annotation在Controller層面記錄日誌的方法:
1.首頁建立一個Annotation類
import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Inherited @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Traced { String name() default "";// 預設為空 }
2.建立核心AOP操作類:ControllerTraceAspect
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.builder.ToStringBuilder; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetails; @Aspect public class ControllerTraceAspect{ protected Logger logger = LoggerFactory.getLogger(getClass()); /** *所有查詢方法 */ @Around("execution(* org.tshark.core.orm.hibernate.EntityManager.search(..)) ") public Object searchAground(ProceedingJoinPoint pjp) throws Throwable { if (TraceContextHolderFactory.getTraceContextHolder().getContext() == null) { return pjp.proceed(); } else { return doAround(null, getUserCode(), getRemoteHost(), pjp); } } //只對Controller進行pointcut @Pointcut("within(@org.springframework.stereotype.Controller *)") public void controllerPointcut() { } /** * 對有@Traced標記的方法,記錄其執行引數及返回結果. */ @Pointcut("@annotation(org.tshark.framework.trace.Traced)") public void tracedMethodPointcut() { } @Around("controllerPointcut() && tracedMethodPointcut()") public Object traceAround(ProceedingJoinPoint pjp) throws Throwable { return doAroundController(pjp); } private Object doAroundController(ProceedingJoinPoint pjp) throws Throwable { String methodName = pjp.getSignature().getName(); Object[] args = pjp.getArgs(); Class[] argsClazz = new Class[args.length]; //Controller中所有方法的引數,前兩個分別為:Request,Response argsClazz[0] = HttpServletRequest.class; argsClazz[1] = HttpServletResponse.class; //第三個為儲存中的上傳的Model if (args.length == 3) { argsClazz[2] = args[2].getClass(); } Traced tracedAnnotation = AnnotationUtils.findAnnotation(pjp.getTarget().getClass().getDeclaredMethod(methodName, argsClazz), Traced.class); String traceName = tracedAnnotation.name(); return doAround(traceName, getUserCode(), getRemoteHost(), pjp); } //獲取當前登入使用者 private String getUserCode() { String userCode = "_system"; Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (principal != null && principal instanceof UserDetails) { userCode = ((UserDetails) principal).getUsername(); } return userCode; } //獲取當前使用者IP private String getRemoteHost() { String host = null; WebAuthenticationDetails authDetails = (WebAuthenticationDetails) SecurityContextHolder.getContext().getAuthentication() .getDetails(); if (authDetails != null) { host = authDetails.getRemoteAddress(); } return host; } private String getOperateContext(Object[] args) { StringBuffer strBuffer = new StringBuffer(); for (int i = 0; i < args.length; i++) { if(i==2){//第三個引數為Model strBuffer.append(ToStringBuilder.reflectionToString(args[i])); } } return strBuffer.toString(); } protected Object doAround(String traceName, String currUser, String host, ProceedingJoinPoint pjp) throws Throwable { //日誌記錄 } }
3.Spring 配置檔案
web.xml中的配置
<servlet> <servlet-name>TS-Dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:action-servlet.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>TS-Dispatcher</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping>
action-servlet.xml中的相關配置(以下配置必須放在action-servlet.xml不能放在service的配置檔案中)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-lazy-init="true">
<context:component-scan base-package="org.tshark.**.action" />
<aop:aspectj-autoproxy proxy-target-class="true" >
<aop:include name="controllerAspect" />
</aop:aspectj-autoproxy>
<bean id="controllerAspect" class="org.tshark.framework.trace.ControllerTraceAspect" />
4.使用方法,在需要記錄的Controller上加上 @Traced(name="基礎管理>使用者管理:新增或修改使用者")
例如:
@Traced(name="基礎管理>使用者管理:新增或修改使用者")
@RequestMapping(value = "/save", method = RequestMethod.POST)
public void save(HttpServletRequest request, HttpServletResponse response, UserModel userModel) throws Exception {
。。。
}