最詳細的代理講解--JDK動態代理和cglib代理

y_keven發表於2013-05-05

1.代理相關的概念

代理模式

    代理模式的英文叫做Proxy或Surrogate,中文都可譯為”代理“,所謂代理,就是一個人或者一個機構代表另一個人或者另一個機構採取行動。在一些情況下,一個客戶不想或者不能夠直接引用一個物件,而代理物件可以在客戶端和目標物件之間起到中介的作用

抽象主題角色

    宣告瞭真實主題和代理主題的共同介面,這樣一來在任何可以使用真實主題的地方都可以是使用代理主題

代理主題(Proxy)角色

    代理主題角色內部含有對真實主題的引用,從而可以在任何時候操作真實主題物件;代理主題角色提供一個與真實主題角色相同的介面,以便可以在任何時候都可以替代真實主題控制對真實主題的引用,負責在需要的時候建立真實主題物件(和刪除真實主題物件);代理角色通常在將客戶端呼叫傳遞給真實的主題之前或之後,都要執行某個操作,而不是單純地將呼叫傳遞給真實主題物件。

真實主題角色

   定義了代理角色所代表地真實物件

spring有兩種代理方式:

   1.若目標物件實現了若干介面,spring使用JDK的java.lang.reflect.Proxy類代理。

   2.若目標物件沒有實現任何介面,spring使用CGLIB庫生成目標物件的子類。

2.案例分析兩種代理的異同

JDK動態代理

    注意事項:編寫JDK代理類引用的是java的工具包lang中的反射包下的Proxy,不需要引入其他jar包,但是要注意不要倒錯包了;import java.lang.reflect.Proxy; 目標物件實現了若干介面。

   兩個介面類SayByeBye、SayGoodBye

package www.csdn.spring.proxy.jdk;

public interface SayByeBye {
	
	public void sayByeBye();

}

package www.csdn.spring.proxy.jdk;

public interface SayGoodBye {
	
	public void sayGoodBye(String content);

}
   這兩個介面類的實現類SayByeImplement
package www.csdn.spring.proxy.jdk;

public class SayByeImpl implements SayGoodBye,SayByeBye {

	@Override
	public void sayGoodBye(String content) {
		System.out.println("say:"+content);
	}

	@Override
	public void sayByeBye() {
		System.out.println("say:拜拜!");
	}

}
  JDK代理類JDKProxy
package www.csdn.spring.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxy implements InvocationHandler {

	// 代理目標物件
	private Object target;

	// 建立目標物件的代理物件
	public Object createProxyInstance(Object target) {
		// 代理的目標物件
		this.target = target;
		// 建立代理物件
		// 1、定義代理類的類載入器
		// 2、代理類要實現的介面列表
		// 3、 指派方法呼叫的呼叫處理程式
		return Proxy.newProxyInstance(target.getClass().getClassLoader(),
				target.getClass().getInterfaces(), this);
	}

	/**
	 * proxy:目標物件的代理例項,改代理例項不管執行多少次都是class $Proxy4 ;
	 * method:對於代理例項呼叫介面方法的Method例項;
	 * args:方法引數
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		/*
		 * 測試invoke方法的三個引數的含義
		 */
		System.out.println("proxy:"+proxy.getClass());
		System.out.println("method:"+method.getName());
		if(args!=null&&args.length>0){
			for(Object obj : args){
				System.out.println("args:"+obj);
			}
		}
		
		//宣告返回值
		Object returnValue = null;
		beforeMethod();
		returnValue = method.invoke(target, args);
		afterMethod();
		return returnValue;
	}
	
	public void beforeMethod(){
		System.out.println("----方法執行之前的操作----");
	}
	public void afterMethod(){
		System.out.println("----方法執行之後的操作----");
	}
}
  測試類ProTest
package www.csdn.spring.proxy.jdk;

import org.junit.Test;

public class ProxyTest {

	@Test
	public void testSay() {
		
		//真是主題角色
		SayByeImpl sayByeImpl = new SayByeImpl();
		
		/*
		 * //不使用代理的情況
		 *  sayByeImpl.sayGoodBye("我要離婚!"); 
		 *  sayByeImpl.sayByeBye();
		 */
      
		//代理主題角色,這裡用代理主題和真實主題實現的同樣介面類來接收建立的代理主題,就是建立一個目標類
		SayGoodBye sayGoodBye = (SayGoodBye) new JDKProxy().createProxyInstance(sayByeImpl);
		SayByeBye sayByeBye = (SayByeBye) new JDKProxy().createProxyInstance(sayByeImpl);
	    sayGoodBye.sayGoodBye("受不了了,我要離婚!");
	    System.out.println("00000000000000000000000000000000000000000000000");
	    sayByeBye.sayByeBye();
	}

}



 

CGLIB做代理

    CGlib是一個強大的,高效能,高質量的Code生成類庫。它可以在執行期擴充套件Java類與實現Java介面。

    注意事項:使用cglib做代理,java中不想jdk動態代理那樣給封裝好了相應的類,他需要匯入所依賴的jar包asm-3.3.jar、cglib-2.2.jar這兩個jar包;目標物件沒有實現任何介面

   目標類SayHello

package www.csdn.spring.proxy.cglib;

 

publicclass SayHello {

  

   publicvoid say(String content) {

      System.out.println("say:" + content);

   }

}

   cglib代理類CglibProxy

package www.csdn.spring.proxy.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor{
	
	private Object target;
	public Object createProxyInstance(Object target){
		this.target = target;
		//生成代理物件
		Enhancer  enhancer = new Enhancer();
		// 用於設定代理物件的父類
		enhancer.setSuperclass(this.target.getClass());
		//設定回撥
		enhancer.setCallback(this);
		//建立代理物件 
		return enhancer.create();
	}

	/**
	 * proxy:目標物件代理的例項;
	 * method:目標物件呼叫父類方法的method例項 ;
	 * args:呼叫父類方法傳遞引數
	 * methodProxy:代理的方法去呼叫目標方法
	 */
	@Override
	public Object intercept(Object proxy, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		/*
		 * 測試invoke方法的四個引數的含義
		 */
		System.out.println("proxy:"+proxy.getClass());
		System.out.println("method:"+method.getName());
		System.out.println("methodProxy:"+methodProxy.getSuperName());
		if(args!=null&&args.length>0){
			for(Object obj : args){
				System.out.println("args:"+obj);
			}
		}
		
		//宣告返回值
		Object returnValue = null;
		beforeMethod();
		//使用method和methodProxy引數都可以呼叫invoke方法,並且執行出來的效果一樣
		//returnValue = methodProxy.invoke(target, args);
		returnValue = method.invoke(target, args);
		afterMethod();
		return returnValue;
	}
	
	public void beforeMethod(){
		System.out.println("----方法執行之前的操作----");
	}
	public void afterMethod(){
		System.out.println("----方法執行之後的操作----");
	}
}
   測試類proxyTest
package www.csdn.spring.proxy.cglib;

import org.junit.Test;

public class ProxyTest {

	@Test
	public void testSay() {
		
		//真是主題角色
		SayHello sayHello = new SayHello();
		//sayHello.say("嗨!你好啊!");
		
		//建立了一個 目標類的物件 ,jdk動態代理中建立的是介面類物件,cglib由於不實現任何介面,所以直接建立一個目標類的物件
		SayHello sh = (SayHello) new CglibProxy().createProxyInstance(sayHello);
		sh.say("嗨!你好啊!");
	}

}


 

相關文章