静态代理和动态代理的区别(附代码实例)

参考答案

静态代理和动态代理的区别

1. 静态代理

  • 静态代理编译期生成代理类;
  • 静态代理和被代理类以及其业务逻辑耦合,适用性较差,代理逻辑难以扩展;
  • 大多数情况下,静态代理只代理一个类;
  • 静态代理事先知道要代理的是什么。

2. 动态代理

  • 动态代理运行期生成代理类;
  • 动态代理是代理一个接口下的多个实现类;
  • 动态代理只有在运行时,才知道要代理的是什么;
  • 动态代理可以在不知道被代理类的前提下编写代理逻辑,运行时才决定被代理对象,适用性好,且代理逻辑易于扩展。

AOP 编程就是基于动态代理实现的,比如著名的Spring框架、Hibernate框架等,都是动态代理的使用案例。

 

举个我们生活中的案例,加深理解。

租房有两种常用的方式,一是通过房东直租,一是通过中介租房。

显而易见,后面这种方式更方便快捷。用户通过“中介”,来完成租房操作,包括看房、交押金、租房、清扫卫生等。这里的“中介”,就类似于租客的代理。

静态代理和动态代理的区别(附代码实例)

静态代理:

/*  静态代理
     第一步:定义抽象的主干业务,即租房
  */
 public interface RentHouse {
     void rentHouse();
 }
 /*
     第二步:定义租客,实现租房接口,表明租客有租房的需求
  */
 public class Tenant implements RentHouse{
     public void rentHouse() {
         System.out.println("租客:我想租房!");
     }
 }
 /*
     第三步:定义中介,中介是要帮租户实现租房子的
            需求,所以也要实现租房接口
  */
 public class RentHouseProxy implements RentHouse{
     private RentHouse rentHouse;
     public RentHouseProxy(RentHouse rentHouse){
         this.rentHouse=rentHouse;
     }
     public void rentHouse() {
         System.out.println("中介:搜集房源......");
         System.out.println("中介:对比市场价格......");
         System.out.println("中介:确定租金......");
         System.out.println("中介:前房屋租赁合同......");
         System.out.println("中介:装修装修,摆点物件......");
         rentHouse.rentHouse();
         System.out.println("中介:租房完毕......");
     }
 }
 /*
     测试
     结果:中介:搜集房源......
          中介:对比市场价格......
          中介:确定租金......
          中介:前房屋租赁合同......
          中介:装修装修,摆点物件......
          租客:我想租房!
          中介:租房完毕......
  */
 public class ProxyDemo {
     public static void main(String[] args) {
         RentHouse rentHouse=new Tenant();
         RentHouseProxy proxy=new RentHouseProxy(rentHouse);
         proxy.rentHouse();
     }
 }​

实例可见,代理模式在不修改被代理对象的基础下,进行了一些功能的附加与增强。

但是,每一个抽象的事务都是一个接口,代理类需要实现这个接口。即:需要为每一个服务,都创建一个代理类,工作量大、不易管理,且如果接口发生改变,代理类需要同步进行相应的修改。

 

动态代理:

使用动态代理,就不需要再手动创建代理类,只用编写一个动态代理处理器。真正的代理对象,由 JVM 在运行时动态创建。

下面代码实例供参考:

/*
     动态代理
     前两步与静态代理相同
  */
 public class DynamicRentHouseProxy implements InvocationHandler {
     private Object rentHouse;
 ​
     public DynamicRentHouseProxy(Object rentHouse) {
         this.rentHouse = rentHouse;
     }
 ​
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         System.out.println("中介:搜集房源......");
         System.out.println("中介:对比市场价格......");
         System.out.println("中介:确定租金......");
         System.out.println("中介:前房屋租赁合同......");
         System.out.println("中介:装修装修,摆点物件......");
         Object result=method.invoke(rentHouse,args);
         System.out.println("中介:租房完毕......");
         return result;
     }
 }
 public class ProxyDemo {
     public static void main(String[] args) {
         /*RentHouse rentHouse=new Tenant();
         RentHouseProxy proxy=new RentHouseProxy(rentHouse);
         proxy.rentHouse();*/
         RentHouse tenant=new Tenant();
         InvocationHandler handler=new DynamicRentHouseProxy(tenant);
         //newProxyInstance运用反射为我们生成一个代理类对象
         RentHouse rentHouse=(RentHouse)            Proxy.newProxyInstance(Tenant.class.getClassLoader(),
                 Tenant.class.getInterfaces(),handler);
         rentHouse.rentHouse();
     }
 }

看下Proxy类newProxyInstance()的源码

//ClassLoader:根据类的字节码直接生成这个类的Class对象
 //interfaces:由委托实现的接口的Class对象数组,主要是包含了最重要的代理类需要
 //            实现的接口方法的信息
 //h:一个实现了InvocationHandler接口的对象
 //简单来讲第一个参数ClassLoader和第二参数接口的Class对象是用来动态生成委托类的
 //包括类名,方法名,继承关系在内的一个空壳。只有接口定义的方法名,没有实际操作。实
 //际的操作是由第三个参数InvocationHandler的invoke()方法来执行
 public static Object newProxyInstance(ClassLoader loader,
                                       Class<?>[] interfaces,
                                       InvocationHandler h) {
     Objects.requireNonNull(h);
 ​
     final Class<?> caller = System.getSecurityManager() == null
                                 ? null
                                 : Reflection.getCallerClass();
 ​
     /*
      * Look up or generate the designated proxy class and its constructor.
      */
     Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
 ​
     return newProxyInstance(caller, cons, h);
 }

如上,代理类并不知道事务的具体名称,即使接口中的方法发生变化,代理类也不受其影响,代理逻辑与业务逻辑之间没有耦合,是彼此独立的。

 

以上,是Java面试题【静态代理和动态代理的区别】的参考答案。

输出,是最好的学习方法

欢迎在评论区留下你的问题、笔记或知识点补充~

—end—

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧