一、AOP是什么
AOP(Aspect-Oriented Programming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需 要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种 散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为 “Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低 模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为; 那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手 将这些剖开的切面复原,不留痕迹。
使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横 切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的 方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。然而殊途同归,实现AOP的技术特性却是相同的,分别为:
1、join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。
2、point cut(切入点):本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。
3、advice(通知):是point cut的执行代码,是执行“方面”的具体逻辑。
4、aspect(方面):point cut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。
5、introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的。有的AOP工具又将其称为mixin。
上述的技术特性组成了基本的AOP技术,大多数AOP工具均实现了这些技术。它们也可以是研究AOP技术的基本术语。
二、为什么要使用AOP
减少代码量。提高系统的一致性。使用场景例如:
1、日志系统
2、消息发布系统
三、如何使用AOP
1、在pom文件加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、添加配置文件类
package com.xyp.swaggertoexcel.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* @author xuyuanpeng
* @version 1.0
* @date 2019-04-28 17:37
*/
@Aspect
@Component
public class HttpRequestAspect {
private static final Logger log = LoggerFactory.getLogger(HttpRequestAspect.class);
public static long startTime;
public static long endTime;
/*@PointCut注解表示表示横切点,哪些方法需要被横切*/
/*切点表达式*/
@Pointcut("execution(public * com.xyp.swaggertoexcel.api.*.*(..))")
/*切点签名*/
public void print() {
}
@Around("print()")
public void around(ProceedingJoinPoint pjp) throws Throwable {
log.info("around proceed before……");
pjp.proceed();
log.info("around proceed after……");
}
/*@Before注解表示在具体的方法之前执行*/
@Before("print()")
public void before(JoinPoint joinPoint) {
log.info("前置切面before……");
startTime = System.currentTimeMillis();
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
String requestURI = request.getRequestURI();
String remoteAddr = request.getRemoteAddr(); //这个方法取客户端ip"不够好"
String requestMethod = request.getMethod();
String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
log.info("请求url=" + requestURI + ",客户端ip=" + remoteAddr + ",请求方式=" + requestMethod + ",请求的类名=" + declaringTypeName + ",方法名=" + methodName + ",入参=" + args);
}
/*@After注解表示在方法执行之后执行*/
@After("print()")
private void after() {
endTime = System.currentTimeMillis() - startTime;
log.info("后置切面after……");
}
/*@AfterReturning注解用于获取方法的返回值*/
@AfterReturning(pointcut = "print()", returning = "object")
public void getAfterReturn(Object object) {
log.info("本次接口耗时={}ms", endTime);
log.info("afterReturning={}", object.toString());
}
}
3、输出
2019-04-28 17:42:21.751 INFO 22620 — [nio-8080-exec-9] c.x.s.aop.HttpRequestAspect : around proceed before……
2019-04-28 17:42:21.751 INFO 22620 — [nio-8080-exec-9] c.x.s.aop.HttpRequestAspect : 前置切面before……
2019-04-28 17:42:21.751 INFO 22620 — [nio-8080-exec-9] c.x.s.aop.HttpRequestAspect : 请求url=/api/http/getDateByUrl,客户端ip=0:0:0:0:0:0:0:1,请求方式=POST,请求的类名=com.xyp.swaggertoexcel.api.IHttpProxyService,方法名=getDateByUrl,入参=[Ljava.lang.Object;@6890087
2019-04-28 17:42:21.753 INFO 22620 — [nio-8080-exec-9] c.x.s.aop.HttpRequestAspect : around proceed after……
2019-04-28 17:42:21.753 INFO 22620 — [nio-8080-exec-9] c.x.s.aop.HttpRequestAspect : 后置切面after……
2019-04-28 17:42:21.753 INFO 22620 — [nio-8080-exec-9] c.x.s.aop.HttpRequestAspect : 本次接口耗时=2ms
输出顺序
1、around 方法中的 调用pjp.proceed();之前
2、before方法
3、正式的请求切面的方法
4、around 方法中的 调用pjp.proceed();之后
5、after方法
四、参考
https://www.cnblogs.com/jingzhishen/p/4980551.html
https://blog.csdn.net/fanrenxiang/article/details/80844077