Java動態代理(Proxy+Cglib)詳解

對本片內容有不明白的地方請提問我,我若是也不懂會再去學習掌握而後再補充完善,謝謝。html

        首先接觸動態代理是在學習Struts框架仍是Spring框架這個我已經記不清了。但在學習Spring框架的時候知道其AOP(面向切面編程)技術是依賴動態代理實現的。大體方式瞭解,但具體細節就不明白了。最近在看MyBatis源碼,一開始就遇到了一個地方使用到了Java的動態代理機制。因此藉此機會,對Java的動態代理機制進行探究和分析以及學習,一方面再遇到動態代理的問題時可以明白實現細節,再一個在往後編程時也可以藉助動態代理來完成一些特定任務。java

        此篇筆記是基於如下博客的內容,加以本身的分析理解來寫的。本身寫技術文章功力不行,若是看不明白能夠參考原博客的內容,或許能更容易明白些。
http://www.cnblogs.com/xiaoluo501395377/p/3383130.html
 編程

我通常習慣於先了解相關類結構及關係,而後再看內部的方法和實現細節代碼。要使用Java動態代理,必須使用接口InvocationHandler和類Proxy
先看下Proxy這個類,若是先看InvocationHandler接口的話不容易明白。Proxy類在JDK1.7的文檔描述以下:框架

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.(Proxy類提供了一些靜態方法用來建立動態代理的類和它們的實例對象。Proxy同時仍是全部經過這些靜態方法建立出來的代理類的父類。)ide

文檔中還提供瞭如何使用Proxy來建立動態代理類:源碼分析

//To create a proxy for some interface Foo:
    //建立一個InvocationHandler接口的實例
     InvocationHandler handler = new MyInvocationHandler(...);
        //建立代理類的Class信息
     Class proxyClass = Proxy.getProxyClass(
         Foo.class.getClassLoader(), new Class[] { Foo.class });
        //根據Class信息實例化代理類的對象
     Foo f = (Foo) proxyClass.
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });

//or more simply://另外一種簡易方式
     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class[] { Foo.class },
                                          handler);

        由上面代碼可知,Proxy類能夠先使用getProxyClass來獲取代理類的Class,而後再根據Class信息實例化具體的代理類實例。也能夠直接使用Proxy類的newProxyInstance方法來直接獲取一個代理類對象。咱們直接看newProxyInstance這個方法學習

public static Object newProxyInstance(
    ClassLoader loader,
    Class<?>[] interfaces,
   InvocationHandler h
)

該方法有三個參數:測試

ClassLoader loader:類加載器,聲明使用哪一個加載器來加載生成後的代理類對象。
Class<?>[] interfaces:一組接口的Class信息,聲明咱們要生成的代理類對象實現了這些接口,既然實現                                     了這些接口,那麼就能夠調用代理類對象中的這些接口方法。
InvocationHandler h:調用處理器。即在調用代理類的接口方法時,會關聯到咱們指定的調用處理器。我                                       們在調用處理器中就能夠在方法執行先後執行咱們要求的任務。spa

總的來講,ClassLoader決定了用哪一個加載器加載生成的代理對象。interfaces決定了代理類能執行哪些方法。invocationHandler決定了在執行調用方法時,咱們能在執行先後作什麼。代理

而後咱們再看下InvocationHandler這個接口信息,先看下JDK中如何描述:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

代理類實例的調用處理器實現了該接口。每個代理類實例都關聯了一個調用處理器。當代理類調用某個方法時,這個方法調用會被分發到咱們關聯的調用處理器中進行調用執行(大體意思就是這,英文高手見諒...)。

InvocationHandler接口只有一個invoke方法。也就是說咱們實現的調用處理器都要實現invoke方法。

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

下面開始用實例來示範Java動態代理具體怎麼使用。

動態代理是基於接口的,因此咱們先聲明一個接口:

package com.yalonglive.java.proxy;

import java.util.List;

/*
 * Jdbc服務接口,用來執行query,update,delete,insert等方法。
 */
public interface JdbcService {
	
	/**
	 * 查詢單個對象
	 * @param id
	 */
	Object queryObject(String id);
	
	/**
	 * 查詢對象列表
	 * @param entity
	 */
	List queryList(String entity);

}

而後聲明接口的實現類,也就是咱們要代理的對象:

package com.yalonglive.java.proxy;

import java.util.ArrayList;
import java.util.List;

public class JdbcServiceImp implements JdbcService {

	@Override
	public Object queryObject(String id) {
		System.out.println("執行queryObject方法");
		return new Object();
	}

	@Override
	public List queryList(String entity) {
		System.out.println("執行queryList方法");
		return new ArrayList<Object>(0);
	}

}

在接口中,咱們定義了JdbcService能夠執行的兩個方法queryObject和queryList。以及對應的實現類,接下來咱們開始建立代理對象,我這裏按照JDK文檔中的方法建立:

/**
	 * 測試代理類:先建立代理類信息
	 * @throws Exception
	 */
	public static void testProxy() throws Exception {
		
		//聲明一個要被代理的對象
		final JdbcService jdbcService = new JdbcServiceImp();
		
		//建立調用處理器,將被代理的對象和調用處理器進行關聯
		InvocationHandler handle = new InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				
				System.out.println("方法調用前執行代碼段");
				
				//代理類在調用任何註冊過的接口方法時都會經過其調用處理器來執行對應的方法(經過反射),那麼在此處就能夠定義一些自定義操做了
				method.invoke(jdbcService, args);
				
				System.out.println("方法調用後執行代碼段");
				
				return null;
			}
		};
		
		//建立代理類Class信息
		Class proxyClass = Proxy.getProxyClass(JdbcService.class.getClassLoader(),new Class[] {JdbcService.class});
		
		//生成代理類對象
		JdbcService proxyObject = (JdbcService)proxyClass.getConstructor(new Class[]{InvocationHandler.class}).newInstance(handle);
		
		//執行代理類的方法
		proxyObject.queryObject("1");
		
		//輸出:
		//方法調用前執行代碼段
		//執行queryObject方法
		//方法調用後執行代碼段
	}

        其實在寫上面的Demo以前,我在想代理對象,調用處理器,被代理對象之間的關係是什麼,他們是如何進行交互的,爲何執行代理類的方法就會執行被代理類的方法。
        從上面的Demo能夠看出。其實是:代理類<------>調用處理器<------>被代理類。也就是說,建立代理類的時候引用了調用處理器,而在建立調用處理器對象的時候引用了被代理類對象。因此代理類對象就能夠調用被代理類的方法了,調用處理器就是中間的「橋樑」,同時也是咱們編寫自定義邏輯代碼的地方。

        到這裏就能知道Java動態代理的實現方法了,改如何使用。你們若是有不明白的地方或者想知道的內容能夠留言我,我會繼續完善,後面會繼續補充Proxy這個類內部的一些源碼分析。

經過上面的講解,咱們只要能獲取到對象的接口信息,就能建立一個該對象的代理類來對其進行代理操做,可是還有的時候咱們的操做類並無實現相關接口,但咱們還有代理該對象的需求,此時,使用Proxy類的方式就不行了,須要使用其餘辦法。

相關文章
相關標籤/搜索