代理模式:就是爲其餘對象提供一種代理以控制對這個對象的訪問。html
代理能夠在不改動目標對象的基礎上,增長其餘額外的功能(擴展功能)。java
舉個例子來講明代理的做用: 通常咱們想邀請明星來當咱們的代言人,咱們並不能直接聯繫到明星,而是經過其經紀人,來告訴經紀人咱們須要和明星進行合做,而後經過經紀人來轉達給明星。,明星只須要作好代言工做就好,其餘繁瑣的事情就交於經紀人就能夠。這裏的經經紀人就是一個代理對象,明星就是一個目標對象。緩存
用圖表示以下:app
2.1 靜態代理ide
靜態代理在使用時,須要定義接口或者父類,被代理對象(目標對象)與代理對象(Proxy)一塊兒實現相同的接口或者是繼承相同父類。函數
下面經過代碼演示下:工具
接口IUserDao:oop
/** * 接口 */ public interface IUserDao { void save(); }
目標對象:UserDao:測試
/** * 實現接口 * 目標對象 */ public class UserDao implements IUserDao { public void save() { System.out.println("----保存數據成功!----"); } }
代理對象:UserDaoProxyui
/** * 代理對象(靜態代理) */ public class UserDaoProxy implements IUserDao{ //接收保存目標對象 private IUserDao target; public UserDaoProxy(IUserDao target){ this.target=target; } public void save() { System.out.println("開始事務..."); target.save();//執行目標對象的方法 System.out.println("提交事務..."); } }
測試類:AppTest:
/** * 測試類 */ public class AppTest { public static void main(String[] args) { //目標對象 UserDao target = new UserDao(); //代理對象,把目標對象傳給代理對象,創建代理關係 UserDaoProxy proxy = new UserDaoProxy(target); proxy.save();//執行的是代理的方法 } }
靜態代理總結:
能夠實如今不修改目標對象的基礎上,對目標對象的功能進行擴展。
可是因爲代理對象須要與目標對象實現同樣的接口,因此會有不少代理類,類太多.同時,一旦接口增長方法,目標對象與代理對象都要維護.
可使用動態代理方式來解決。
2.2 動態代理(JDK代理)
動態代理有如下特色:
1.代理對象,不須要實現接口
2.代理對象的生成,是利用JDK的API,動態的在內存中建立代理對象(須要咱們指定建立代理對象/目標對象實現的接口的類型)
3.動態代理也叫作:JDK代理,接口代理
JDK中生成代理對象的API
代理類所在包:java.lang.reflect.Proxy
JDK實現代理只須要使用newProxyInstance方法,可是該方法須要接收三個參數,完整的寫法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
方法是在Proxy類中是靜態方法,且接收的三個參數依次爲:
ClassLoader loader //指定當前目標對象使用類加載器
Class<?>[] interfaces //目標對象實現的接口的類型,使用泛型方式確認類型
InvocationHandler h //事件處理器
下面進行代碼演示:
接口類IUserDao
/** * 接口 */ public interface IUserDao { void save(); }
目標對象UserDao
/** * 接口實現 * 目標對象 */ public class UserDao implements IUserDao { public void save() { System.out.println("----保存數據成功!----"); } }
代理工廠類:ProxyFactory
/** * 建立動態代理對象 * 動態代理不須要實現接口,可是須要指定接口類型 */ public class ProxyFactory{ //維護一個目標對象 private Object target; public ProxyFactory(Object target){ this.target=target; } //給目標對象生成代理對象 public Object getProxyInstance(){ return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("開始事務111"); //執行目標對象方法 Object returnValue = method.invoke(target, args); System.out.println("提交事務111"); return returnValue; } } ); } }
測試類:App:
/** * 測試類 */ public class App { public static void main(String[] args) { // 目標對象 IUserDao target = new UserDao(); // 【原始的類型 class com.zhong.UserDao】 System.out.println(target.getClass()); // 給目標對象,建立代理對象 IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance(); // class $Proxy0 內存中動態生成的代理對象 System.out.println(proxy.getClass()); // 執行方法 【代理對象】 proxy.save(); } }
在這裏咱們會想:代理對象是誰,是如何生成這個代理對象的呢?接下來咱們主要看這個方法 getProxyInstance()
//給目標對象生成代理對象 public Object getProxyInstance(){ return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("開始事務111"); //執行目標對象方法 Object returnValue = method.invoke(target, args); System.out.println("提交事務111"); return returnValue; } } );
咱們看到其返回了一個Proxy類的對象,即JDK的動態代理,是經過一個叫Proxy的類的靜態方法newProxyInstance來實現的,其那麼咱們就去它的源碼裏看一下它到底都作了些什麼?
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //檢查h 不爲空,不然拋異常 Objects.requireNonNull(h); final Class>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 得到與指定類裝載器和一組接口相關的代理類類型對象 */ Class> cl = getProxyClass0(loader, intfs); /* * 經過反射獲取構造函數對象並生成代理類實例 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //獲取代理對象的構造方法(也就是$Proxy0(InvocationHandler h)) final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true); return null; } }); } //生成代理類的實例並把InvocationHandlerImpl的實例傳給它的構造方法 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
上面的代碼代表,首先經過getProxyClass0得到這個代理類,而後經過c1.getConstructor()拿到構造函數,最後一步,經過cons.newInstance返回這個新的代理類的一個實例,注意:調用newInstance的時候,傳入的參數爲h,即咱們本身定義好的InvocationHandler類 。咱們再進去getProxyClass0方法看一下:
/** * Generate a proxy class. Must call the checkProxyAccess method * to perform permission checks before calling this. */ private static Class> getProxyClass0(ClassLoader loader, Class>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); }
這裏用到了緩存,先從緩存裏查一下,若是存在,直接返回,不存在就新建立。
真相仍是沒有來到,繼續,看一下proxyClassCache
/** * a cache of proxy classes */ private static final WeakCache[], Class>> proxyClassCache = new WeakCache(new KeyFactory(), new ProxyClassFactory());
再看下proxyClassCache.get方法,
public synchronized V get() { // serialize access // re-check Supplier supplier = valuesMap.get(subKey); if (supplier != this) { // something changed while we were waiting: // might be that we were replaced by a CacheValue // or were removed because of failure -> // return null to signal WeakCache.get() to retry // the loop return null; } // else still us (supplier == this) // create new value V value = null; try { value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { // remove us on failure valuesMap.remove(subKey, this); } } // the only path to reach here is with non-null value assert value != null; // wrap value with CacheValue (WeakReference) CacheValue cacheValue = new CacheValue(value); // try replacing us with CacheValue (this should always succeed) if (valuesMap.replace(subKey, this, cacheValue)) { // put also in reverseMap reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } // successfully replaced us with new CacheValue -> return the value // wrapped by it return value; } }
其中,value = Objects.requireNonNull(valueFactory.apply(key, parameter));
提到了apply(),是Proxy類的內部類ProxyClassFactory實現其接口的一個方法,具體實現以下:
/** * A factory function that generates, defines and returns the proxy class given * the ClassLoader and array of interfaces. */ private static final class ProxyClassFactory implements BiFunction[], Class>> { // prefix for all proxy class names private static final String proxyClassNamePrefix = "$Proxy"; // next number to use for generation of unique proxy class names private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class> apply(ClassLoader loader, Class>[] interfaces) { Map, Boolean> interfaceSet = new IdentityHashMap(interfaces.length); for (Class> intf : interfaces) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ Class> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (Class> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } }
這裏咱們看到了熟悉的方法Class.forName();要加載指定的接口,便是生成類,那就有對應的class字節碼
/生成字節碼 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
接下來咱們也使用測試一下,使用這個方法生成的字節碼是個什麼樣子:
package com.adam.java.basic; import java.io.FileOutputStream; import java.io.IOException; import sun.misc.ProxyGenerator; public class DynamicProxyTest { public static void main(String[] args) { IUserDao userdao = new UserDao(); ProxyFactory handler = new ProxyFactory ( userdao); IUserDao proxy = (IUserDao ) handler.getProxyInstance(); proxy.save(); String path = "C:/$Proxy0.class"; byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", UserDao.class.getInterfaces()); FileOutputStream out = null; try { out = new FileOutputStream(path); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }
不是原始的IUserDao裏的save()方法了,而是新生成的代理類的save()方法,咱們將生成的$Proxy0.class文件用jd-gui打開
public final void save() throws { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }
核心就在於this.h.invoke(this. m3, null);此處的h是啥呢?咱們看看這個類的類名:
public final class $Proxy0 extends Proxy implements IUserDao
不難發現,新生成的這個類,繼承了Proxy類實現了IUserDao這個接口,而這個UserService就是咱們指定的接口,因此,這裏咱們基本能夠判定,JDK的動態代理,生成的新代理類就是繼承了Proxy基類,實現了傳入的接口的類。那這個h究竟是啥呢?咱們再看看這個新代理類,看看構造函數:
public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); }
這裏傳入了InvocationHandler類型的參數,而以前有一句代碼:
return cons.newInstance(new Object[]{h});
這是newInstance方法的最後一句,傳入的h,就是這裏用到的h,也就是咱們最初本身定義的MyInvocationHandler類的實例。因此,咱們發現,其實最後調用的save()方法,其實調用的是ProxyFactory的invoke()方法.繼續看:
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.zhong.IUserDao").getMethod("save", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; }
m3就是原接口的save()方法.
經過跟蹤提示代碼能夠看出:當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用。
總結:
動態代理實現過程:
1. 經過getProxyClass0()生成代理類。JDK生成的最終真正的代理類,它繼承自Proxy並實現了咱們定義的接口.
2. 經過Proxy.newProxyInstance()生成代理類的實例對象,建立對象時傳入InvocationHandler類型的實例。
3. 調用新實例的方法,即此例中的save(),即原InvocationHandler類中的invoke()方法。
代理對象不須要實現接口,可是目標對象必定要實現接口,不然不能用動態代理
JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現加強,但由於採用的是繼承,因此不能對final修飾的類進行代理。
Cglib代理,也叫做子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展
Cglib子類代理實現方法:
1.須要引入cglib的jar文件,可是Spring的核心包中已經包括了Cglib功能,因此直接引入Spring-core.jar
便可.
2.引入功能包後,就能夠在內存中動態構建子類
3.代理的類不能爲final,不然報錯
4.目標對象的方法若是爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.
代碼演示以下:
/** * 目標對象,沒有實現任何接口 */ public class UserDao { public void save() { System.out.println("----保存數據成功!----"); } }
Cglib代理工廠:ProxyFactory
/** * Cglib子類代理工廠 * 對UserDao在內存中動態構建一個子類對象 */ public class ProxyFactory implements MethodInterceptor{ //維護目標對象 private Object target; public ProxyFactory(Object target) { this.target = target; } //給目標對象建立一個代理對象 public Object getProxyInstance(){ //1.工具類 Enhancer en = new Enhancer(); //2.設置父類 en.setSuperclass(target.getClass()); //3.設置回調函數 en.setCallback(this); //4.建立子類(代理對象) return en.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("開始事務..."); //執行目標對象的方法 Object returnValue = method.invoke(target, args); System.out.println("提交事務..."); return returnValue; } }
測試類APPTest:
/** * 測試類 */ public class AppTest { @Test public void test(){ //目標對象 UserDao target = new UserDao(); //代理對象 UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance(); //執行代理對象的方法 proxy.save(); } }
連接:http://www.cnblogs.com/cenyu/p/6289209.html;