一篇文章让你搞懂Java中的静态代理和动态代理

什么是代理模式代理模式是常用的java设计模式,在Java中我们通常会通过new一个对象再调用其对应的方法来访问我们需要的服务 。代理模式则是通过创建代理类(proxy)的方式间接地来访问我们需要的服务 。
举一个生活中的例子:像我们在网上通过中介租到其背后房东的房子,因为房东也嫌麻烦想省事,此时中介就相当于代理而房东则是被代理,两者是分开的,这样我们就不会直接访问到房东,大部分情况下在中介手中租到的房子都会比原价要贵一些,这好比代理的作用,即不需要通过房东中介也可以在原有房价基础上进行增减或者添加其他的推广方式等操作来进行出租 。
在Java中也是如此,我们需要遵循类的单一性原则 , 只有功能单一这个类被改动的可能性才会最小或者说在尽量不修改源码的前提下进行方法扩展,这样也可以避免在不清楚别人代码的情况下进行修改所导致的各种问题等等 。
代理的优缺点优点:

  • 可以提高程序灵活性和可扩展性 。
  • 在不修改原代码的基础上,扩展和增强实现;
  • 代码解耦,在代理中通过参数可以判断真实类,做出不同的响应或调用;
  • 隐藏部分实现过程和细节,可以起到保护目标对象的作用
缺点:
  • 由于在客户端和真实对象之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢;
  • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂 。
  • 静态代理在委托类变多的情况时会显的非常臃肿,不方便阅读与使用

静态代理由程序员创建或工具生成代理类的源码,再编译代理类,即代理类和委托类的关系再程序运行前就已经存在
实现步骤:
  1. 定义一个接口和接口的实现类
  2. 创建一个代理类同样的实现上述接口
  3. 将目标对象注入代理类中然后在代理类方法中调用目标类中的对应方法,这样我们就可以通过代理类在不修改原有方法的基础上进行扩展
代码实现:
/** * 目标接口 */interface Hobby { //唱void sing(); //跳void dance();}/** * 目标类 */class MyHobby implements Hobby {@Overridepublic void sing() {System.out.println("sing...");}@Overridepublic void dance() {System.out.println("dance...");}}/** * 代理类 */class Hobbystaticproxy implements Hobby {Hobby hobby;public Hobbystaticproxy(Hobby hobby) {this.hobby = hobby;}@Overridepublic void sing() {rap();hobby.sing();rap();}@Overridepublic void dance() {rap();hobby.dance();rap();}public void rap() {System.out.println("static-rap...");}}/** * main方法 */class Test {public static void main(String[] args) {Hobby hobby = new MyHobby();Hobbystaticproxy hobbystaticproxy = new Hobbystaticproxy(hobby);hobbystaticproxy.sing();hobbystaticproxy.dance();}}输出结果:
一篇文章让你搞懂Java中的静态代理和动态代理

文章插图
可以看到我们在没有修改目标类的情况下通过代理类来实现按目标接口并且分别在目标类的 唱、跳方法前后都扩展了一个rap方法,这就是静态代理
缺点:接口类变化会影响实现类和代理类;比如方法修改返回值、参数类型、增加方法,实现类和代理类都需要修改,不灵活而且很麻烦 。
动态代理使用反射和字节码的技术,在运行期创建指定接口或类的子类,以及其实例对象,控制被代理对象的访问,使用的工具有 jdkproxy、cglibproxy 等 。
上述静态代理在程序运行前就需要把对应的类和方法写好,这样就被写死了,这只是一个简单的接口里面也只有两个方法,那如果接口中有几十个方法都需要扩展呢 , 总不能一个个地手动去添加吧,所以有了我们的动态代理
实现步骤:
  1. 定义一个接口和接口的实现类
  2. 创建一个代理类实现InvocationHandler接口(指定运行时生成代理类需要完成的具体任务
  3. 重写InvocationHandler接口中的invoke方法
  4. 创建被代理类的对象,调用处理程序最后通过代理类对象来调用对应方法
代码实现:
/** * 目标接口 */interface Hobby {void sing();void dance();}/** * 目标类 */class MyHobby implements Hobby {@Overridepublic void sing() {System.out.println("sing...");}@Overridepublic void dance() {System.out.println("dance...");}}/** * 代理类 */class HobbyDynamicProxy implements InvocationHandler{private final Object obj;public HobbyDynamicProxy(Object obj){this.obj = obj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {rap();Object result = method.invoke(obj, args);rap();return result;}public void rap() {System.out.println("Dynamic-rap...");}}/** * main方法 */class Test {public static void main(String[] args) {InvocationHandler renterHandler = new HobbyDynamicProxy(new MyHobby());Hobby hobbyProxy = (Hobby)Proxy.newProxyInstance(Hobby.class.getClassLoader(), new Class[]{Hobby.class},renterHandler);hobbyProxy.sing();hobbyProxy.dance();}}

推荐阅读