目錄
一、依賴
二、自定義注解
三、切面
一、依賴
以SpringBoot工程為例,導入aop的依賴。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二、自定義注解
我們自定義一個注解。這里叫LogAnnotation,屬性也可以自定義。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {String module() default "";String operation() default "";}
注解有了,我們就可以將這個注解作用到任何方法上面了。
但是僅僅有這個注解還不行,它不能發揮任何功能,起不了什么作用,也僅僅是標記了一個方法。
因此,我們就要為這個注解建立切面。
三、切面
解釋如下代碼含義:
只要標記了@LogAnnotation注解的方法都會執行環繞通知。
通知中做如下處理:方法調用前記錄時間、執行方法、方法執行后記錄時間,最后保存日志。
日志顯示@LogAnnotation的屬性(不寫就是空值默認值)、請求參數、方法名、請求者的ip地址、方法執行耗時等信息。
@Aspect
@Component
@Slf4j
public class LogAspect {@Pointcut("@annotation(com.pps.aop.LogAnnotation)")public void logPointCut() {}@Around("logPointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {long beginTime = System.currentTimeMillis();//執行方法Object result = point.proceed();//執行時長(毫秒)long time = System.currentTimeMillis() - beginTime;//保存日志recordLog(point, time);return result;}private void recordLog(ProceedingJoinPoint joinPoint, long time) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);log.info("=====================log start================================");log.info("module:{}",logAnnotation.module());log.info("operation:{}",logAnnotation.operation());//請求的方法名String className = joinPoint.getTarget().getClass().getName();String methodName = signature.getName();log.info("request method:{}",className + "." + methodName + "()");//請求的參數Object[] args = joinPoint.getArgs();if (args.length > 0 ){// 調用的該方法必須有形參才會進入,按順序記錄實參。for (Object param : args) {log.info("params:{}",JSON.toJSONString(param));}}//獲取request 設置IP地址HttpServletRequest request = HttpServletUtils.getHttpServletRequest();log.info("ip:{}", IpUtils.getIpAddr(request));log.info("execute time: {} ms",time);log.info("=====================log end=================================");}
}
IP工具類
/*** 從http請求中獲取ip地址<br>* @author hssy* @version 1.0*/
@Slf4j
public class IpUtils {/*** 獲取IP地址<br/>* 使用Nginx等反向代理軟件, 則不能通過request.getRemoteAddr()獲取IP地址<br/>* 如果使用了多級反向代理的話,X-Forwarded-For的值并不止一個,<br/>* 而是一串IP地址,X-Forwarded-For中第一個非unknown的有效IP字符串,則為真實IP地址<br/>*/public static String getIpAddr(HttpServletRequest request) {String ip = null;try {ip = request.getHeader("x-forwarded-for");if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}} catch (Exception e) {log.error("IPUtils ERROR ", e);}// 使用代理,則獲取第一個IP地址if (StringUtils.isEmpty(ip) && ip.length() > 15) {if (ip.indexOf(",") > 0) {ip = ip.substring(0, ip.indexOf(","));}}return ip;}
}
獲取HttpServletRequest工具類
public class HttpServletUtils {public static HttpServletRequest getHttpServletRequest(){return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();}
}
演示效果