Hibernate与代理模式

Posted 低调的洋仔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate与代理模式相关的知识,希望对你有一定的参考价值。

代理模式:当需要调用某个对象的时候,不需要关心拿到的是不是一定是这个对象,它需要的是,我拿到的这个对象能够完成我想要让它完成的任务即可,也就是说,这时调用方可以拿到一个代理的一个对象,这个对象可以调用它想创建的对象的方法完成调用方的任务就好了。


静态代理模式模拟实例

应用场景介绍:这里有一个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();
	}
}

运行结果:

技术分享

动态代理可以灵活地实现解耦,这种方式可以为对象提供附加的功能。


















以上是关于Hibernate与代理模式的主要内容,如果未能解决你的问题,请参考以下文章

6.2Java静态代理设计模式

静态代理模式

设计思想与模式之四静态代理模式

代理模式(思维导图)

设计模式之代理模式-静态代理

设计模式----代理模式