代理模式:当须要调用某个对象的时候。不须要关心拿到的是不是一定是这个对象,它须要的是,我拿到的这个对象能够完毕我想要让它完毕的任务就可以,也就是说。这时调用方能够拿到一个代理的一个对象,这个对象能够调用它想创建的对象的方法完毕调用方的任务就好了。
静态代理模式模拟实例
应用场景介绍:这里有一个PDF。我想打开。可是,初始化的过程比較耗时,那么在这个时候假设在创建对象的时候直接初始化那么势必会消耗掉一定的时间,可是并不一定初始化完毕以后就直接要打开,可能过一段时间之后才须要打开呢,是有可能的。
File的一个接口,这个接口定义了一个openFile的方法。
package com.siti.proxytest1; public interface File { /** * 打开文件 */ public void openFile(); }
PDFFile实现File接口,并实现openFile方法。
package com.siti.proxytest1; public class PDFFile implements File{ /** * sleep一秒钟表示初始化的时候的耗时 */ public PDFFile(){ try { Thread.sleep(1000); System.out.println("PDF文件载入成功~"); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void openFile() { System.out.println("PDF文件打开!"); } }
PDFFile的代理类,也要实现File接口,并实现openFile方法,在这种方法其中并非从新的实现一遍PDFFile的openFile方法,而是将获得调用者想要创建的那个对象的实例,用这个实例去调用详细的实现方法。
代码例如以下:
package com.siti.proxytest1; public class PDFFileProxy implements File{ private PDFFile pdfFile; /** * 获代替理对象 * @param pdfFile */ public PDFFileProxy(PDFFile pdfFile){ this.pdfFile = pdfFile; } /** * 代理对象调用openFile方法时。才会创建pdfFile对象(耗时1s); * 然后运行对象的openFile方法完毕。 */ @Override public void openFile() { if(pdfFile == null){ this.pdfFile = new PDFFile(); } pdfFile.openFile(); } }
在这个代理类中,能够清楚的看到。当创建一个代理对象传了null參数的时候,并没有直接得到pdfFile的实例。这时候速度很快的创建完毕了,当你调用运行的时候才会创建真正的实例,并调用该对象的方法,完毕调用者的请求。
測试代码例如以下:
package com.siti.proxytest1; public class ProxyTest { public static void main(String[] args) { Long beginTime = System.currentTimeMillis(); // 创建代理对象 PDFFileProxy proxy = new PDFFileProxy(null); Long MiddTime = System.currentTimeMillis(); System.out.println("创建代理对象耗时:" + (MiddTime - beginTime)); // 调用openFile方法。创建实际的PDFFile对象并运行openFile方法 // 假设此时不调用openFile的话那么这一秒钟的时间就会被节约下来,那么仅仅是消耗了创建代理对象的时间(非常少的时间) proxy.openFile(); System.out.println("打开文件耗时:" + (System.currentTimeMillis() - beginTime)); } }
当然,系统的终于开销并没有降低,可是有的时候创建完对象之后并不一定就会直接调用它的方法,甚至直到被回收也没有调用。那么这时候代理模式非常显然的效率更高,再者就是推迟了对象的创建时间,保障前面的程序执行流畅的,降低对象在内存中的存活时间。宏观上降低了内存的消耗。
Hibernate默认使用延迟载入(懒载入)。也就是,当一个对象关联着还有一个对象的时候,默认是不被直接载入的,它会获得一个代理对象。等到真正调用的时候,这个对象才真正的被创建。
动态代理模式模拟实例
借用上面类似的样例
首先还是一个File接口:
package com.siti.proxytest2; public interface File { /** * 载入文件 */ public void loadFile(); /** * 打开文件 */ public void openFile(); }
PDFFile对File接口进行实现:
package com.siti.proxytest2; public class PDFFile implements File{ @Override public void openFile() { System.out.println("PDF文件打开!"); } @Override public void loadFile() { System.out.println("PDF文件载入成功~"); } }
创建一个事务类,表示事务机制。
package com.siti.proxytest2; public class TransactionManager { /** * 事务开启 */ public void transactionStart(){ System.out.println("事务开启。"); } /** * 事务关闭 */ public void transactionEnd(){ System.out.println("事务关闭。"); } }
接下来是MyInvocationHandler类实现InvocationHandler,实现的invoke方法将作为代理对象的方法实现。
package com.siti.proxytest2; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { // 须要被代理的对象 private Object targetObj; public void setProxyObj(Object targetObj){ this.targetObj = targetObj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { TransactionManager transaction = new TransactionManager(); transaction.transactionStart(); // 运行targetObj的method方法 Object obj = method.invoke(targetObj, args); transaction.transactionEnd(); return obj; } }
上面的invoke方法将会作为动态代理对象的全部方法的真正实现者。这种话即调用了须要调用的方法又添加了一层事务管理机制。
package com.siti.proxytest2; import java.lang.reflect.Proxy; public class ProxyFactory { public static Object getProxy(Object targetObj) { // 创建一个MyInvocationHandler MyInvocationHandler handler = new MyInvocationHandler(); // 为MyInvocationHandler设置target对象 handler.setProxyObj(targetObj); // 返回一个动态代理对象 return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), targetObj.getClass().getInterfaces(), handler); } }
该类提供了一个getProxy()方法,该方法为目标对象生成一个动态代理对象。该动态代理对象能够看做是须要的目标对象使用,真正调用的时候会运行MyInvocationHandler的invoke方法,运行事务调用方法完毕操作。
測试类例如以下:
package com.siti.proxytest2; import com.siti.proxytest2.PDFFile; public class ProxyTest { public static void main(String[] args) { File pdfFileTarget = new PDFFile(); // 创建动态代理 File pdfFile = (File) ProxyFactory.getProxy(pdfFileTarget); pdfFile.loadFile(); pdfFile.openFile(); } }
执行结果:
动态代理能够灵活地实现解耦,这样的方式能够为对象提供附加的功能。