在Spring框架中,使用AOP配合自定義註解能夠方便的實現用戶操做的監控。首先搭建一個基本的Spring Boot Web環境開啓Spring Boot,而後引入必要依賴:html
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- aop依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- oracle驅動 --> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>6.0</version> </dependency> <!-- druid數據源驅動 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.6</version> </dependency>
定義一個方法級別的@Log
註解,用於標註須要監控的方法:git
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String value() default ""; }
在數據庫中建立一張sys_log表,用於保存用戶的操做日誌,數據庫採用oracle 11g:github
CREATE TABLE "SCOTT"."SYS_LOG" ( "ID" NUMBER(20) NOT NULL , "USERNAME" VARCHAR2(50 BYTE) NULL , "OPERATION" VARCHAR2(50 BYTE) NULL , "TIME" NUMBER(11) NULL , "METHOD" VARCHAR2(200 BYTE) NULL , "PARAMS" VARCHAR2(500 BYTE) NULL , "IP" VARCHAR2(64 BYTE) NULL , "CREATE_TIME" DATE NULL ); COMMENT ON COLUMN "SCOTT"."SYS_LOG"."USERNAME" IS '用戶名'; COMMENT ON COLUMN "SCOTT"."SYS_LOG"."OPERATION" IS '用戶操做'; COMMENT ON COLUMN "SCOTT"."SYS_LOG"."TIME" IS '響應時間'; COMMENT ON COLUMN "SCOTT"."SYS_LOG"."METHOD" IS '請求方法'; COMMENT ON COLUMN "SCOTT"."SYS_LOG"."PARAMS" IS '請求參數'; COMMENT ON COLUMN "SCOTT"."SYS_LOG"."IP" IS 'IP地址'; COMMENT ON COLUMN "SCOTT"."SYS_LOG"."CREATE_TIME" IS '建立時間'; CREATE SEQUENCE seq_sys_log START WITH 1 INCREMENT BY 1;
庫表對應的實體:web
public class SysLog implements Serializable{ private static final long serialVersionUID = -6309732882044872298L; private Integer id; private String username; private String operation; private Integer time; private String method; private String params; private String ip; private Date createTime; // get,set略 }
爲了方便,這裏直接使用Spring JdbcTemplate來操做數據庫。定義一個SysLogDao接口,包含一個保存操做日誌的抽象方法:spring
public interface SysLogDao { void saveSysLog(SysLog syslog); }
其實現方法:sql
@Repository public class SysLogDaoImp implements SysLogDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public void saveSysLog(SysLog syslog) { StringBuffer sql = new StringBuffer("insert into sys_log "); sql.append("(id,username,operation,time,method,params,ip,create_time) "); sql.append("values(seq_sys_log.nextval,:username,:operation,:time,:method,"); sql.append(":params,:ip,:createTime)"); NamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate(this.jdbcTemplate.getDataSource()); npjt.update(sql.toString(), new BeanPropertySqlParameterSource(syslog)); } }
定義一個LogAspect類,使用@Aspect
標註讓其成爲一個切面,切點爲使用@Log
註解標註的方法,使用@Around
環繞通知:數據庫
@Aspect @Component public class LogAspect { @Autowired private SysLogDao sysLogDao; @Pointcut("@annotation(com.springboot.annotation.Log)") public void pointcut() { } @Around("pointcut()") public Object around(ProceedingJoinPoint point) { Object result = null; long beginTime = System.currentTimeMillis(); try { // 執行方法 result = point.proceed(); } catch (Throwable e) { e.printStackTrace(); } // 執行時長(毫秒) long time = System.currentTimeMillis() - beginTime; // 保存日誌 saveLog(point, time); return result; } private void saveLog(ProceedingJoinPoint joinPoint, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); SysLog sysLog = new SysLog(); Log logAnnotation = method.getAnnotation(Log.class); if (logAnnotation != null) { // 註解上的描述 sysLog.setOperation(logAnnotation.value()); } // 請求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); sysLog.setMethod(className + "." + methodName + "()"); // 請求的方法參數值 Object[] args = joinPoint.getArgs(); // 請求的方法參數名稱 LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); String[] paramNames = u.getParameterNames(method); if (args != null && paramNames != null) { String params = ""; for (int i = 0; i < args.length; i++) { params += " " + paramNames[i] + ": " + args[i]; } sysLog.setParams(params); } // 獲取request HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); // 設置IP地址 sysLog.setIp(IPUtils.getIpAddr(request)); // 模擬一個用戶名 sysLog.setUsername("mrbird"); sysLog.setTime((int) time); sysLog.setCreateTime(new Date()); // 保存系統日誌 sysLogDao.saveSysLog(sysLog); } }
TestController:springboot
@RestController public class TestController { @Log("執行方法一") @GetMapping("/one") public void methodOne(String name) { } @Log("執行方法二") @GetMapping("/two") public void methodTwo() throws InterruptedException { Thread.sleep(2000); } @Log("執行方法三") @GetMapping("/three") public void methodThree(String name, String age) { } }
最終項目目錄以下圖所示:oracle
啓動項目,分別訪問:app
查詢數據庫:
SQL> select * from sys_log order by id; ID USERNAME OPERATION TIME METHOD PARAMS IP CREATE_TIME ---------- ---------- ---------- ---------- ------------------------------ ------------------------------ ---------- -------------- 11 mrbird 執行方法一 6 com.springboot.controller.Test name: KangKang 127.0.0.1 08-12月-17 Controller.methodOne() 12 mrbird 執行方法二 2000 com.springboot.controller.Test 127.0.0.1 08-12月-17 Controller.methodTwo() 13 mrbird 執行方法三 0 com.springboot.controller.Test name: Mike age: 25 127.0.0.1 08-12月-17 Controller.methodThree()
source code 本文由博客一文多發平臺 OpenWrite 發佈!