参考答案
静态代理和动态代理的区别
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—