Java代理的三种方式

Java代理的三种方式

一、代理模式

  1. 什么是代理模式?

    代理模式是23种设计模式中的一种。代理模式是一种结构型设计模式,它允许为其他对象提供一个替代品或占位符,以控制对这个对象的访问。

  2. 代理模式的角色构成

    • 抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样代理类可以通过实现该接口来代理真实主题。
    • 真实主题(Real Subject):定义了代理所代表的真实对象。
    • 代理(Proxy):持有对真实主题的引用,并实现了与真实主题一样的接口,客户端通过代理来访问真实主题,同时可以在访问真实主题前后进行一些额外操作。
  3. 代理模式的作用

    代理模式的主要目的是控制对对象的访问,可以实现一些额外的功能,比如延迟加载、访问控制、日志记录、性能监控等。代理模式在实际开发中有着广泛的应用,比如远程代理、虚拟代理、保护代理等。总的来说,代理模式提供了一种灵活的方式来控制对对象的访问,并且可以在不改变原始对象的情况下实现一些额外的功能。

二、代理模式的三种方式

代理模式有三种常见的实现方式,分别是静态代理、动态代理和CGLIB代理。

  1. 静态代理(Static Proxy)

    1.1 静态代理是在编译时就已经确定了代理关系,代理类和真实类的关系在代码中是固定的。在静态代理中,代理类需要实现与真实类相同的接口,并持有真实类的引用,在调用真实类的方法前后可以执行一些额外的操作。

    • 优点:实现简单。
    • 缺点:每个代理类只能代理一个真实类,如果需要代理多个真实类,就需要编写多个代理类。

    1.2 代码举例

    //接口
    public interface Shopping {
        void buy();
    }
    
    //真实类
    public class RealShopping implements Shopping {
        @Override
        public void buy() {
            System.out.println("购买商品");
        }
    }
    
    //代理类
    public class ShoppingProxy implements Shopping {
        private RealShopping realShopping;
    
        public ShoppingProxy(RealShopping realShopping) {
            this.realShopping = realShopping;
        }
    
        @Override
        public void buy() {
            System.out.println("开始记录日志");
            realShopping.buy();
            System.out.println("记录日志完成");
        }
    }
    
    //客户端
    public class Client {
        public static void main(String[] args) {
            RealShopping realShopping = new RealShopping();
            ShoppingProxy shoppingProxy = new ShoppingProxy(realShopping);
            shoppingProxy.buy();
        }
    }
    
  2. 动态代理(Dynamic Proxy)

    2.1 动态代理是在运行时动态生成代理类,不需要手动编写代理类。Java种的动态代理主要是使用java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler接口实现。

    • 优点:可以代理多个真实类,同时也可以在代理类中实现通用的逻辑,比如日志记录、异常处理等。
    • 缺点:基于反射机制,性能相对较低;无法代理目标类中的final方法。

    2.2 代码举例

    其中最主要的就是Proxy.newProxyInstance方法,它是用于创建动态代理对象的静态方法。它接受三个参数:

    • ClassLoader:用于加载动态代理类的类加载器。
    • interfaces:要代理的接口数组。
    • InvocationHandler:实现了InvocationHandler接口的对象,用于处理代理对象的方法调用。
    //接口
    public interface Shopping {
        void buy();
    }
    
    //真实类
    public class RealShopping implements Shopping {
        @Override
        public void buy() {
            System.out.println("购买商品");
        }
    }
    
    // 动态代理处理器
    public class ShoppingInvocationHandler implements InvocationHandler {
        private Object target;
    
        public ShoppingInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("开始记录日志");
            Object result = method.invoke(target, args);
            System.out.println("记录日志完成");
            return result;
        }
    }
    
    // 动态代理类
    public class DynamicShoppingProxy {
        public static Shopping createProxy(RealShopping realShopping) {
            return (Shopping) Proxy.newProxyInstance(
                    realShopping.getClass().getClassLoader(),
                    realShopping.getClass().getInterfaces(),
                    new ShoppingInvocationHandler(realShopping));
        }
    }
    
    // 客户端
    public class Client {
        public static void main(String[] args) {
            RealShopping realShopping = new RealShopping();
            Shopping dynamicProxy = DynamicShoppingProxy.createProxy(realShopping);
            dynamicProxy.buy();
        }
    }
    

    执行结果

    image-20231128194614338

    在上面的示例中,DynamicShoppingProxy类的createProxy方法使用了Proxy.newProxyInstance方法来创建一个代理对象。在这里,ShoppingInvocationHandler实现了InvocationHandler接口,用于处理代理对象的方法调用。当代理对象的方法被调用时,invoke方法会被触发,从而可以在方法调用前后执行额外的逻辑

  3. CGLIB代理

    3.1 CGLIB是一个强大的,高性能的代码生成类库,它可以在运行时扩展Java类。CGLIB代理不要求目标对象实现接口,它通过继承目标类生成代理类。

    • 优点:可以为没有实现接口的类提供代理;性能比动态代理更高
    • 缺点:不能代理代理final类和final方法;生成的代理类可能会占用较多的内存,因为它生成的代理类通常比目标类更庞大

    3.2 代码举例

    //接口
    public interface Shopping {
        void buy();
    }
    
    //真实类
    public class RealShopping implements Shopping {
        @Override
        public void buy() {
            System.out.println("购买商品");
        }
    }
    
    // CGLIB代理处理器
    public class ShoppingMethodInterceptor implements MethodInterceptor {
        @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;
        }
    }
    
    // CGLIB代理类
    public class CglibShoppingProxy {
        public static Shopping createProxy() {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(RealShopping.class);
            enhancer.setCallback(new ShoppingMethodInterceptor());
            return (Shopping) enhancer.create();
        }
    }
    
    // 客户端
    public class Client {
        public static void main(String[] args) {
            Shopping cglibProxy = CglibShoppingProxy.createProxy();
            cglibProxy.buy();
        }
    }
    

    执行结果

    image-20231128194908485

    在CGLIB中,Enhancer类用于创建代理对象,并且通过setSuperclass方法设置要代理的类,通过setCallback方法设置代理处理器。在这里,ShoppingMethodInterceptor实现了MethodInterceptor接口,用于处理代理对象的方法调用。

    CglibShoppingProxy类的createProxy方法中,我们首先创建了一个Enhancer对象,然后设置了要代理的类和代理处理器。最后,通过enhancer.create()方法来创建代理对象。当代理对象的方法被调用时,intercept方法会被触发,从而可以在方法调用前后执行额外的逻辑。

3.3 动态代理与CGLIB代理的对比

3.3.1 基于继承 vs 基于接口:

  • Java动态代理是基于接口的,它要求目标类必须实现接口,然后通过Proxy.newProxyInstance方法创建代理对象。
  • CGLIB是基于继承的,它可以代理没有实现接口的类,通过创建目标类的子类来实现代理。

3.3.2 代理对象的生成方式:

  • Java动态代理是在运行时动态生成代理类,利用Proxy.newProxyInstance方法创建代理对象。
  • CGLIB是在运行时动态生成代理类,通过继承目标类来创建代理对象。

3.3.3 性能:

  • 通常情况下,CGLIB的性能比Java动态代理更高,因为CGLIB是直接操作字节码来创建代理类,而Java动态代理是通过反射来实现。

3.3.4 调用目标方法的方式:

  • Java动态代理通过InvocationHandler来处理代理对象的方法调用,可以在invoke方法中实现对目标方法的增强。
  • CGLIB通过MethodInterceptor来处理代理对象的方法调用,同样可以在intercept方法中实现对目标方法的增强。

三、代理模式的应用与总结

  1. 远程代理(Remote Proxy): 当某个对象位于不同的地址空间,需要通过网络进行远程访问时,可以使用远程代理。远程代理隐藏了对象存在于不同地址空间的事实,使得客户端可以像访问本地对象一样访问远程对象。
  2. 虚拟代理(Virtual Proxy): 虚拟代理是一种延迟加载的代理,用于按需创建开销较大的对象。在需要时,虚拟代理才会真正创建目标对象,而在目标对象创建之前,虚拟代理可以提供一些占位信息。
  3. 保护代理(Protection Proxy): 保护代理用于控制对目标对象的访问。通过保护代理,可以对客户端的请求进行一些访问控制,如权限验证、安全检查等。
  4. 缓存代理(Cache Proxy): 缓存代理用于为一些开销较大的操作结果提供缓存,以提高系统的性能。当客户端请求相同的操作时,缓存代理可以直接返回缓存的结果,而不需要重新执行操作。
  5. AOP(面向切面编程): 代理模式是AOP的核心实现方式之一。AOP通过代理来实现横切关注点,如日志记录、事务管理等,从而实现了对业务逻辑的解耦和集中管理。
  6. Spring框架中的代理: Spring框架广泛使用了代理模式,主要用于实现依赖注入、事务管理、AOP等功能。Spring提供了基于JDK动态代理和CGLIB的代理实现,可以根据需要选择不同的代理方式。

总的来说,代理模式在实际开发中有着广泛的应用,特别是在分布式系统、性能优化、安全控制、日志记录等方面,代理模式都能发挥重要作用。通过代理模式,可以有效地实现对目标对象的访问控制、性能优化、横切关注点的处理等功能。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计模式是在软件设计过程中,对于常见问题的解决方案的总结和模板化的提炼。Java语言中广泛应用的设计模式有23种,分为三个大类:创建型模式、结构型模式和行为型模式。 首先是创建型模式,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。单例模式确保一个类只有一个实例化对象,简单工厂模式通过一个工厂类来创建对象,工厂方法模式通过使用工厂接口来创建对象,抽象工厂模式为一个产品族提供创建对象的接口,建造者模式逐步构建一个复杂对象,原型模式通过复制现有对象来创建新对象。 其次是结构型模式,包括适配器模式、桥接模式、装饰器模式、组合模式、外观模式、享元模式和代理模式。适配器模式用于将一个接口转换成客户端所期待的接口,桥接模式将抽象和实现解耦,装饰器模式动态地给对象添加额外职责,组合模式将对象组合成树形结构以表示“部分-整体”的层次结构,外观模式提供了一个简化客户端调用复杂子系统的接口,享元模式通过共享对象来有效地支持大量细粒度的对象,代理模式为其他对象提供一种代理以控制对这个对象的访问。 最后是行为型模式,包括模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式和访问者模式。模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中实现,命令模式将请求封装成一个对象,以使不同的请求能够参数化客户端对象,迭代器模式提供一种访问容器对象中各个元素的方式,观察者模式定义了对象之间的一对多依赖,中介者模式用一个中介对象来封装一系列对象的交互,备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,解释器模式给定一个语言,定义它的文法的一种表示,并定义一个解释器,状态模式允许一个对象在其内部状态改变时改变它的行为,策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,以使得算法的变化不影响到使用算法的客户,访问者模式可以在不改变数据结构的前提下,定义作用于这个结构元素的新操作。通过使用这些设计模式,可以提高软件系统的可维护性、可扩展性和可重用性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值