1.引入Aop的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.定义切面Aspect
package com.example.boot.aspect;
import lombok.extern.slf4j.Slf4j;
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.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
/**
* com.example.boot.aspect
* Description:
*
* @author jack
* @date 2021/6/24 11:18 上午
*/
@Aspect
@Component
@Slf4j
public class LogAspect {
/**
* 配置切入点
*/
@Pointcut("@annotation(com.example.boot.aspect.Log)")
public void logPointcut() {
}
/**
* 配置环绕通知,使用在方法logPointcut()上注册的切入点
*
* @param joinPoint join point for advice
*/
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
printLog(joinPoint, startTime);
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
log.debug("Cost : {}ms", endTime - startTime);
System.out.println();
return result;
}
private void printLog(ProceedingJoinPoint joinPoint, long startTime) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (Objects.isNull(servletRequestAttributes)) {
return;
}
HttpServletRequest request = servletRequestAttributes.getRequest();
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
String requestType = request.getMethod();
String uri = request.getRequestURI();
String requestTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date(startTime));
log.debug("====================Request time:{}====================", requestTime);
log.debug("Controller : {}#{}", className, methodName);
log.debug("RequestType : {}", requestType);
log.debug("Uri : {}", uri);
log.debug("Parameters : {}", geParametersString(request));
log.debug("RequestJson : {}", getSubscribeJson(request));
log.debug("Ip : {}", getIpAddress(request));
}
/**
* 获取表单提交的参数信息
*
* @param request requestp
* @return parameterString
*/
private String geParametersString(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
StringBuilder stringBuilder = new StringBuilder();
parameterMap.forEach((k, v) -> stringBuilder.append(k).append("=").append(Arrays.toString(v)).append(" "));
return stringBuilder.toString();
}
/**
* 从request中获取json
*
* @param request request
* @return parameterJson
*/
public String getSubscribeJson(HttpServletRequest request) {
String contentType = request.getContentType();
if (!"application/json".equals(contentType)) {
return null;
}
BufferedReader reader = null;
StringBuilder stringBuilder = new StringBuilder();
try {
reader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
} catch (IOException e) {
log.error("从request中获取json数据失败");
} finally {
try {
if (null != reader) {
reader.close();
}
} catch (IOException e) {
log.error("getSubscribeJson()关闭数据流失败");
}
}
return stringBuilder.toString();
}
/**
* 获取客户端ip
*
* @param request request
* @return ip
*/
private String getIpAddress(HttpServletRequest request) {
String unknown = "unknown";
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
3.自定义注解,在切面中拦截
/**
* com.example.boot.aspect
* Description:
*
* @author jack
* @date 2021/6/24 11:20 上午
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
}
4.使用注解
@RestController
@RequestMapping(value = "/message")
@Slf4j
public class MessageController {
@Log
@GetMapping(value = "/getOne")
public void getOne(Long id) {
log.info("getOne:{}", id);
}
}
5.效果
6.advice执行的顺序
6.1 单个切面正常情况的顺序
@Around@BeforejoinPoint.proceed();执行方法@AfterReturning@After@Around
6.2 单个切面异常情况的顺序
@Around@BeforejoinPoint.proceed();执行方法@AfterThrowing
6.3 多个切面正常情况的顺序
@Order 值越小的越先执行,越先执行的最后才结束
Aspect1 @AroundAspect1 @BeforeAspect2 @AroundAspect2 @BeforeAspect2 joinPoint.proceed();执行方法Aspect2 @AfterReturningAspect2 @AfterAspect2 @AroundAspect1 @AfterReturningAspect1 @AfterAspect1 @Around
6.4 测试代码
LogAspect1
package com.example.boot.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* com.example.boot.aspect
* Description:
*
* @author jack
* @date 2021/6/24 11:18 上午
*/
@Aspect
@Component
@Slf4j
@Order(1)
public class LogAspect1 {
/**
* 配置切入点
*/
@Pointcut("@annotation(com.example.boot.aspect.Log)")
public void logPointcut() {
}
/**
* 声明前置通知
*
* @param point point
*/
@Before("logPointcut()")
public void doBefore(JoinPoint point) {
log.info("LogAspect1:doBefore");
}
/**
* 声明后置通知
*
* @param point point
* @param returnValue returnValue
*/
@AfterReturning(pointcut = "logPointcut()", returning = "returnValue")
public void doAfterReturning(JoinPoint point, Object returnValue) {
log.info("LogAspect1:doAfterReturning,returnValue:{}", returnValue);
}
/**
* 声明异常通知
*
* @param e e
*/
@AfterThrowing(pointcut = "logPointcut()", throwing = "e")
public void doAfterThrowing(Exception e) {
log.error("LogAspect1:doAfterThrowing", e);
}
/**
* 声明最终通知
*/
@After("logPointcut()")
public void doAfter() {
log.info("LogAspect1:doAfter");
}
/**
* 配置环绕通知,使用在方法logPointcut()上注册的切入点
*
* @param joinPoint join point for advice
*/
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("LogAspect1:doAround-1");
Object result = joinPoint.proceed();
log.info("LogAspect1:doAround-2");
return result;
}
}
LogAspect2
package com.example.boot.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* com.example.boot.aspect
* Description:
*
* @author jack
* @date 2021/6/24 11:18 上午
*/
@Aspect
@Component
@Slf4j
@Order(2)
public class LogAspect2 {
/**
* 配置切入点
*/
@Pointcut("@annotation(com.example.boot.aspect.Log)")
public void logPointcut() {
}
/**
* 声明前置通知
*
* @param point point
*/
@Before("logPointcut()")
public void doBefore(JoinPoint point) {
log.info("LogAspect2:doBefore");
}
/**
* 声明后置通知
*
* @param point point
* @param returnValue returnValue
*/
@AfterReturning(pointcut = "logPointcut()", returning = "returnValue")
public void doAfterReturning(JoinPoint point, Object returnValue) {
log.info("LogAspect2:doAfterReturning,returnValue:{}", returnValue);
}
/**
* 声明异常通知
*
* @param e e
*/
@AfterThrowing(pointcut = "logPointcut()", throwing = "e")
public void doAfterThrowing(Exception e) {
log.error("LogAspect2:doAfterThrowing", e);
}
/**
* 声明最终通知
*/
@After("logPointcut()")
public void doAfter() {
log.info("LogAspect2:doAfter");
}
/**
* 配置环绕通知,使用在方法logPointcut()上注册的切入点
*
* @param joinPoint join point for advice
*/
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("LogAspect2:doAround-1");
Object result = joinPoint.proceed();
log.info("LogAspect2:doAround-2");
return result;
}
}