創建監聽器三步驟:
1、事件(event)可以封裝和傳遞監聽器中要處理的參數,如對象或字符串,並作為監聽器中監聽的目標。
2、監聽器(listener)具體根據事件發生的業務處理模塊,這里可以接收處理事件中封裝的對象或字符串。
3、事件發布者(publisher)事件發生的觸發者。
代碼展示:
pom.xml
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>4.6.8</version> </dependency>
第一步:
定義一個事件,需要繼承spring的ApplicationEvent
package top.xzhand.event; import org.springframework.context.ApplicationEvent; /** * 定義一個事件,需要繼承spring的ApplicationEvent */ public class LogEvent extends ApplicationEvent { public LogEvent(Object source) { super(source); } }
第二步:
定義切面,發布事件
@Component
@Aspect
這兩個注解必須添加
package top.xzhand.event.aspect; import com.alibaba.fastjson.JSON; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import top.xzhand.event.LogEvent; import top.xzhand.po.RequestLog; import org.aspectj.lang.JoinPoint; import top.xzhand.util.LogUtil; import java.util.Date; /** * 切面發布 */ @Component @Aspect public class LogAspect { public static final ThreadLocal<RequestLog> THREAD_LOCAL = new ThreadLocal<>(); @Autowired private ApplicationContext applicationContext; @Pointcut("@annotation(top.xzhand.event.common.Log)") public void logAspect() { } @Before(value = "logAspect()") public void before(JoinPoint point) throws Throwable { RequestLog requestLog = new RequestLog(); requestLog.setCreateAt(new Date());//開始時間 Environment environment = applicationContext.getEnvironment(); String appName = environment.getProperty("spring.application.name"); // sysLog.setCreateName(createName); THREAD_LOCAL.set(LogUtil.getSysLog(point,requestLog)); System.out.println("進入切面:"+JSON.toJSONString(requestLog)); } @AfterReturning(returning = "rvt", pointcut = "logAspect()") public void afterReturning(JoinPoint point, Object rvt) throws Throwable { RequestLog sysLog = get(); if (rvt != null) { sysLog.setResponseResult(LogUtil.getText(JSON.toJSONString(rvt))); } else { sysLog.setResponseResult(null); } publishEvent(sysLog); System.out.println("切面監聽事件發布成功:"+JSON.toJSONString(sysLog)); } private void publishEvent(RequestLog sysLog) { applicationContext.publishEvent(new LogEvent(sysLog)); THREAD_LOCAL.remove(); } @AfterThrowing(pointcut = "logAspect()", throwing = "e") public void afterThrowing(Throwable e) { RequestLog sysLog = get(); publishEvent(sysLog); } private RequestLog get() { RequestLog sysLog = THREAD_LOCAL.get(); if (sysLog == null) { return new RequestLog(); } return sysLog; } }
定義切入點注解
package top.xzhand.event.common; 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; /** * 方法級別 日志 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface Log { String value() default ""; }
相關實體類,mapper,service等忽略,可自己生成!
package top.xzhand.po; import java.util.Date; public class RequestLog { private Integer id; private String requestUrl; private String requestArgs; private String ipUrl; private String message; private String responseResult; private Date createAt; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getRequestUrl() { return requestUrl; } public void setRequestUrl(String requestUrl) { this.requestUrl = requestUrl == null ? null : requestUrl.trim(); } public String getRequestArgs() { return requestArgs; } public void setRequestArgs(String requestArgs) { this.requestArgs = requestArgs == null ? null : requestArgs.trim(); } public String getIpUrl() { return ipUrl; } public void setIpUrl(String ipUrl) { this.ipUrl = ipUrl == null ? null : ipUrl.trim(); } public String getMessage() { return message; } public void setMessage(String message) { this.message = message == null ? null : message.trim(); } public String getResponseResult() { return responseResult; } public void setResponseResult(String responseResult) { this.responseResult = responseResult == null ? null : responseResult.trim(); } public Date getCreateAt() { return createAt; } public void setCreateAt(Date createAt) { this.createAt = createAt; } }
package top.xzhand.util; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; import lombok.experimental.UtilityClass; import org.aspectj.lang.JoinPoint; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import top.xzhand.event.common.Log; import top.xzhand.po.RequestLog; import javax.servlet.http.HttpServletRequest; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Method; import java.util.Date; @UtilityClass // 方法變量 靜態話,類final 私有構造器 public class LogUtil { public RequestLog getSysLog(JoinPoint point, RequestLog sysLog) { HttpServletRequest request = getRequest(); sysLog.setIpUrl(getIP(request)); sysLog.setRequestUrl(URLUtil.getPath(request.getRequestURI())); sysLog.setRequestArgs(request.getQueryString()); sysLog.setCreateAt(new Date()); return sysLog; } private HttpServletRequest getRequest() { return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } private final String UNKNOWN = "unknown"; public String getIP(HttpServletRequest request) { String ip = request.getHeader("X-Requested-For"); if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader("X-Forwarded-For"); } if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return StrUtil.isBlank(ip) ? null : ip.split(",")[0]; } public String getText(String val) { return StrUtil.sub(val, 0, 65535); } /*** * 獲取操作信息 * * @param point * @return */ public String getControllerMethodDescription(JoinPoint point) { try { // 獲取連接點目標類名 String targetName = point.getTarget().getClass().getName(); // 獲取連接點簽名的方法名 String methodName = point.getSignature().getName(); // 獲取連接點參數 Object[] args = point.getArgs(); // 根據連接點類的名字獲取指定類 Class targetClass = Class.forName(targetName); // 獲取類里面的方法 Method[] methods = targetClass.getMethods(); String description = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == args.length) { description = method.getAnnotation(Log.class).value(); break; } } } return description; } catch (Exception e) { return ""; } } /** * 獲取堆棧信息 * * @param throwable * @return */ public String getStackTrace(Throwable throwable) { StringWriter sw = new StringWriter(); try (PrintWriter pw = new PrintWriter(sw)) { throwable.printStackTrace(pw); return getText(sw.toString()); } } }
package top.xzhand.event; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import top.xzhand.po.RequestLog; /** * 事件發布 * */ @Component public class EventPublister { @Autowired private ApplicationContext applicationContext; // 事件發布方法 public void pushListener(RequestLog requsetLog) { applicationContext.publishEvent(new LogEvent(requsetLog)); } }
第三步:
自定義監聽器處理業務
package top.xzhand.event; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import top.xzhand.po.RequestLog; import top.xzhand.service.RequestLogService; /** * 自己定義的監聽器需要實現ApplicationListener, * 同時泛型參數要加上自己要監聽的事件Class名, * 在重寫的方法onApplicationEvent中,添加自己的業務處理 */ @Component public class LogListerner implements ApplicationListener<LogEvent> { @Autowired private RequestLogService requestLogService; @Override public void onApplicationEvent(LogEvent logEvent) { RequestLog requestLog=(RequestLog) logEvent.getSource(); requestLogService.insertSelective(requestLog); } }
驗證測試:
package top.xzhand.controller; import com.alibaba.fastjson.JSON; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import top.xzhand.event.EventPublister; import top.xzhand.event.common.Log; import top.xzhand.po.RequestLog; import top.xzhand.util.LogUtil; import javax.servlet.http.HttpServletRequest; import java.util.Date; /** * */ @Controller public class TestEventListenerController { @Autowired private EventPublister publisher; /** * 非注解形式監聽事件 * @param request * @param arg */ @RequestMapping(value = "/test/logEvent1" ) public void testPublishEvent1(HttpServletRequest request,String arg ){ RequestLog requestLog=new RequestLog(); requestLog.setCreateAt(new Date()); requestLog.setRequestUrl(request.getContextPath()); requestLog.setIpUrl(LogUtil.getIP(request)); requestLog.setRequestArgs(request.getQueryString()); publisher.pushListener(requestLog); System.out.println(JSON.toJSONString(requestLog)); } /** * 基於注解的切面事件發布監聽 * @param request * @param arg */ @Log @RequestMapping(value = "/test/logEvent2" ) public void testPublishEvent2(HttpServletRequest request,String arg ){ System.out.println("切面注解監聽"); } }
日志成功記錄