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 是无能为力的。

相关推荐
©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页