走过路过不要错过
点击蓝字关注我们
SpringAOP的相关的知识点包括源码解析,我已经在之前的文章中详细说明,如果对AOP的概念还不是特别清晰的话。
为了加深印象,这边再做一个简短的回顾:
上面提到:连接点增加切入规则就相当于定义了切入点,当然切入点表达式分为很多种,这里主要学习execution和annotation表达式。
execution(public void com.smday.service.impl.AccountServiceImpl.saveAccount())
*
匹配,数量代表包的层级,当前包可以使用..
标识,例如* *..AccountServiceImpl.saveAccount()
*
匹配:* *..*.*()
..
可以标识有无参数均可,且参数可为任意类型。全通配写法:
* *…*.*(…)
通常情况下,切入点应当设置再业务层实现类下的所有方法:* com.smday.service.impl.*.*(..)
。
匹配连接点被它参数指定的Annotation注解的方法。也就是说,所有被指定注解标注的方法都将匹配。
@annotation(com.hyh.annotation.Log)
:指定Log注解方法的连接点。
如果你使用的是SpringBoot,那么只需要引入:spring-boot-starter-aop
,框架已经将spring-aop
和aspectjweaver
整合进去。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
/**
* Controller层的日志封装
* @author Summerday
*/
@Data
@ToString
public class WebLog implements Serializable {
private static final long serialVersionUID = 1L;
// 操作描述
private String description;
// 操作时间
private Long startTime;
// 消耗时间
private Integer timeCost;
// URL
private String url;
// URI
private String uri;
// 请求类型
private String httpMethod;
// IP地址
private String ipAddress;
// 请求参数
private Object params;
// 请求返回的结果
private Object result;
// 操作类型
private String methodType;
}
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 描述
*/
String description() default "";
/**
* 方法类型 INSERT DELETE UPDATE OTHER
*/
MethodType methodType() default MethodType.OTHER;
}
@RestController
public class HelloController {
@PostMapping("/hello")
@Log(description = "hello post",methodType = MethodType.INSERT)
public String hello(@RequestBody User user) {
return "hello";
}
@GetMapping("/hello")
@Log(description = "hello get")
public String hello(@RequestParam("name") String username, String hobby) {
int a = 1 / 0;
return "hello";
}
}
用@Aspect注解标注标识切面,用@PointCut定义切点。
/**
* 定义切面
* @author Summerday
*/
@Aspect
@Component
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
/**
* web层切点
* 1. @Pointcut("execution(public * com.hyh.web.*.*(..))") web层的所有方法
* 2. @Pointcut("@annotation(com.hyh.annotation.Log)") Log注解标注的方法
*/
@Pointcut("@annotation(com.hyh.annotation.Log)")
public void webLog() {
}
}
这里使用环绕通知,
/**
* 定义切面
* @author Summerday
*/
@Aspect
@Component
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
/**
* web层切点
* 1. @Pointcut("execution(public * com.hyh.web.*.*(..))") web层的所有方法
* 2. @Pointcut("@annotation(com.hyh.annotation.Log)") Log注解标注的方法
*/
@Pointcut("@annotation(com.hyh.annotation.Log)")
public void webLog() {
}
/**
* 环绕通知
*/
@Around("webLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//获取请求对象
HttpServletRequest request = getRequest();
WebLog webLog = new WebLog();
Object result = null;
try {
log.info("=================前置通知=====================");
long start = System.currentTimeMillis();
result = joinPoint.proceed();
log.info("=================返回通知=====================");
long timeCost = System.currentTimeMillis() - start;
// 获取Log注解
Log logAnnotation = getAnnotation(joinPoint);
// 封装webLog对象
webLog.setMethodType(logAnnotation.methodType().name());
webLog.setDescription(logAnnotation.description());
webLog.setTimeCost((int) timeCost);
webLog.setStartTime(start);
webLog.setIpAddress(request.getRemoteAddr());
webLog.setHttpMethod(request.getMethod());
webLog.setParams(getParams(joinPoint));
webLog.setResult(result);
webLog.setUri(request.getRequestURI());
webLog.setUrl(request.getRequestURL().toString());
log.info("{}", JSONUtil.parse(webLog));
} catch (Throwable e) {
log.info("==================异常通知=====================");
log.error(e.getMessage());
throw new Throwable(e);
}finally {
log.info("=================后置通知=====================");
}
return result;
}
/**
* 获取方法上的注解
*/
private Log getAnnotation(ProceedingJoinPoint joinPoint) {
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
return method.getAnnotation(Log.class);
}
/**
* 获取参数 params:{"name":"天乔巴夏"}
*/
private Object getParams(ProceedingJoinPoint joinPoint) {
// 参数名
String[] paramNames = getMethodSignature(joinPoint).getParameterNames();
// 参数值
Object[] paramValues = joinPoint.getArgs();
// 存储参数
Map<String, Object> params = new LinkedHashMap<>();
for (int i = 0; i < paramNames.length; i++) {
Object value = paramValues[i];
// MultipartFile对象以文件名作为参数值
if (value instanceof MultipartFile) {
MultipartFile file = (MultipartFile) value;
value = file.getOriginalFilename();
}
params.put(paramNames[i], value);
}
return params;
}
private MethodSignature getMethodSignature(ProceedingJoinPoint joinPoint) {
return (MethodSignature) joinPoint.getSignature();
}
private HttpServletRequest getRequest() {
ServletRequestAttributes requestAttributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return requestAttributes.getRequest();
}
}
这里处理webLog的方式有很多种,考虑性能,可以采用异步方式存入数据库,相应代码已经上传至Gitee。
POST http://localhost:8081/hello
Content-Type: application/json
{ "id" : 1, "username" : "天乔巴夏", "age": 18 }
结果如下:
=================前置通知=====================
=================返回通知=====================
{"ipAddress":"127.0.0.1","description":"hello post","httpMethod":"POST","params":{"user":{"id":1,"age":18,"username":"天乔巴夏"}},"uri":"/hello","url":"http://localhost:8081/hello","result":"hello","methodType":"INSERT","startTime":1605596028383,"timeCost":28}
=================后置通知=====================
本文内容均为对优秀博客及官方文档总结而得,原文地址均已在文中参考阅读处标注。最后,文中的代码样例已经全部上传至Gitee:https://gitee.com/tqbx/springboot-samples-learn,另有其他SpringBoot的整合哦。
—END—
关注作者微信公众号 —《JAVA烂猪皮》
了解更多java后端架构知识以及最新面试宝典
你点的每个好看,我都认真当成了
看完本文记得给作者点赞+在看哦~~~大家的支持,是作者源源不断出文的动力
作者:天乔巴夏 出处:https://www.cnblogs.com/summerday152/p/13994673.html
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有