Spring Aop+註解實現日誌記錄
阿新 • • 發佈:2018-12-31
系統業務操作日誌記錄是每個系統必不可少的一部分,但通常的做法是在每個需要記錄日誌的地方,呼叫新增日誌的Service方法,這樣做主要是顯的麻煩。
我們可以使用Spring AOP結合註解來實現這一功能。
1、首先定義一個註解類,如下:
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String module() default "";
String desc() default "";
}
註解類有2個屬性,module是操作的模組,desc為具體記錄的日誌資訊。
2、定義切面類:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
/**
* Created by Administrator on 2017/6/2.
*/
@Aspect
public class LogAspect {
private final Logger logger = LoggerFactory.getLogger(LogAspect.class);
private long timeStart;
@Before(value = "execution(* aop.service..*.*(..))")
public void doBefore(JoinPoint joinPoint) {
timeStart = System.currentTimeMillis();
}
@After(value = "execution(* aop.service..*.*(..))")
public void doAfter(JoinPoint joinPoint) {
long timeEnd = System.currentTimeMillis();
logger.info("方法:" + joinPoint.getTarget().getClass().getSimpleName()+"."+joinPoint.getSignature().getName() + "執行結束。耗時:" + (timeEnd-timeStart)+"ms.");
Method proxyMethod = ((MethodSignature)(joinPoint.getSignature())).getMethod();
try {
Method sourceMethod = joinPoint.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
SysLog sysLog = sourceMethod.getAnnotation(SysLog.class);
if (sysLog != null) {
String module = sysLog.module();
String desc = sysLog.desc();
// 這裡獲取登陸使用者資訊
/**
* 1.獲取request資訊
* 2.根據request獲取session
* 3.從session中取出登入使用者資訊
*/
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes)ra;
HttpServletRequest request = sra.getRequest();
HttpSession session = request.getSession();
// 從session中獲取使用者資訊
String loginInfo = (String) session.getAttribute("username");
String username = "admin";
logger.info(username + "在[" + module + "]" + desc);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
3、spring配置檔案加入:
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean id="aspectEventLog" class="aop.LogAspect" />
4、maven依賴:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>${spring.groupId}</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
5、測試service:
@Service
public class TestService {
@SysLog(module = SysConstant.MODULE_USER_MGR,desc = "新增使用者")
public void test() {
System.out.println("執行了test()方法,username=" + username);
try {
Thread.sleep((long) (Math.random()*3000L));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
執行結果:
執行了test()方法
12:39:30,355 INFO main aop.LogAspect:33 - 方法:TestService.test執行結束。耗時:1601ms.
12:39:30,356 INFO main aop.LogAspect:44 - admin在[系統管理]新增使用者
最後說明一下,這種事沒法很具體的,比如某個人添加了某個使用者,這種級別做不到。
上面拿到httpservletrequest後當然可以拿到所有的請求引數,如果要更具體,就需要在每個請求上攜帶特定的引數。
比如使用者AA添加了使用者BB,BB就得放到固定名字的引數中。如果不用註解,就是把模組名和具體的日誌資訊都放到request中,在切面中從request獲取,不過這樣就比較麻煩了。
如果只是需要具體到模組,並記錄IP之類的,上面的就可以滿足了。