当前位置: 首页 > article >正文

Spring-aop介绍

1、核心术语介绍

AOP 切面编程核心术语如下:

术语含义
目标(Target)被通知的对象
代理(Proxy)向目标对象应用通知之后创建的代理对象
连接点(JoinPoint)目标对象的所属类中,定义的所有方法均为连接点
切入点(Pointcut)被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点)
通知(Advice)增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情
切面(Aspect)切入点(Pointcut)+通知(Advice)
Weaving(织入)将通知应用到目标对象,进而生成代理对象的过程动作

上述描述比较抽象,上述术语间的关系如下图所示:

2、spring 实现方式

对于有接口的bean,采用JDK Proxy去创建代理对象。对于没有实现接口的对象,使用 Cglib 生成一个被代理对象的子类来作为代理。

2.1 JDK Proxy

采用java反射机制实现。一个简单的示例如下所示:

// 定义接口
public interface TenantService {
    void doSomething(String tenantId);
}

// 定义实现
public class TenantServiceImpl implements TenantService {
    @Override
    public void doSomething(String tenantId) {
        System.out.println("Doing something for tenant: " + tenantId);
    }
}



public class JdkProxyExample {

    public static void main(String[] args) {
        // 创建真实对象
        TenantService realTenantService = new TenantServiceImpl();

        // 创建 InvocationHandler
        InvocationHandler handler = new InvocationHandler() {
            private final TenantService tenantService = realTenantService;

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String tenantId = (String) args[0];
                System.out.println("Before method call - Tenant ID: " + tenantId);

                // 调用真实对象的方法
                method.invoke(tenantService, args);

                System.out.println("After method call - Tenant ID: " + tenantId);
                return null; // 无返回值方法返回 null
            }
        };

        // 创建代理对象
        TenantService proxyTenantService = (TenantService) Proxy.newProxyInstance(
                TenantService.class.getClassLoader(),
                new Class[]{TenantService.class},
                handler
        );

        // 调用代理对象的方法
        proxyTenantService.doSomething("tenant1");
    }
}

2.2 Cglib

Cglib 通过字节码技术为现有类生成子类,并在子类中增强逻辑,从而实现AOP功能。

CGLIB 实现 AOP 的基本原理

  • 生成子类: CGLIB 通过生成现有类的子类来实现增强逻辑。这意味着原始类的所有方法都会在子类中存在,并且可以在子类中添加新的方法或覆盖父类的方法。
  • 方法拦截: 在子类中,CGLIB 可以通过拦截器(Interceptor)来控制方法的调用。拦截器可以在方法调用前后执行自定义逻辑。
  • 动态代理: CGLIB 使用动态代理机制来创建增强的对象。这种代理机制允许在运行时动态地生成增强的类实例。

CGLIB 的核心组件

  • Enhancer:负责生成增强类的工具类。
  • MethodInterceptor:方法拦截器,用于在方法调用前后执行自定义逻辑。
  • MethodProxy:方法代理,提供了对方法的代理调用。

代码示例:

// 业务实现,不实现接口
public class TargetClass {
    public void doSomething() {
        System.out.println("TargetClass doing something...");
    }
}


// 拦截器,在方法执行前后织入逻辑
public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method call");
        Object result = proxy.invokeSuper(obj, args); // 调用原始方法
        System.out.println("After method call");
        return result;
    }
}


public class CglibAopExample {
    public static void main(String[] args) {
        // 创建 Enhancer 实例
        Enhancer enhancer = new Enhancer();
        
        // 设置父类
        enhancer.setSuperclass(TargetClass.class);
        
        // 设置方法拦截器
        enhancer.setCallback(new MyMethodInterceptor());
        
        // 创建增强对象
        TargetClass enhancedObject = (TargetClass) enhancer.create();
        
        // 调用增强对象的方法
        enhancedObject.doSomething();
    }
}

2.3 JDK Proxy VS Cglib

对2种方式的优缺点进行对比。

特性

JDK ProxyCGLIB
适用范围仅适用于实现了接口的类适用于所有类(包括未实现接口的类)
实现方式通过 Proxy.newProxyInstance 方法创建代理对象通过 Enhancer 类生成子类
性能通常比 CGLIB 略慢性能较好,因为避免了接口查找和反射调用
灵活性需要实现接口,限制较大更加灵活,可以代理任意类
兼容性兼容性较好,广泛支持兼容性良好,但在某些旧版本 JVM 上可能需要额外配置
代理类型动态代理动态代理
代码侵入性低,只需要实现接口较高,需要生成子类
调试难度较低,因为不需要生成额外的类较高,因为生成了额外的子类
扩展性扩展性较好,可以通过接口进行扩展扩展性一般,需要修改子类的生成逻辑
安全性较好,因为基于接口一般,因为涉及子类生成
应用场景适合于需要代理实现了接口的类适合于需要代理任意类,包括未实现接口的类

从上述分析来看,JDK proxy的方式并没有明显的优点,那么为什么spring 会选择JDK proxy作为默认aop实现呢?

我认为有以下几个原因:

  • 兼容性:JDK proxy 是jdk自身的实现,兼容性更高
  • 安全性:基于接口实现,安全性更高
  • 历史原因:因为spring最开始就是选择的JDK proxy作为实现方式,一脉相承。

http://www.kler.cn/news/360364.html

相关文章:

  • Adobe Acrobat DC 打印PDF文件,没有打印出注释的解决方法
  • 【Linux】文件IO深度解析:文件描述符与重定向的奥秘
  • LeetCode 1456.定长子串中元音的最大数目
  • ESP32移植Openharmony外设篇(1)MQ-2烟雾传感器
  • 独著的出版流程是怎样的?
  • rabbitMQ消息重复问题怎么解决的?
  • API 接口封装技术详解
  • 【JVM】—深入理解ZGC回收器—关键技术详解
  • M3D: Advancing 3D Medical Image Analysis with Multi-Modal Large Language Models
  • Scala内部类和Java内部类的不同
  • TCP/IP 寻址
  • 力扣面试150 完全二叉树的节点个数 树的高度
  • Python酷库之旅-第三方库Pandas(148)
  • 基于vue框架的的地铁站智慧管理系统的设计n09jb(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
  • Java:IDEA生成JavaDoc文档
  • 2024年双十一最建议买的东西,女生双十一必买清单,双11好物推荐
  • Git使用(什么是工作区、暂存区、本地库、远程库、Pycharm部署)
  • 【C++篇】深度解析类与对象(中)
  • ASP.NET.Web应用程序(.NET Framework)添加Swagger本地Debuge成功打开接口展示界面,发布服务器无法打开接口展示界面
  • 【ChatGPT】如何限定 ChatGPT 的回答范围