代理-你可能需要知道這些

代理在我們?nèi)粘i_發(fā)中是一個(gè)很常見的知識(shí)點(diǎn)败许,也是我們面試中經(jīng)常被問到的內(nèi)容脐区,本本博文帶大家來學(xué)習(xí)和分析下代理的相關(guān)內(nèi)容。

1. 概念

代理(Proxy)是一種設(shè)計(jì)模式,提供了對(duì)目標(biāo)對(duì)象另外的訪問方式;即通過代理對(duì)象訪問目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能.

這里使用到編程中的一個(gè)思想:不要隨意去修改別人已經(jīng)寫好的代碼或者方法,如果需改修改,可以通過代理的方式來擴(kuò)展該方法。

舉個(gè)例子來說明代理的作用:假設(shè)我們想邀請(qǐng)一位明星,那么并不是直接連接明星,而是聯(lián)系明星的經(jīng)紀(jì)人,來達(dá)到同樣的目的.明星就是一個(gè)目標(biāo)對(duì)象,他只要負(fù)責(zé)活動(dòng)中的節(jié)目,而其他瑣碎的事情就交給他的代理人(經(jīng)紀(jì)人)來解決.這就是代理思想在現(xiàn)實(shí)中的一個(gè)例子.

其中的明星我們一般稱為委托類葬燎,或者稱之為被代理類眯分;而明星的經(jīng)紀(jì)人我們稱之為代理類拌汇。

代理的優(yōu)點(diǎn):

  1. 隱藏委托類的實(shí)現(xiàn)。
  2. 解耦弊决,在不改委托類的實(shí)現(xiàn)下添加一些額外操作噪舀。

2. 分類

根據(jù)運(yùn)行前委托類是否存在魁淳,我們將代理分為兩類:

  • 靜態(tài)代理
  • 動(dòng)態(tài)代理

2.1 靜態(tài)代理

代理類在程序運(yùn)行前已經(jīng)存在的代理方式稱之為靜態(tài)代理。

靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類.

2.1.1 實(shí)例

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

    void save();
}

/**
 * 接口實(shí)現(xiàn)
 * 目標(biāo)對(duì)象
 */
public class UserDao implements IUserDao {
    public void save() {
        System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
    }
}

/**
 * 代理對(duì)象,靜態(tài)代理
 */
public class UserDaoProxy implements IUserDao{
    //接收保存目標(biāo)對(duì)象
    private IUserDao target;
    public UserDaoProxy(IUserDao target){
        this.target=target;
    }

    public void save() {
        System.out.println("開始事務(wù)...");
        target.save();//執(zhí)行目標(biāo)對(duì)象的方法
        System.out.println("提交事務(wù)...");
    }
}

優(yōu)缺點(diǎn):
1.可以做到在不修改目標(biāo)對(duì)象的功能前提下,對(duì)目標(biāo)功能擴(kuò)展.
2.缺點(diǎn):

  • 因?yàn)榇韺?duì)象需要與被代理對(duì)象實(shí)現(xiàn)一樣的接口,所以會(huì)有很多代理類,類太多.同時(shí),一旦接口增加方法,被代理對(duì)象與代理對(duì)象都要維護(hù)与倡。

那么有沒有什么方法界逛,可以解決靜態(tài)代理的缺點(diǎn)呢?有纺座,動(dòng)態(tài)代理息拜。

2.2 動(dòng)態(tài)代理

代理類在程序運(yùn)行前不存在、運(yùn)行時(shí)由程序動(dòng)態(tài)生成的代理方式稱為動(dòng)態(tài)代理净响。

Java 提供了動(dòng)態(tài)代理的實(shí)現(xiàn)方式少欺,可以在運(yùn)行時(shí)刻動(dòng)態(tài)生成代理類。
這種代理方式的一大好處是可以方便對(duì)代理類的函數(shù)做統(tǒng)一或特殊處理馋贤,如記錄所有函數(shù)執(zhí)行時(shí)間赞别、所有函數(shù)執(zhí)行前添加驗(yàn)證判斷、對(duì)某個(gè)特殊函數(shù)進(jìn)行特殊操作掸掸,而不用像靜態(tài)代理方式那樣需要修改每個(gè)函數(shù)氯庆。

2.2.1 實(shí)現(xiàn)

  • 新建委托類
  • 實(shí)現(xiàn)InvocationHandler,這是負(fù)責(zé)代理類和委托類的中間類必須實(shí)現(xiàn)的接口扰付。
  • 通過Proxy類實(shí)現(xiàn)新建代理類對(duì)象
  1. 新建委托類
public interface Operate {

    public void operateMethod1();

    public void operateMethod2();

    public void operateMethod3();
}

public class OperateImpl implements Operate {

    @Override
    public void operateMethod1() {
        System.out.println("Invoke operateMethod1");
        sleep(110);
    }

    @Override
    public void operateMethod2() {
        System.out.println("Invoke operateMethod2");
        sleep(120);
    }

    @Override
    public void operateMethod3() {
        System.out.println("Invoke operateMethod3");
        sleep(130);
    }

    private static void sleep(long millSeconds) {
        try {
            Thread.sleep(millSeconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  1. 實(shí)現(xiàn)InvocationHandler
public class TimingInvocationHandler implements InvocationHandler {

    private Object target;

    public TimingInvocationHandler() {}

    public TimingInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        Object obj = method.invoke(target, args);
        System.out.println(method.getName() + " cost time is:" + (System.currentTimeMillis() - start));
        return obj;
    }
}

  • target屬性表示委托類對(duì)象堤撵。
  • InvocationHandler是負(fù)責(zé)連接代理類和委托類的中間類必須實(shí)現(xiàn)的接口。其中只有一個(gè)
public Object invoke(Object proxy, Method method, Object[] args)

函數(shù)需要去實(shí)現(xiàn)羽莺,參數(shù):

  • proxy表示下面通過 Proxy.newProxyInstance() 生成的代理類對(duì)象实昨。
  • method表示代理對(duì)象被調(diào)用的函數(shù)。
  • args表示代理對(duì)象被調(diào)用的函數(shù)的參數(shù)盐固。

調(diào)用代理對(duì)象的每個(gè)函數(shù)實(shí)際最終都是調(diào)用了InvocationHandler的invoke函數(shù)荒给。這里我們?cè)趇nvoke實(shí)現(xiàn)中添加了開始結(jié)束計(jì)時(shí),其中還調(diào)用了委托類對(duì)象target的相應(yīng)函數(shù)刁卜,這樣便完成了統(tǒng)計(jì)執(zhí)行時(shí)間的需求志电。
invoke函數(shù)中我們也可以通過對(duì)method做一些判斷,從而對(duì)某些函數(shù)特殊處理蛔趴。

  1. 生成代理對(duì)象
public class Main {
    public static void main(String[] args) {
        // create proxy instance
        TimingInvocationHandler timingInvocationHandler = new TimingInvocationHandler(new OperateImpl());
        Operate operate = (Operate)(Proxy.newProxyInstance(Operate.class.getClassLoader(), new Class[] {Operate.class},
                timingInvocationHandler));

        // call method of proxy instance
        operate.operateMethod1();
        System.out.println();
        operate.operateMethod2();
        System.out.println();
        operate.operateMethod3();
    }
}
  • 這里我們先將委托類對(duì)象new OperateImpl()作為TimingInvocationHandler構(gòu)造函數(shù)入?yún)?chuàng)建timingInvocationHandler對(duì)象挑辆;
  • 然后通過Proxy.newProxyInstance(…)函數(shù)新建了一個(gè)代理對(duì)象,實(shí)際代理類就是在這時(shí)候動(dòng)態(tài)生成的孝情。我們調(diào)用該代理對(duì)象的函數(shù)就會(huì)調(diào)用到timingInvocationHandler的invoke函數(shù)(是不是有點(diǎn)類似靜態(tài)代理)鱼蝉,而invoke函數(shù)實(shí)現(xiàn)中調(diào)用委托類對(duì)象new OperateImpl()相應(yīng)的 method(是不是有點(diǎn)類似靜態(tài)代理)。

2.2.2 newProxyInstance

下面我們來分下Proxy.newProxyInstance方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  • loader表示類加載器
  • interfaces表示委托類的接口箫荡,生成代理類時(shí)需要實(shí)現(xiàn)這些接口
  • h是InvocationHandler實(shí)現(xiàn)類對(duì)象魁亦,負(fù)責(zé)連接代理類和委托類的中間類

我們可以這樣理解,如上的動(dòng)態(tài)代理實(shí)現(xiàn)實(shí)際是雙層的靜態(tài)代理羔挡,開發(fā)者提供了委托類 B洁奈,程序動(dòng)態(tài)生成了代理類 A间唉。開發(fā)者還需要提供一個(gè)實(shí)現(xiàn)了InvocationHandler的子類 C,子類 C 連接代理類 A 和委托類 B睬魂,它是代理類 A 的委托類终吼,委托類 B 的代理類。用戶直接調(diào)用代理類 A 的對(duì)象氯哮,A 將調(diào)用轉(zhuǎn)發(fā)給委托類 C,委托類 C 再將調(diào)用轉(zhuǎn)發(fā)給它的委托類 B商佛。

3. 動(dòng)態(tài)代理原理

3.1 生成的動(dòng)態(tài)代理類代碼

下面是上面示例程序運(yùn)行時(shí)自動(dòng)生成的動(dòng)態(tài)代理類代碼喉钢。

public final class $Proxy0 extends Proxy
  implements Operate
{
  private static Method m4;
  private static Method m1;
  private static Method m5;
  private static Method m0;
  private static Method m3;
  private static Method m2;

  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final void operateMethod1()
    throws 
  {
    try
    {
      h.invoke(this, m4, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void operateMethod2()
    throws 
  {
    try
    {
      h.invoke(this, m5, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void operateMethod3()
    throws 
  {
    try
    {
      h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m4 = Class.forName("com.codekk.java.test.dynamicproxy.Operate").getMethod("operateMethod1", new Class[0]);
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m5 = Class.forName("com.codekk.java.test.dynamicproxy.Operate").getMethod("operateMethod2", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m3 = Class.forName("com.codekk.java.test.dynamicproxy.Operate").getMethod("operateMethod3", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

從中我們可以看出動(dòng)態(tài)生成的代理類是以$Proxy為類名前綴,繼承自Proxy良姆,并且實(shí)現(xiàn)了Proxy.newProxyInstance(…)第二個(gè)參數(shù)傳入的所有接口的類肠虽。
如果代理類實(shí)現(xiàn)的接口中存在非 public 接口,則其包名為該接口的包名玛追,否則為com.sun.proxy税课。
其中的operateMethod1()、operateMethod2()痊剖、operateMethod3()函數(shù)都是直接交給h去處理韩玩,h在父類Proxy中定義為
protected InvocationHandler h;
即為Proxy.newProxyInstance(…)第三個(gè)參數(shù)。
所以InvocationHandler的子類 C 連接代理類 A 和委托類 B陆馁,它是代理類 A 的委托類找颓,委托類 B 的代理類。

3.2 生成動(dòng)態(tài)代理類原理

  1. newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, interfaces);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            return newInstance(cons, h);
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

    private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
        try {
            return cons.newInstance(new Object[] {h} );
        } catch (IllegalAccessException | InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString());
            }
        }
    }

從上面的代碼我們可以看到叮贩,調(diào)用了getProxyClass0得到動(dòng)態(tài)代理類击狮,然后將h傳入了動(dòng)態(tài)代理類。

  1. getProxyClass0

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        // 判斷委托類的接口數(shù)量益老,超載直接拋出異常
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // 聲明代理類對(duì)象
        Class<?> proxyClass = null;

        /* collect interface names to use as key for proxy class cache */
        String[] interfaceNames = new String[interfaces.length];

        // for detecting duplicates
        Set<Class<?>> interfaceSet = new HashSet<>();

      /**
        * 入?yún)?interfaces 檢驗(yàn)彪蓬,包含三部分
        * (1)是否在入?yún)⒅付ǖ?ClassLoader 內(nèi)
        * (2)是否是 Interface
        * (3)interfaces 中是否有重復(fù)
        */
        for (int i = 0; i < interfaces.length; i++) {
            /*
             * Verify that the class loader resolves the name of this
             * interface to the same Class object.
             */
            String interfaceName = interfaces[i].getName();
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(
                    interfaces[i] + " 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.contains(interfaceClass)) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);

            interfaceNames[i] = interfaceName;
        }

        /*
         * Using string representations of the proxy interfaces as
         * keys in the proxy class cache (instead of their Class
         * objects) is sufficient because we require the proxy
         * interfaces to be resolvable by name through the supplied
         * class loader, and it has the advantage that using a string
         * representation of a class makes for an implicit weak
         * reference to the class.
         */
        // 以接口名對(duì)應(yīng)的 List 作為緩存的 key
        List<String> key = Arrays.asList(interfaceNames);

        /*
         * Find or create the proxy class cache for the class loader.
         */
        /*
        * loaderToCache 是個(gè)雙層的 Map
        * 第一層 key 為 ClassLoader,第二層 key 為 上面的 List捺萌,value 為代理類的弱引用
        */
        Map<List<String>, Object> cache;
        synchronized (loaderToCache) {
            cache = loaderToCache.get(loader);
            if (cache == null) {
                cache = new HashMap<>();
                loaderToCache.put(loader, cache);
            }
            /*
             * This mapping will remain valid for the duration of this
             * method, without further synchronization, because the mapping
             * will only be removed if the class loader becomes unreachable.
             */
        }

        /*
         * Look up the list of interfaces in the proxy class cache using
         * the key.  This lookup will result in one of three possible
         * kinds of values:
         *     null, if there is currently no proxy class for the list of
         *         interfaces in the class loader,
         *     the pendingGenerationMarker object, if a proxy class for the
         *         list of interfaces is currently being generated,
         *     or a weak reference to a Class object, if a proxy class for
         *         the list of interfaces has already been generated.
         */
        /*
         * 以上面的接口名對(duì)應(yīng)的 List 為 key 查找代理類档冬,如果結(jié)果為:
         * (1) 弱引用,表示代理類已經(jīng)在緩存中
         * (2) pendingGenerationMarker 對(duì)象互婿,表示代理類正在生成中捣郊,等待生成完成通知。
         * (3) null 表示不在緩存中且沒有開始生成慈参,添加標(biāo)記到緩存中呛牲,繼續(xù)生成代理類
         */
        synchronized (cache) {
            /*
             * Note that we need not worry about reaping the cache for
             * entries with cleared weak references because if a proxy class
             * has been garbage collected, its class loader will have been
             * garbage collected as well, so the entire cache will be reaped
             * from the loaderToCache map.
             */
            do {
                Object value = cache.get(key);
                if (value instanceof Reference) {
                    proxyClass = (Class<?>) ((Reference) value).get();
                }
                if (proxyClass != null) {
                    // proxy class already generated: return it
                    return proxyClass;
                } else if (value == pendingGenerationMarker) {
                    // proxy class being generated: wait for it
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                        /*
                         * The class generation that we are waiting for should
                         * take a small, bounded time, so we can safely ignore
                         * thread interrupts here.
                         */
                    }
                    continue;
                } else {
                    /*
                     * No proxy class for this list of interfaces has been
                     * generated or is being generated, so we will go and
                     * generate it now.  Mark it as pending generation.
                     */
                    cache.put(key, pendingGenerationMarker);
                    break;
                }
            } while (true);
        }

        try {
            String proxyPkg = null;     // package to define proxy class in

            /*
             * 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.
             */
            /*
             * 如果 interfaces 中存在非 public 的接口,則所有非 public 接口必須在同一包下面驮配,后續(xù)生成的代理類也會(huì)在該包下面
              */
            for (int i = 0; i < interfaces.length; i++) {
                int flags = interfaces[i].getModifiers();
                if (!Modifier.isPublic(flags)) {
                    String name = interfaces[i].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 the default package.
                proxyPkg = "";
            }

            {
                // Android-changed: Generate the proxy directly instead of calling
                // through to ProxyGenerator.
                List<Method> methods = getMethods(interfaces);
                Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                validateReturnTypes(methods);
                List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);

                Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);

                /*
                 * Choose a name for the proxy class to generate.
                 */
                final long num;
                synchronized (nextUniqueNumberLock) {
                    num = nextUniqueNumber++;
                }
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

                // 動(dòng)態(tài)生成代理類的字節(jié)碼
                // 最終調(diào)用 sun.misc.ProxyGenerator.generateClassFile() 得到代理類相關(guān)信息寫入 DataOutputStream 實(shí)現(xiàn)
                proxyClass = generateProxy(proxyName, interfaces, loader, methodsArray,
                        exceptionsArray);
            }
            // add to set of all generated proxy classes, for isProxyClass
            proxyClasses.put(proxyClass, null);

        } finally {
            /*
             * We must clean up the "pending generation" state of the proxy
             * class cache entry somehow.  If a proxy class was successfully
             * generated, store it in the cache (with a weak reference);
             * otherwise, remove the reserved entry.  In all cases, notify
             * all waiters on reserved entries in this cache.
             */
            synchronized (cache) {
                if (proxyClass != null) {
                    cache.put(key, new WeakReference<Class<?>>(proxyClass));
                } else {
                    cache.remove(key);
                }
                cache.notifyAll();
            }
        }
        return proxyClass;
    }


4. CGLib實(shí)現(xiàn)代理

4.1 與JDK代理區(qū)別

JDK動(dòng)態(tài)代理和CGLIB字節(jié)碼生成的區(qū)別娘扩?
(1)JDK動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類生成代理着茸,而不能針對(duì)類
(2)CGLIB是針對(duì)類實(shí)現(xiàn)代理,主要是對(duì)指定的類生成一個(gè)子類琐旁,覆蓋其中的方法
因?yàn)槭抢^承涮阔,所以該類或方法最好不要聲明成final

4.2 對(duì)比

JDK代理是不需要依賴第三方的庫,只要JDK環(huán)境就可以進(jìn)行代理灰殴,它有幾個(gè)要求

  • 實(shí)現(xiàn)InvocationHandler
  • 使用Proxy.newProxyInstance產(chǎn)生代理對(duì)象
  • 被代理的對(duì)象必須要實(shí)現(xiàn)接口
    使用JDK動(dòng)態(tài)代理敬特,目標(biāo)類必須實(shí)現(xiàn)的某個(gè)接口,如果某個(gè)類沒有實(shí)現(xiàn)接口則不能生成代理對(duì)象牺陶。

CGLib 必須依賴于CGLib的類庫伟阔,Cglib原理是針對(duì)目標(biāo)類生成一個(gè)子類,覆蓋其中的所有方法掰伸,所以目標(biāo)類和方法不能聲明為final類型皱炉。針對(duì)接口編程的環(huán)境下推薦使用JDK的代理。從執(zhí)行效率上看狮鸭,Cglib動(dòng)態(tài)代理效率較高合搅。在Hibernate中的攔截器其實(shí)現(xiàn)考慮到不需要其他接口的條件Hibernate中的相關(guān)代理采用的是CGLib來執(zhí)行。

4.3 CGLib實(shí)例

實(shí)例:

/**   
 * CGLibProxy動(dòng)態(tài)代理類的實(shí)例   
 *     
 *    
 */    
public class CGLibProxy implements MethodInterceptor {    
    
    private Object targetObject;// CGLib需要代理的目標(biāo)對(duì)象    
    
    public Object createProxyObject(Object obj) {    
        this.targetObject = obj;    
        Enhancer enhancer = new Enhancer();    
        enhancer.setSuperclass(obj.getClass());    
        enhancer.setCallback(this);    
        Object proxyObj = enhancer.create();    
        return proxyObj;// 返回代理對(duì)象    
    }    
    
    public Object intercept(Object proxy, Method method, Object[] args,    
            MethodProxy methodProxy) throws Throwable {    
        Object obj = null;    
        if ("addUser".equals(method.getName())) {// 過濾方法    
            checkPopedom();// 檢查權(quán)限    
        }    
        obj = method.invoke(targetObject, args);    
        return obj;    
    }    
    
    private void checkPopedom() {    
        System.out.println(".:檢查權(quán)限  checkPopedom()!");    
    }    
}    


// 調(diào)用

UserManager userManager = (UserManager) new CGLibProxy()    
                .createProxyObject(new UserManagerImpl());   

5. 結(jié)束語

詳細(xì)大家通過上面的學(xué)習(xí)歧蕉,已經(jīng)對(duì)代理有了一個(gè)更深層次的認(rèn)識(shí)灾部,代理在AOP開發(fā)中特別有用,讓我們?cè)谠O(shè)計(jì)和開發(fā)中把代理用起來吧廊谓。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末梳猪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蒸痹,更是在濱河造成了極大的恐慌春弥,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叠荠,死亡現(xiàn)場(chǎng)離奇詭異匿沛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)榛鼎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門逃呼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人者娱,你說我怎么就攤上這事抡笼。” “怎么了黄鳍?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵推姻,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我框沟,道長(zhǎng)藏古,這世上最難降的妖魔是什么增炭? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮拧晕,結(jié)果婚禮上隙姿,老公的妹妹穿的比我還像新娘。我一直安慰自己厂捞,他們只是感情好输玷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蔫敲,像睡著了一般饲嗽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奈嘿,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音吞加,去河邊找鬼裙犹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛衔憨,可吹牛的內(nèi)容都是我干的叶圃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼践图,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼掺冠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起码党,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤德崭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后揖盘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眉厨,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年兽狭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了憾股。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡箕慧,死狀恐怖服球,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情颠焦,我是刑警寧澤斩熊,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站蒸健,受9級(jí)特大地震影響座享,放射性物質(zhì)發(fā)生泄漏婉商。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一渣叛、第九天 我趴在偏房一處隱蔽的房頂上張望丈秩。 院中可真熱鬧,春花似錦淳衙、人聲如沸蘑秽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肠牲。三九已至,卻和暖如春靴跛,著一層夾襖步出監(jiān)牢的瞬間缀雳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工梢睛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肥印,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓绝葡,卻偏偏與公主長(zhǎng)得像深碱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子藏畅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Java代理和動(dòng)態(tài)代理機(jī)制分析和應(yīng)用 概述 代理是一種常用的設(shè)計(jì)模式敷硅,其目的就是為其他對(duì)象提供一個(gè)代理以控制對(duì)某個(gè)...
    丸_子閱讀 3,022評(píng)論 6 57
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司愉阎,掛了不少绞蹦,但最終還是拿到小米、百度诫硕、阿里坦辟、京東、新浪章办、CVTE锉走、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,246評(píng)論 11 349
  • 《轉(zhuǎn)》JAVA動(dòng)態(tài)代理(JDK和CGLIB) 代理模式是常用的java設(shè)計(jì)模式,他的特征是代理類與委托類有同樣的接...
    奈何心善閱讀 264評(píng)論 0 0
  • 不要打我,不要打我休偶。 你只是一只丑小鴨梁厉,還想和我們一起玩,你活膩歪了吧。狠以后別來煩我們词顾。 為什么八秃,所有人都不喜歡...
    青詭閱讀 167評(píng)論 0 1
  • 蒲公英戀上樹的落寞,樹戀上浮云的自在肉盹,也許初相遇昔驱,已是場(chǎng)注定了的安排。 每一次傾心上忍,最初總是不經(jīng)意的邂逅骤肛。...
    玄米_1225閱讀 220評(píng)論 1 4