时间:2021-05-20
前言
AOP 是 Aspect Oriented Program (面向切面)的编程的缩写。他是和面向对象编程相对的一个概念。在面向对象的编程中,我们倾向于采用封装、继承、多态等概念,将一个个的功能在对象中来实现。但是,我们在实际情况中也发现,会有另外一种需求就是一类功能在很多对象的很多方法中都有需要。例如有一些对数据库访问的方法有事务管理的需求,有很多方法中要求打印日志。按照面向对象的方式,那么这些相同的功能要在很多地方来实现或者在很多地方来调用。这就非常繁琐并且和这些和业务不相关的需求耦合太紧密了。所以后来就出现了面向切面的编程来解决这一类问题,并对面向对象的编程做了很好的补充
概念
要很好的理解面向切面的编程,先要理解 AOP 的一些概念。在 Java 中 AspectJ 比较完整的实现了 AOP 的功能,但是使用起来也比较复,所以这里主要是讨论 Spring 的 AOP 。Spring AOP 采用简单够用的原则,实现了 AOP 的核心功能。下面先说说 AOP 中的具体概念
SprinBoot AOP 实现
前面我们已经用好几章讲述了 SpringBoot 的基本使用。那么这里我们就用 SpringBoot 和 AOP 结合来实现一个输出所有 Rest 接口输入参数和返回参数的日志的功能。
实现 rest 服务功能。
根据前面的文章,我们先建立一个 SpingBoot 的工程如下图所示
demo 工程
SpringBoot 项目配置
我们对 SpringBoot 项目配置如下
server: port: 3030 servlet: context-path: /aop-demospring: jackson: date-format: yyyy-MM-dd HH:mm:ss serialization: indent-output: truelogging: level: com.yanggch: debug其中 jackson 相关配置是为了将对象输出成 json 字符串后能够格式化输出
实现一个 rest 接口的 Controller 类
在这里,我们实现两个 rest 接口。一个是返回 hello 信息。一个是根据输入返回登录信息。
package com.yanggch.demo.aop.web;import com.yanggch.demo.aop.domain.LoginEntity;import com.yanggch.demo.aop.domain.SecurityEntity;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import java.util.Date;/** * 安全相关 rest 服务 * * @author : 杨高超 * @since : 2018-05-27 */@RestController@RequestMapping("/api/v1/security")public class SecurityApi { @RequestMapping(value = "/login/{shopId}", method = RequestMethod.POST) public SecurityEntity login(@RequestBody LoginEntity loginEntity, @PathVariable Long shopId) { SecurityEntity securityEntity = new SecurityEntity(); securityEntity.setShopId(shopId); securityEntity.setAccount(loginEntity.getAccount()); securityEntity.setPwd(loginEntity.getPwd()); securityEntity.setLoginTime(new Date()); return securityEntity; } @RequestMapping(value = "/echo/{name}", method = RequestMethod.GET) public String login(@PathVariable String name) { return "hello," + name; }}先在我们要通过 AOP 功能将所有 Rest 接口的输入参数和返回结果输出到日志中。
实现 Web Aop 功能。
package com.yanggch.demo.aop.comment;import com.fasterxml.jackson.databind.ObjectMapper;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * web 接口日志 * * @author : 杨高超 * @since : 2018-05-27 */@Aspect@Componentpublic class WebLogAspect { private static Logger log = LoggerFactory.getLogger(WebLogAspect.class); private final ObjectMapper mapper; @Autowired public WebLogAspect(ObjectMapper mapper) { this.mapper = mapper; } @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)") public void webLog() { } @Before("webLog()") public void doBefore(JoinPoint joinPoint) { for (Object object : joinPoint.getArgs()) { if ( object instanceof MultipartFile || object instanceof HttpServletRequest || object instanceof HttpServletResponse ) { continue; } try { if (log.isDebugEnabled()) { log.debug( joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + " : request parameter : " + mapper.writeValueAsString(object) ); } } catch (Exception e) { e.printStackTrace(); } } } @AfterReturning(returning = "response", pointcut = "webLog()") public void doAfterReturning(Object response) throws Throwable { if (response != null) { log.debug("response parameter : " + mapper.writeValueAsString(response)); } }}这里有几个需要注意的地方,
测试
在前台通过 postman 发起请求,后台日志输入结果如下
2018-05-27 19:58:42.941 DEBUG 86072 --- [nio-3030-exec-4] c.yanggch.demo.aop.comment.WebLogAspect : com.yanggch.demo.aop.web.SecurityApi.login : request parameter : {
"account" : "yanggch",
"pwd" : "123456"
}
2018-05-27 19:58:42.941 DEBUG 86072 --- [nio-3030-exec-4] c.yanggch.demo.aop.comment.WebLogAspect : com.yanggch.demo.aop.web.SecurityApi.login : request parameter : 2001
2018-05-27 19:58:42.942 DEBUG 86072 --- [nio-3030-exec-4] c.yanggch.demo.aop.comment.WebLogAspect : response parameter : {
"shopId" : 2001,
"account" : "yanggch",
"pwd" : "123456",
"loginTime" : "2018-05-27 11:58:42"
}
2018-05-27 19:58:45.796 DEBUG 86072 --- [nio-3030-exec-5] c.yanggch.demo.aop.comment.WebLogAspect : com.yanggch.demo.aop.web.SecurityApi.echo : request parameter : "yanggch"
2018-05-27 19:58:45.796 DEBUG 86072 --- [nio-3030-exec-5] c.yanggch.demo.aop.comment.WebLogAspect : response parameter : "hello,yanggch"
由此可见,我们虽然没有在 rest 接口方法中写输出日志的代码,但是通过 AOP 的方式可以自动的给各个 rest 入口方法中添加上输出入口参数和返回参数的代码并正确执行。
其他说明
前面提到了 Advice 的类型和 Pointcut 的 AOP 表达式语言。具体参考如下。
Advice 类型
AOP 表达式语言
1、方法参数匹配
@args()
2、方法描述匹配
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
其中 returning type pattern,name pattern, and parameters pattern是必须的.
. ret-type-pattern:可以为表示任何返回值,全路径的类名等.
*. name-pattern:指定方法名, *代表所有
.set代表以set开头的所有方法.
. parameters pattern:指定方法参数(声明的类型),(..)代表所有参数,()代表一个参数
. (,String)代表第一个参数为任何值,第二个为String类型.
3、当前AOP代理对象类型匹配
4、目标类匹配
@target()
@within()
5、标有此注解的方法匹配
@annotation()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
spring的事务控制本质上是通过aop实现的。在springboot中使用时,可以通过注解@Transactional进行类或者方法级别的事务控制,也可以自己
spring的事务控制本质上是通过aop实现的。在springboot中使用时,可以通过注解@Transactional进行类或者方法级别的事务控制,也可以自己
前言SpringBoot在所有内部日志中使用CommonsLogging,但是默认配置也提供了对常用日志的支持,如:JavaUtilLogging,Log4J,
因为公司业务需要,需要把性能日志和业务日志分开打印,用elk收集处理,所以需要对不同的业务的日志,打印到不同文件。使用的是springboot自带的logbac
AOP日志框架实现JDK动态代理实现日志框架首先,在项目包com.ay.test下创建业务接口类BusinessClassService,具体代码如下:Busi