Java代理的三种方式
一、代理模式
-
什么是代理模式?
代理模式是23种设计模式中的一种。代理模式是一种结构型设计模式,它允许为其他对象提供一个替代品或占位符,以控制对这个对象的访问。
-
代理模式的角色构成
- 抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样代理类可以通过实现该接口来代理真实主题。
- 真实主题(Real Subject):定义了代理所代表的真实对象。
- 代理(Proxy):持有对真实主题的引用,并实现了与真实主题一样的接口,客户端通过代理来访问真实主题,同时可以在访问真实主题前后进行一些额外操作。
-
代理模式的作用
代理模式的主要目的是控制对对象的访问,可以实现一些额外的功能,比如延迟加载、访问控制、日志记录、性能监控等。代理模式在实际开发中有着广泛的应用,比如远程代理、虚拟代理、保护代理等。总的来说,代理模式提供了一种灵活的方式来控制对对象的访问,并且可以在不改变原始对象的情况下实现一些额外的功能。
二、代理模式的三种方式
代理模式有三种常见的实现方式,分别是静态代理、动态代理和CGLIB代理。
-
静态代理(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(); } }
-
动态代理(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(); } }
执行结果
在上面的示例中,
DynamicShoppingProxy
类的createProxy
方法使用了Proxy.newProxyInstance
方法来创建一个代理对象。在这里,ShoppingInvocationHandler
实现了InvocationHandler
接口,用于处理代理对象的方法调用。当代理对象的方法被调用时,invoke
方法会被触发,从而可以在方法调用前后执行额外的逻辑 -
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(); } }
执行结果
在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
方法中实现对目标方法的增强。
三、代理模式的应用与总结
- 远程代理(Remote Proxy): 当某个对象位于不同的地址空间,需要通过网络进行远程访问时,可以使用远程代理。远程代理隐藏了对象存在于不同地址空间的事实,使得客户端可以像访问本地对象一样访问远程对象。
- 虚拟代理(Virtual Proxy): 虚拟代理是一种延迟加载的代理,用于按需创建开销较大的对象。在需要时,虚拟代理才会真正创建目标对象,而在目标对象创建之前,虚拟代理可以提供一些占位信息。
- 保护代理(Protection Proxy): 保护代理用于控制对目标对象的访问。通过保护代理,可以对客户端的请求进行一些访问控制,如权限验证、安全检查等。
- 缓存代理(Cache Proxy): 缓存代理用于为一些开销较大的操作结果提供缓存,以提高系统的性能。当客户端请求相同的操作时,缓存代理可以直接返回缓存的结果,而不需要重新执行操作。
- AOP(面向切面编程): 代理模式是AOP的核心实现方式之一。AOP通过代理来实现横切关注点,如日志记录、事务管理等,从而实现了对业务逻辑的解耦和集中管理。
- Spring框架中的代理: Spring框架广泛使用了代理模式,主要用于实现依赖注入、事务管理、AOP等功能。Spring提供了基于JDK动态代理和CGLIB的代理实现,可以根据需要选择不同的代理方式。
总的来说,代理模式在实际开发中有着广泛的应用,特别是在分布式系统、性能优化、安全控制、日志记录等方面,代理模式都能发挥重要作用。通过代理模式,可以有效地实现对目标对象的访问控制、性能优化、横切关注点的处理等功能。