代理模式
程序世界的代理模式同现实中的代理人(例如:律师)类似,执行当事人授权的一些事情或执行一些当事人无力完成的专业操作。
代理模式的作用:
- 完成用户请求,屏蔽用户对真实对象的直接访问。
- 在远程连接中对用户请求进行一些其他操作。例如转账,转账前进行登录检查,转账后记录日志。
- 延时加载,提升系统性能。
下面通过代码来验证
A、代理模式可以做到延时加载。
B、能在真实调用之前之后加入处理逻辑。
JDK1.8
先定义一个接口,两个方法,一个有参,一个无参。
1 public interface IWork { 2 void work(); 3 4 void hello(String str); 5 }
实现IWork接口。
1 /** 2 * 真正工作的类 3 */ 4 public class RealWork implements IWork { 5 6 public RealWork() { 7 System.out.println("时间:"+ new Date().toLocaleString() + ", 我来了"); 8 } 9 @Override 10 public void work() { 11 System.out.println("时间:"+ new Date().toLocaleString() + ", 开始工作"); 12 } 13 @Override 14 public void hello(String str) { 15 System.out.println("时间:"+ new Date().toLocaleString() + ", 大家好," + str); 16 } 17 18 }
这里加时间打印是方便测试延时加载。
接下来用静态代理的方式实现这个IWork接口:
1 /** 2 * 静态代理类 3 * 1、延时加载 4 * 2、方法调用前后加入其它操作,每个方法都必须添加。 5 */ 6 public class StaticProxy implements IWork { 7 IWork real = null; 8 @Override 9 public void work() { 10 if(null == real){ 11 real = new RealWork(); 12 } 13 14 System.out.println("时间:"+ new Date().toLocaleString() + ", 先吃点东西"); 15 16 real.work(); 17 18 System.out.println("时间:"+ new Date().toLocaleString() + ", 喝杯茶休息一下"); 19 } 20 21 @Override 22 public void hello(String str) { 23 if(null == real){ 24 real = new RealWork(); 25 } 26 27 real.hello(str); 28 } 29 }
也用 JAVA提供的动态代理方式实现 IWork 接口:
1 /** 2 * 动态代理类 3 * 1、延时加载。 4 * 2、方法调用前后加入其它操作,只需要处理一次,创建代理时传入的 接口 的所有方法 都会执行相同的处理。 5 */ 6 public class DynamicProxy implements InvocationHandler{ 7 private IWork real; 8 9 /** 10 * @param proxy : 指代代理的那个真实对象,具体用途暂时不知道 11 * @param method :调用的方法的反射对象 12 * @param method :方法的参数 13 */ 14 @Override 15 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 16 if(null == real){ 17 real = new RealWork(); 18 } 19 System.out.println(proxy.getClass().getName());//com.sun.proxy.$Proxy0 20 21 System.out.println("时间:"+ new Date().toLocaleString() + ", 先吃点东西"); 22 23 method.invoke(real, args); 24 System.out.println("时间:"+ new Date().toLocaleString() + ", 喝杯茶休息一下"); 25 return null; 26 } 27 }
最后是测试方法的实现:
1 public class TestWork { 2 public static void main(String[] args) { 3 System.out.println("################## 静态代理 ##################"); 4 testStaticWork(); 5 System.out.println("################## 动态代理 ##################"); 6 testDynamicWork(); 7 } 8 9 /** 10 * 测试静态代理类的延时加载 和 在方法前后加入处理逻辑 11 */ 12 public static void testStaticWork(){ 13 IWork proxy = new StaticProxy(); 14 try { 15 System.out.println("时间:"+ new Date().toLocaleString() + ", 我在睡觉"); 16 Thread.sleep(2 * 1000); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } 20 proxy.hello("我是菜鸟"); 21 System.out.println("-------------我是华丽的分割线-------------"); 22 proxy.work(); 23 } 24 25 /** 26 * 测试动态代理类的延时加载 和 在方法前后加入处理逻辑 27 * 动态加载的本质就是将动态代理类生成的子节码,通过反射载入类加载器中。 28 */ 29 public static void testDynamicWork(){ 30 IWork proxy = (IWork)Proxy.newProxyInstance( 31 ClassLoader.getSystemClassLoader(), //类加载器,也可以通关对象获得 32 new Class[]{IWork.class}, //需要代理的接口,决定哪些方法是可以动态代理的 33 new DynamicProxy()); //InvocationHandler的实例,决定了方法调用前后有哪些操作 34 35 System.out.println(proxy.getClass().getName());//com.sun.proxy.$Proxy0 36 37 try { 38 System.out.println("时间:"+ new Date().toLocaleString() + ", 我在睡觉"); 39 Thread.sleep(2 * 1000); 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 proxy.hello("我是菜鸟"); 44 System.out.println("-------------我是华丽的分割线-------------"); 45 proxy.work(); 46 } 47 }
运行代码,控制台输出如下:
1 ################## 静态代理 ################## 2 时间:2017-3-22 15:15:15, 我在睡觉 3 时间:2017-3-22 15:15:17, 我来了 4 时间:2017-3-22 15:15:17, 大家好,我是菜鸟 5 -------------我是华丽的分割线------------- 6 时间:2017-3-22 15:15:17, 先吃点东西 7 时间:2017-3-22 15:15:17, 开始工作 8 时间:2017-3-22 15:15:17, 喝杯茶休息一下 9 ################## 动态代理 ################## 10 com.sun.proxy.$Proxy0 11 时间:2017-3-22 15:15:17, 我在睡觉 12 时间:2017-3-22 15:15:19, 我来了 13 com.sun.proxy.$Proxy0 14 时间:2017-3-22 15:15:19, 先吃点东西 15 时间:2017-3-22 15:15:19, 大家好,我是菜鸟 16 时间:2017-3-22 15:15:19, 喝杯茶休息一下 17 -------------我是华丽的分割线------------- 18 com.sun.proxy.$Proxy0 19 时间:2017-3-22 15:15:19, 先吃点东西 20 时间:2017-3-22 15:15:19, 开始工作 21 时间:2017-3-22 15:15:19, 喝杯茶休息一下
针对结果分析:
静态代理:最先创建了代理类,但是真实对象并没有立即初始化,直到方法调用时才进行初始化,验证A。对象在"工作"之前和之后都进行了其他操作,验证B。
动态代理:创建代理对象两秒以后才初始化,验证A。两个方法调用前后都进行了其他操作,验证B。
对比两者,静态代理的方法不手动添加代码就不会在方法调用前后执行其他操作。而动态代理,只要是接口里面有的的方法都自动进行了添加,而且创建代理对象时传入的是一个接口class数组,表示可以对多个接口的方法有效,前提是"真实的类"实现了这些接口。