本文是关于如何实现一个简单的Spring AOP日志部分。边肖觉得挺实用的,所以想分享给大家学习。希望大家看完这篇文章能有所收获。话不多说,让我们和边肖一起看看。
AOP
1.什么是 AOP ?
AOP被称为面向方面编程,它被翻译为面向方面编程。它是通过预编译和运行时动态代理实现核心业务逻辑以外的横切行为统一维护的技术。面向对象程序设计是面向对象程序设计的补充和扩展。AOP可以隔离业务逻辑的各个部分,从而减少模块之间的耦合,将影响多个类的公共行为封装成一个可重用的模块,从而提高程序的可重用性,提高开发效率,提高系统的可操作性和可维护性。
2.为什么要用 AOP ?
在实际的Web项目开发中,我们经常需要实现日志记录、性能统计、安全控制、事务处理、异常处理等功能。如果我们为每个级别的每个类独立编写这部分代码,那么随着时间的推移,代码将变得难以维护,所以我们将这些函数从业务逻辑代码中分离出来,并将其聚合起来进行维护,我们可以灵活地选择在哪里使用这些代码。
00-1010概念概念理解建议截取连接点后要执行的代码。通知分为五种类型:预通知、后通知、异常通知、最终通知和环绕通知,如日志记录、性能统计、安全控制、事务处理、异常处理等。解释何时做关节点的截取点。例如被拦截的方法、对类成员的访问以及异常处理程序块的执行等。您还可以嵌套到其他可以使用Joint PointSpring通知的地方。通过与方法相关的切入点截取连接点的定义指定了要通知哪个方法,并解释了在哪里剪切类的定义。包含切入点和建议的定义部分是通知和切入点的组合。切入点选择的目标对象,即要通知的对象;因为Spring AOP是通过代理模式实现的,所以这个对象始终是通过代理对象本身的业务逻辑进行编织,并将切面应用到目标对象的过程,从而创建AOP代理对象。编织可以在编译时、类加载时和运行时完成,而Spring通过在运行时完成截止点来定义将通知哪些连接点。Introduction可以在运行时为类动态添加方法和字段。Spring允许向所有目标对象引入新的接口。引言是介绍基于一个接口/类的新的接口增强功能AOP代理。Spring AOP可以使用JDK动态代理或者CGLIB代理,前者基于接口,后者基于类,使用代理将facets
3.AOP 的核心概念
Spring AOP
AOP应用到目标对象,这是Spring框架中的核心内容。在Spring中,AOP代理可以通过JDK动态代理或者CGLIB代理CglibAopProxy来实现。Spring中的AOP代理由Spring的IOC容器生成和管理,其依赖关系也由IOC容器管理。
1.简介
注意@Aspect将一个java类定义为切片类@Pointcut定义了一个切入点,它可以是一个正则表达式,比如下面例子中一个包下的所有函数。也可以是注释等。@在切入点开始处切入内容之前@在切入点结束处切入内容之后@在切入点之后返回处理逻辑之后返回内容@在切入点前后左右切入内容,自己控制什么时候执行切入点自己的内容@AfterThrowing用于在切入点部分抛出异常之后处理处理逻辑@Order(100)AOP段执行顺序,@Be。
fore 数值越小越先执行,@After 和 @AfterReturning 数值越大越先执行
利用 AOP 实现 Web 日志处理
1.构建项目
2.添加依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 热部署模块 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <!-- 这个需要为 true 热部署才有效 --> </dependency> <!-- Spring AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies>
3.Web 日志注解
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface ControllerWebLog { String name();//所调用接口的名称 boolean intoDb() default false;//标识该条操作日志是否需要持久化存储}
4.实现切面逻辑
@Aspect@Component@Order(100)public class WebLogAspect { private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class); private ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>(); /** * 横切点 */ @Pointcut("execution(public * cn.zwqh.springboot.controller..*.*(..))") public void webLog() { } /** * 接收请求,并记录数据 * @param joinPoint * @param controllerWebLog */ @Before(value = "webLog()&& @annotation(controllerWebLog)") public void doBefore(JoinPoint joinPoint, ControllerWebLog controllerWebLog) { // 接收到请求 RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) ra; HttpServletRequest request = sra.getRequest(); // 记录请求内容,threadInfo存储所有内容 Map<String, Object> threadInfo = new HashMap<>(); logger.info("URL : " + request.getRequestURL()); threadInfo.put("url", request.getRequestURL()); logger.info("URI : " + request.getRequestURI()); threadInfo.put("uri", request.getRequestURI()); logger.info("HTTP_METHOD : " + request.getMethod()); threadInfo.put("httpMethod", request.getMethod()); logger.info("REMOTE_ADDR : " + request.getRemoteAddr()); threadInfo.put("ip", request.getRemoteAddr()); logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); threadInfo.put("classMethod", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs())); threadInfo.put("args", Arrays.toString(joinPoint.getArgs())); logger.info("USER_AGENT"+request.getHeader("User-Agent")); threadInfo.put("userAgent", request.getHeader("User-Agent")); logger.info("执行方法:" + controllerWebLog.name()); threadInfo.put("methodName", controllerWebLog.name()); threadLocal.set(threadInfo); } /** * 执行成功后处理 * @param controllerWebLog * @param ret * @throws Throwable */ @AfterReturning(value = "webLog()&& @annotation(controllerWebLog)", returning = "ret") public void doAfterReturning(ControllerWebLog controllerWebLog, Object ret) throws Throwable { Map<String, Object> threadInfo = threadLocal.get(); threadInfo.put("result", ret); if (controllerWebLog.intoDb()) { //插入数据库操作 //insertResult(threadInfo); } // 处理完请求,返回内容 logger.info("RESPONSE : " + ret); } /** * 获取执行时间 * @param proceedingJoinPoint * @return * @throws Throwable */ @Around(value = "webLog()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object ob = proceedingJoinPoint.proceed(); Map<String, Object> threadInfo = threadLocal.get(); Long takeTime = System.currentTimeMillis() - startTime; threadInfo.put("takeTime", takeTime); logger.info("耗时:" + takeTime); threadLocal.set(threadInfo); return ob; } /** * 异常处理 * @param throwable */ @AfterThrowing(value = "webLog()", throwing = "throwable") public void doAfterThrowing(Throwable throwable) { RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) ra; HttpServletRequest request = sra.getRequest(); // 异常信息 logger.error("{}接口调用异常,异常信息{}", request.getRequestURI(), throwable); }}
5.测试接口
@RestController@RequestMapping("/user")public class UserController { @GetMapping("/getOne") @ControllerWebLog(name = "查询", intoDb = true) public String getOne(Long id, String name) { return "1234"; }}
6.运行测试
浏览器请求:http://127.0.0.1:8080/user/getOne?id=1&name=zwqh ,可以看到后台日志输出:
日志记录只是一个简单的示例,而 Spring AOP 的应用让整个系统变的更加有条不紊,在其他场景应用也很强大。 它帮助我们降低模块间耦合度,提高程序复用性,提高开发效率,提高系统可做性和可维护性。
以上就是Spring AOP如何实现简单的日志切面,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注行业资讯频道。
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/51145.html