设计模式-代理模式

简介

在Java中,动态代理模式就是在程序运行中动态创建代理类或者对象的机制。

实现动态代理的两种方式:

  • JDK动态代理:JDK原生自带的动态代理,不需要引入第三方框架和类库。java.lang.reflect包下的InvocationHandler接口和java.lang.reflect.Proxy类提供了动态代理的能力
  • Cglib动态代理:第三方代理类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能扩展

优缺点

JDK动态代理:

优点:

  • JDK动态代理的优势在于其内置于Java标准库之中,无需引入额外的第三方库,简化了开发环境配置,降低了对外部依赖的管理成本。实现上,它提供了直观易用的API,开发者仅需实现InvocationHandler接口并配置Proxy对象,即可轻松实现对目标接口的代理增强
  • 其核心运作机制基于Java的反射API,相较于底层字节码操作,这种方式在实现层面上更为简洁明了,对于开发者来说易于理解和调试
  • JDK动态代理严格遵循面向对象设计模式中的开闭原则,即允许在不对原有类代码进行修改的前提下,通过代理机制透明地向系统中添加新功能,提高了系统的可扩展性和可维护性。

缺点:

  • 性能方面,若代理对象数量庞大或高并发场景下,由于频繁的反射操作会导致一定的性能损耗,相比字节码生成技术可能表现得不够高效
  • 功能限制上,JDK动态代理仅适用于实现了接口的类,对于未实现接口的普通类或者被private、final等关键字修饰的方法和类,它无法进行代理
  • 由于基于接口代理的特性,JDK动态代理并不支持通过构造函数注入来初始化代理对象,这意味着所有代理对象的创建都必须基于接口方法的调用

JDK动态代理

JDK动态代理机制的实现相对简便易行,关键在于实现InvocationHandler接口,该接口中只有一个核心方法——invoke()。通过重写invoke()方法,开发者能够有效地对代理类的方法调用进行拦截,并在此过程中对代理类的功能进行拓展与增强。同时,这一机制赋予了开发者在调用前后插入自定义逻辑的能力,例如进行前置处理以验证调用权限、收集相关数据,或是后置处理以执行清理任务、记录执行结果等操作。

要构建一个动态代理实例,只需借助Java标准库中的Proxy类所提供的newProxyInstance()方法。该方法接收三个重要参数:首先是一个ClassLoader对象,它是用于加载代理类的类加载器;其次是目标类实现的所有接口构成的Class<?>数组,表明代理类将实现这些接口的方法;最后是一个InvocationHandler对象,通常情况下,这是开发者之前实现的自定义InvocationHandler子类的实例,它负责处理代理实例上的方法调用。

总之,通过整合以上步骤,JDK动态代理机制能在运行时无缝创建并返回一个目标类的代理实例,该实例既保留了原始类的功能,又在其基础上融入了开发者定制的额外逻辑,极大地提升了程序的灵活性和可扩展性。

创建一个接口并实现方法打印日志:

public interface UserService {

    void add();
}
@Service
public  class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("用户新增--------------");
    }
}

自定义一个InvocationHandler处理类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理
 * @author yiridancan
 **/
public class JDKProxyTest implements InvocationHandler {

    /**
     * 创建一个代理的目标类
     **/
    private Object target;

    /**
     * 利用构造函数对目标类进行赋值
     **/
    public JDKProxyTest(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("可以进行前置处理");
        Object invoke = method.invoke(target, args);
        System.out.println("可以进行后置处理");
        return invoke;
    }

    public Object getProxy(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
    }

}

测试代码:

    @Test
    public void proxyTest() {
        UserService service = new UserServiceImpl();
        JDKProxyTest jdkProxyTest = new JDKProxyTest(service);
        UserService userService = (UserService)jdkProxyTest.getProxy();
        userService.add();

        /*NoImplementsService service = new NoImplementsService();
        JDKProxyTest jdkProxyTest = new JDKProxyTest(service);
        NoImplementsService proxy = (NoImplementsService)jdkProxyTest.getProxy();
        proxy.add();*/
    }

运行结果

Cglib动态代理

Cglib库的核心机制依赖于MethodInterceptor接口及其配套的Enhancer工具类。为了实现动态代理功能,开发者首先需要实现MethodInterceptor接口,其中关键在于重写其intercept方法,以便在代理方法调用时执行特定的拦截逻辑。接下来,利用MethodProxy在拦截方法内部调用super类的方法,以维持原有的业务流程

随后,借助Enhancer对象来设定需要增强的目标类,这一步通过调用setSuperclass()方法完成,传入需要代理的类类型。进一步地,设置回调方法到Enhancer对象上,这是通过调用setCallback()方法并将自定义的MethodInterceptor实例作为参数传递,确保当代理类的方法被调用时,能触发我们定制的拦截行为

最后,调用Enhancer的create()方法,Cglib会在运行时运用字节码技术动态地创建出目标类的子类作为代理类。这一过程犹如魔术般地构建了一个新的类结构,该子类不仅继承了原始类的所有特性,还包含了我们在MethodInterceptor中编写的额外功能逻辑。这样,便成功实现了对目标类功能的灵活扩展和控制

创建一个没有接口的普通类:

@Service
public class NoInterfacesImple {

    public void sayHello() {
        System.out.println("hello");
    }
}

自定义Cglib拦截器:


public class CglibProxyTest implements MethodInterceptor{

    //创建一个Enhancer对象
    private Enhancer enhancer = new Enhancer();
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("前置处理============================");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("后置处理============================");
        return result;
    }

    public Object getProxy(Class clazz){
        //设置enhancer需要增强的类
        enhancer.setSuperclass(clazz);
        //设置回调方法
        enhancer.setCallback(this);
        //通过字节码动态创建代理类
        return enhancer.create();
    }
}

测试代码:

    @Test
    public void cglibProxyTest(){
        CglibProxyTest cglibProxyTest = new CglibProxyTest();
        NoInterfacesImple noInterfacesImple = (NoInterfacesImple)cglibProxyTest.getProxy(NoInterfacesImple.class);
        noInterfacesImple.sayHello();
    }

结论

JDK动态代理和CGLIB动态代理在实现类的行为扩展时,确实都遵循了设计模式中的开闭原则,即它们允许在不修改原始类代码的基础上增加额外的功能。不过,两者实现这一原则的技术手段和适用场景有所不同:

在实际应用中,如果需要代理的类已经实现了某个接口,并且并发访问量不是特别高,那么可以选择使用JDK动态代理。这种方式实现相对简单直观,通过实现InvocationHandler接口以及Java标准库提供的Proxy类,开发人员能够便捷地为接口方法添加额外逻辑,从而实现对代理类功能的扩展。

相反,当代理的目标类是一个普通类,即没有实现任何接口,或者在高并发场景下追求更高的执行效率时,CGLIB动态代理则是一个更好的选择。CGLIB通过字节码技术动态生成目标类的子类来实现代理,这种底层的操作方式虽然复杂度相对较高,但在并发环境下往往能展现出优于JDK动态代理的性能表现,因为它规避了反射操作带来的额外开销。

  • 32
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值