代理模式-jdk动态代理
IDB
package com.bjpowernode.proxy; /** * 代理类和目标类都必须使用同一个接口。 */ public interface IDB { int insert(); int delete(); int update(); }
OracleDB
package com.bjpowernode.proxy; /** * 这是一个Oracle数据库相关的操作类 * * 目标类(委托类)。 */ public class OracleDB implements IDB{ public int insert(){ //以下是一个插入操作 System.out.println("Oracle insert data...."); try { Thread.sleep(526); } catch (InterruptedException e) { e.printStackTrace(); } return 0; } public int delete(){ //以下是一个删除操作 System.out.println("Oracle delete data...."); try { Thread.sleep(569); } catch (InterruptedException e) { e.printStackTrace(); } return 0; } public int update(){ //以下是一个更新操作 System.out.println("Oracle update data...."); try { Thread.sleep(456); } catch (InterruptedException e) { e.printStackTrace(); } return 0; } }
TimeInvocationHandler
package com.bjpowernode.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 注册在这个接口中的代码,在代理对象调用代理方法的时候,自动执行。 * 我们的扩展代码就注册在这个类中。 */ public class TimeInvocationHandler implements InvocationHandler { //这个程序运行期,obj引用中保存了内存地址指向的对象是一个目标对象。 private Object obj; public TimeInvocationHandler(Object obj){ this.obj = obj; } /** * proxy 是代理对象的引用。 * method 是目标类中的目标方法 * args 是目标类中的目标方法的实参 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long begin = System.currentTimeMillis(); //通过反射机制去调用目标类中的目标方法 Object retValue = method.invoke(obj, args); long end = System.currentTimeMillis(); System.out.println("方法执行所耗费" + (end-begin) + "毫秒"); return retValue; } }
Test
package com.bjpowernode.proxy; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { //创建目标类对象 IDB db = new OracleDB(); //创建代理对象 //DBProxy dbProxy = new DBProxy(); //这是静态代理模式中创建对象,因为DBProxy是我们程序员手动编写的。 //创建代理对象(这次创建代理对象是交给JVM完成,包括在这个过程中,JVM也会自动生成“代理类的字节码”) //这个代理类是动态生成的字节码不会以.class文件的形式保存在硬盘上,所以我们称作动态代理。 //第一个参数是类装载器:JVM会在内存中自动生成一个字节码,但是这个字节码要想运行,必须经过类装载器的装载 //然后进一步解析,所以必须传递一个类装载器,这个类装载器就是用来装载这个临时的“字节码”。 //代理类和目标类必须使用同一个类装载器。 //第二个参数:是JVM在生成代理类的时候需要实现接口,但是这个接口又不能随便写,因为代理类和目标类要求实现相同的接口。 //所以我们将目标类实现的接口获取到之后传递过来就行了。 //JDK中提供的代理模式只能代理接口。如果目标类它的父类型不是一个接口而是一个抽象类,那么JDK中提供的动态代理就不能用了,需要使用 //第三方组件,例如:cglib。 //第三个参数:是InvocationHandler,这是一个接口,这个接口中的方法会在代理类调用代理方法的时候自动执行。 IDB dbProxy = (IDB)Proxy.newProxyInstance(db.getClass().getClassLoader(), db.getClass().getInterfaces(), new TimeInvocationHandler(db)); //通过代理对象中的方法去执行目标对象中的方法。 dbProxy.insert(); //dbProxy是一个动态生成的代理对象,由于这个代理对象实现了和OracleDB一样的接口,所以可以用IDB接口指向JVM中的代理对象。 dbProxy.delete(); // 这里调用的delete方法是是代理类中的代理方法,不是目标类中的目标方法。但是一定会通过代理方法去调用目标方法。 dbProxy.update(); } }