Spring AOP 的使用和实现原理
Posted 一叶知秋V
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring AOP 的使用和实现原理相关的知识,希望对你有一定的参考价值。
软件工程有个基本原则,即关注点分离,不同的问题交给不同的部分去解决,面向切面编程 AOP 正是此种技术的体现,通用化功能代码的实现对应的就是所谓的切面(Aspect),业务功能代码和切面代码分开后,架构将变得高内聚低耦合。
AOP 的三种织入方式:
- 编译时织入:需要特殊的 Java 编译器,如 AspectJ;
- 类加载时织入:需要特殊的 Java 编译器,如 AspectJ 和 AspectWerkz;
- 运行时织入:Spring 采用的方式,通过动态代理的方式,实现简单,动态代理虽然有性能上的开销,但是好处就是不需要特殊的编译器和类加载器。
AOP 的主要名词概念:
- Aspect:通用功能的代码实现;
- Target:被织入 Aspect 的对象;
- Join Point:可以作为切入点的机会,所有方法都可以作为切入点;
- Pointcut:Aspect 实际被应用在的 Join Point,支持正则;
- Advice:类里的方法以及这个方法如何织入到目标方法的方式;
- Weaving:AOP 的实现过程。
Advice 的种类:
- 前置通知(Before);
- 后置通知(AfterReturning);
- 异常通知(AfterThrowing);
- 最终通知(After);
- 环绕通知(Around)。
1.Spring AOP的使用
这里我们基于 Spring Boot 构建项目,使用 AOP 需要添加 Maven 依赖:
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
这里我们创建 Aspect 文件通过 AOP 统一处理 request 请求日志:
import org.aspectj.lang.JoinPoint;
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;
@Aspect // 定义为切面类
@Component
public class RequestLogAspect
private static final Logger logger = LoggerFactory.getLogger(RequestLogAspect.class);
// 定义切入点
@Pointcut("execution(public * com.example.server.soa.web.controller..*.*(..))") // 支持正则
public void webLog()
// 方法执行之前切入
@Before("webLog()")
public void doBefore(JoinPoint joinPoint)
// 接收到请求, 记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
logger.info("url: ", request.getRequestURL().toString());
logger.info("ip: ", request.getRemoteAddr());
@After("webLog()")
public void doAfter()
logger.info("doAfter");
// 方法执行return之后切入
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret)
// 处理完请求, 返回内容
logger.info("response: ", ret);
@AfterThrowing("webLog()")
public void doAfterThrowing()
logger.info("doAfterThrowing");
2.Spring AOP的原理
Spring 提供了两种方式来生成代理对象:JdkProxy 和 Cglib,具体使用哪种方式生成,由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类实现了接口,则用 JdkProxy 来实现,否则使用 Cglib。
说明 | 实现机制 | 优点 | |
---|---|---|---|
JdkProxy(Jdk 动态代理技术) | 1、通过反射接受被代理的类,并且要求被代理的类必须实现一个接口; 2、JdkProxy 的核心是 InvocationHandler 接口和 Proxy 类; | 通过 Java 的内部反射机制实现的 | 反射机制在生成类的过程中比较高效 |
Cglib(一个代码生成的类库) | 1、以继承的方式动态生成目标类的代理; 2、是通过修改字节码来实现代理的,可以在运行时动态的生成某个类的子类; 3、如果某个类被 final 修饰,是无法使用 Cglib 来做动态代理的; | 借助 ASM 实现,ASM 是一种能够操作字节码的框架 | ASM 在生成类之后的执行过程中比较高效 |
掌握 Spring AOP 的原理,需要先看下代理模式:
代理模式就是接口 + 真正实现类 + 代理类组成的,其中真实实现类和代理类都是需要实现接口的,实例化的时候要使用代理类,所以 Spring AOP 需要做的是生成一个代理类来替换掉真实实现类以对外服务。
代理模式参考:https://blog.csdn.net/smartbetter/article/details/70834042
Spring 里的代理模式的实现:
- 真正实现类的逻辑包含在了 getBean() 方法里;
- getBean() 方法返回的实际上是 Proxy 的实例;
- Proxy 实例是 Spring 采用 JDK Proxy 或 Cglib 动态生成的;
getBean() 方法用于查找和实例化容器中的 Bean,这也是为什么 Spring AOP 只能作用于 Spring 容器中 Bean 的原因,对于不是使用 IOC 容器管理的对象,Spring AOP 是无能为力的。
以上是关于Spring AOP 的使用和实现原理的主要内容,如果未能解决你的问题,请参考以下文章