Java中的代理模式

一、什麼是代理模式

代理模式:就是爲其餘對象提供一種代理以控制對這個對象的訪問。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()方法。

 

代理對象不須要實現接口,可是目標對象必定要實現接口,不然不能用動態代理

 

2.3.Cglib代理

 

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;

相關文章
相關標籤/搜索