动态代理——既有落地也有思想

前言

就是个思想,只要是知道怎么回事就行,反正我开发这么久用到的就是SpringAOP并没有别的使用场景。
SpringAOP也就是一句话:
Spring使用这种根据目标对象是否实现接口来选择代理模式的策略,主要是为了提供最大程度的灵活性和兼容性。

如果目标对象实现了接口,使用JDK代理可以提供更好的性能。
如果目标对象没有实现接口,CGLIB代理能确保AOP仍然可以进行,确保不会因为实现方式的不同而限制了AOP的应用。

一、什么是代理模式

通过一个代理对象来间接访问目标对象的方法。代理可以是静态的也可以是动态的。
其实就是一个人替你去办你的事情

二、代理的是的分类

静态代理
动态代理:JDK动态代理、CGLIB动态代理

三、静态代理模式的代码实现

静态代理是指由程序员或特定的工具自动生成代理类的源代码,然后编译生成代理类的.class文件。每一个代理类只能为一个接口服务,如果要代理的方法很多,代理类的工作就会非常繁琐。

public interface Subject {
    void doAction();
}

public class RealSubject implements Subject {
    public void doAction() {
        System.out.println("Performing an action in RealSubject");
    }
}

public class StaticProxy implements Subject {
    private Subject realSubject;

    public StaticProxy(Subject realSubject) {
        this.realSubject = realSubject;
    }

    public void doAction() {
        System.out.println("StaticProxy: Pre-processing before RealSubject action");
        realSubject.doAction();
        System.out.println("StaticProxy: Post-processing after RealSubject action");
    }
}

四、动态代理模式的代码实现

方式一:JDK
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface ServiceInterface {
    void doSomething();
}

class RealService implements ServiceInterface {
    public void doSomething() {
        System.out.println("Doing something in RealService");
    }
}

class JdkDynamicProxyHandler implements InvocationHandler {
    private final Object target;

    public JdkDynamicProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK Proxy - Before method: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("JDK Proxy - After method: " + method.getName());
        return result;
    }
}

public class JdkProxyDemo {
    public static void main(String[] args) {
        ServiceInterface realService = new RealService();
        ServiceInterface proxyInstance = (ServiceInterface) Proxy.newProxyInstance(
            ServiceInterface.class.getClassLoader(),
            new Class<?>[]{ServiceInterface.class},
            new JdkDynamicProxyHandler(realService)
        );
        
        proxyInstance.doSomething();
    }
}

方式二:CDLIB

引入POM——maven坐标

<!--Maven为例,添加CGLIB依赖 -->
<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version> <!-- 请使用适合你项目的CGLIB版本 -->
    </dependency>
</dependencies>

代码展示

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

class RealServiceNoInterface {
    public void doSomething() {
        System.out.println("Doing something in RealServiceNoInterface");
    }
}

class CglibDynamicProxyInterceptor implements MethodInterceptor {
    
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB Proxy - Before method: " + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("CGLIB Proxy - After method: " + method.getName());
        return result;
    }

    public static Object createCglibProxy(Object target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new CglibDynamicProxyInterceptor());
        return enhancer.create();
    }
}

public class CglibProxyDemo {
    public static void main(String[] args) {
        RealServiceNoInterface realServiceNoInterface = new RealServiceNoInterface();
        RealServiceNoInterface cglibProxyInstance = (RealServiceNoInterface) CglibDynamicProxyInterceptor.createCglibProxy(realServiceNoInterface);
        
        cglibProxyInstance.doSomething();
    }
}

五、springAOP中的代理模式是什么

Spring AOP 使用两种代理模式:JDK 动态代理和 CGLIB 代理
Spring 框架在决定使用哪种方式时遵循以下规则:
1、如果被代理的目标对象实现了至少一个接口,则默认使用 JDK 动态代理。
在这种情况下,Spring 会动态创建一个实现了目标对象所有接口的代理对象,在这个代理对象中实现了接口中的所有方法。
当这些方法被调用时,实际上它们会转发到一个 InvocationHandler 实例上,然后由此可以在调用实际目标对象方法之前或之后插入额外的处理逻辑(比如前置/后置通知、异常处理等)。

2、如果被代理的目标对象没有实现任何接口,则会使用 CGLIB 创建代理。
CGLIB 库通过继承目标类并在子类中覆盖方法来生成代理。
CGLIB 代理可以被用来创建一个目标类的子类,并且所有非 final 的方法调用都会被拦截。
与 JDK 动态代理类似,这也允许在调用真正的目标对象方法之前或之后加入额外的逻辑。

实现机制:

JDK 动态代理:

使用 java.lang.reflect.Proxy 类的 newProxyInstance 方法,并传入一个 InvocationHandler 实例。
在 InvocationHandler 的 invoke 方法中实现了对目标方法的调用,以及横切逻辑的执行。

CGLIB 代理:

使用 net.sf.cglib.proxy.Enhancer 类,并设置一个 MethodInterceptor。
在 MethodInterceptor 的 intercept 方法中实现对目标方法的调用和横切逻辑的执行。

使用代理模式的原因:

1、解耦:Spring AOP 基于代理模式实现,目的是将横切关注点(特别是与业务无关的关注点,如事务管理、日志记录等)与业务逻辑代码分离,提高模块之间的解耦。

2、灵活性:Spring AOP 提供灵活的代理选择,根据目标对象是否实现了接口来决定使用 JDK 动态代理还是 CGLIB 代理。这样一来,使用 AOP 的用户几乎不用关心底层是利用哪种代理技术实现的(除非对性能影响敏感),Spring 会为他们选择最佳方案。

3、动态代理提供的AOP支持:动态代理技术提供了在运行时不修改原始代码的前提下进行方法增强的能力,为实现运行时横切逻辑提供了技术手段。

4、合适的抽象:通过代理模式,Spring AOP 能为开发者提供一个足够简单的编程模型,同时它抽象的级别让开发者不用关心此技术的复杂性。

5、性能和兼容性:虽然 CGLIB 代理在某些情况下会比 JDK 代理有更好的性能表现(尤其是方法调用较为密集时),但 JDK 动态代理由于不需要额外的类库依赖,同时足够满足大多数场景,因此是一个默认选项。当然,当目标对象没有实现接口时,CGLIB代理是唯一选择。

总结:Spring AOP 选择在这两种动态代理方法之间自由切换,使其非常适合用来实现企业级应用的横切逻辑。通过让开发者使用简单的注解或配置来定义切面和通知,Spring AOP 使代码更加简洁、易维护,同时也保证了高效的执行。

六、业务中的使用场景

1. 远程代理(Remote Proxies)

用于对位于远程服务器上的对象进行操作。远程代理通过本地的代理对象来代表远程对象,使远程通信对客户端透明。
业务场景:
分布式系统中远程对象的访问,比如在一个微服务架构中调用另一个微服务的API。
远程方法调用(RMI)、Java Naming and Directory Interface (JNDI)、WebService 或 RESTful API 等技术的封装。

2. 虚拟代理(Virtual Proxies)

用于执行资源密集型操作的延迟初始化。虚拟代理通常保存对需要创建的对象的引用,直到对象被真正需要时才创建。
业务场景:

图像加载和处理程序,只在图像真正需要显示时才加载图像。
对象的懒加载,如 Hibernate 或 JPA 中的实体加载。

3. 保护代理(Protection Proxies)

控制对敏感对象的访问,它根据不同的访问权限提供对目标对象的访问。

业务场景:

系统中有权限控制的部分,不同的用户角色可以看到不同的内容。

4. 缓存代理(Cache Proxies)

为重复的请求提供临时存储以加速系统的响应速度。缓存代理可以保存运算结果,当相同请求再次发生时直接返回缓存的结果。

业务场景:

网页服务器使用缓存代理来缓存常见页面和图片,减少数据库查询次数。
应用程序缓存,比如使用 Spring Cache 等框架。

5. 智能引用代理(Smart Reference Proxies)

在目标对象被访问时,执行额外的动作,例如计算对象被引用的次数。

业务场景:

引用计数,用于资源的优雅释放(如数据库连接池、内存管理等)。
性能监测,记录方法的调用次数和响应时间。

6. 日志记录(Logging)

在执行真正操作之前或之后进行日志记录,方便调试和监控。

业务场景:

记录系统的操作日志,如用户对数据库的增删改查操作日志。
监控系统性能,方法调用时间,使得性能分析更简单。

7. 事务处理(Transaction Handling)

代理模式用于管理事务,保证一系列操作要么全部成功,要么完全不执行。

业务场景:

数据库操作和管理,提供事务性的数据库CRUD操作。
分布式事务处理,如使用 Spring Framework 的声明式事务管理。

8. 安全检查(Security)

在访问真实对象之前进行安全检查,确保操作者拥有执行操作的权限。

业务场景:

系统访问控制,如web 应用中用户权限验证。

9. 访问控制和过滤(Access Control and Filtering)

控制客户端对目标对象的访问和请求。

业务场景:

API 速率限制和控制。
跨域请求时的跨域资源共享(CORS)问题处理。
结论
代理模式提供了一个用于扩展和保护目标对象的非常强大的方法。但是,在实际应用中,代理模式可能会引入额外的复杂性和开销,因此应该在确有必要时才采用。通常来说,需要间接访问对象或希望在访问对象时提供额外处理时,代理模式都是一个很好的选择。

总结:

1、Spring框架默认会使用JDK动态代理作为AOP代理实现,如果被代理的类没有实现任何接口,则自动回退到CGLIB动态代理。

静态代理:

定义方式:

在编译时就确定了代理类和真实对象之间的关系。代理类通常是手动编写的,并且在代码中直接引用。

实现:

代理类需要显式地实现与目标接口相同的接口,并在内部持有一个对真实对象的引用。

优点:
	简单易理解,不需要复杂的编程技巧;
	在编译时就可以完全确定代理的行为。
缺点:
	每一个需要代理的类都必须有一个与之对应的代理类,这导致类文件数量成倍增加;
	代理类和真实对象之间的代码重复;
	可维护性差,如果接口发生变化,代理和真实对象都需要进行相应的修改。

动态代理:

定义方式:

在运行时动态创建代理并确定代理与真实对象之间的关系。代理类在程序运行时被创建,并且在代码中通常不直接引用。

实现:

通过反射机制,使用JDK提供的 Proxy 类或第三方库(如CGLIB)来动态生成代理类。

优点:
	减少了大量的重复代码,提高了代码复用率;
	系统更加灵活和可扩展,代理类在运行时创建,增强逻辑与业务逻辑解耦;
	一次性解决了多个类或多个方法的代理问题。
缺点:
	动态代理的实现更复杂,理解和应用门槛相对高;
	动态代理的性能略低于静态代理,因为涉及到反射机制(但对于现代JVM的优化,这点性能差别通常可以忽略);
	JDK动态代理只能代理接口,不支持类。
应用场景分析:
	如果目标对象较少,且不经常变更,可以考虑使用静态代理。
	如果想要一种更灵活且易于扩展的代理方式,可以选择动态代理。尤其是当有大量类需要代理时,
动态代理可以大大减少代码量。
	在面向切面编程(AOP)和模块化系统(像Spring框架)中,动态代理是实现解耦的关键技术。

Spring框架代理总结:

Spring AOP使用代理模式来实现横切关注点的模块化。它主要通过动态代理来实现,尽可能使用JDK动态代理以提高性能。
当代理的目标类没有实现接口时,Spring AOP会退回到使用CGLIB,从而确保任何类都可以被代理。

代理模式在Java开发中起着重要的作用,尤其是在创建可扩展和可维护的应用程序方面。
理解和掌握静态代理和动态代理在工程实践中至关重要,可使开发人员编写出更加高效、灵活的代码。

提示:有问题欢迎待见评论留言

  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值