Java中的代理模式


1、什么是代理模式

代理模式:就是為其他對象提供一種代理以控制對這個對象的訪問。

代理可以在不改動目標對象的基礎上,增加其他額外的功能(擴展功能)。

 

 

舉個例子來說明代理的作用: 一般我們想邀請明星來當我們的代言人,我們並不能直接聯系到明星,而是通過其經紀人,來告訴經紀人我們需要和明星進行合作,然后通過經紀人來轉達給明星。,明星只需要做好代言工作就好,其他繁瑣的事情就交於經紀人就可以。這里的經經紀人就是一個代理對象,明星就是一個目標對象。

 

用圖表示如下:

 

2、三種代理模式

 2.1 靜態代理

 靜態代理在使用時,需要定義接口或者父類,被代理對象(目標對象)與代理對象(Proxy)一起實現相同的接口或者是繼承相同父類。

下面通過代碼演示下:

接口IUserDao:

/**
 * 接口
 */
public interface IUserDao {

    void save();

}

目標對象:UserDao:

/**
 * 實現接口
 * 目標對象
 */
public class UserDao implements IUserDao {

    public void save() {
        System.out.println("----保存數據成功!----");
    }

}

代理對象:UserDaoProxy

/**
 * 代理對象(靜態代理)
 */
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;

         

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM