程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Java動態代理、cglib動態代理

Java動態代理、cglib動態代理

編輯:JAVA綜合教程

Java動態代理、cglib動態代理


說動態代理,需要先清楚靜態代理。所謂靜態代理就是程序員提前實現好的代理類,編譯後class文件是已經存在的。
實現原理,利用Java代理模式,由一個代理類持有委托類的實例,並實現委托類一樣的接口,來實現增強方法的目的。

我們主要用它來做方法的增強,讓你可以在不修改源碼的情況下,增強一些方法,在方法執行前後做任何你想做的事情,甚至根本不去執行這個方法。因為在InvocationHandler的invoke方法中,你可以直接獲取正在調用方法對應的Method對象。比如可以添加調用日志,做事務控制,對方法進行緩存等。

Spring容器代替工廠,Spring AOP代替JDK動態代理,讓面向切面編程更容易實現。在Spring的幫助下輕松添加,移除動態代理,且對源代碼無任何影響。

本文給出靜態代理、JDK動態代理、CGLIB動態代理的三種例子。

一、靜態代理
在了解代理模式的情況下看下面的代碼,沒什麼可說的。

package com.shanhy.demo.proxy;

public interface Account {

    public void queryAccount();

    public void updateAccount();

}
package com.shanhy.demo.proxy;

public class AccountImpl implements Account {

    @Override
    public void queryAccount() {
        System.out.println("查看賬戶");
    }

    @Override
    public void updateAccount() {
        System.out.println("修改賬戶");
    }

}
package com.shanhy.demo.proxy;

public class AccountProxy implements Account {

    private Account account;

    public AccountProxy(Account account) {
        super();
        this.account = account;
    }

    @Override
    public void queryAccount() {
        System.out.println("代理before");
        account.queryAccount();
        System.out.println("代理after");
    }

    @Override
    public void updateAccount() {
        System.out.println("代理before");
        account.updateAccount();
        System.out.println("代理after");
    }

}
package com.shanhy.demo.proxy;

public class AccountProxyTest {

    public static void main(String[] args) {
        // AccountProxy為自己實現的代理類,可以發現,一個代理類只能為一個接口服務。
        Account account = new AccountImpl(); 
        AccountProxy proxy = new AccountProxy(account);
        proxy.queryAccount();
        proxy.updateAccount();
    }
}

二、JDK動態代理
使用JDK動態代理使用到一個Proxy類和一個InvocationHandler接口。
Proxy已經設計得非常優美,但是還是有一點點小小的遺憾之處,那就是它僅支持interface代理(也就是代理類必須實現接口),因為它的設計注定了這個遺憾。

package com.shanhy.demo.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class AccountProxyFactory implements InvocationHandler {

    private Object target;

    public Object bind(Object target){
        // 這裡使用的是Jdk的動態代理,其必須要綁定接口,在我們的業務實現中有可能是沒有基於接口是實現的。所以說這個缺陷cglib彌補了。
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//      System.out.println(Proxy.isProxyClass(proxy.getClass()));
        boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object");

        Object result = null;
        if(!objFlag)
            System.out.println("代理before");
        result = method.invoke(this.target, args);
        if(!objFlag)
            System.out.println("代理after");
        return result;
    }


}
package com.shanhy.demo.proxy;

public class AccountProxyTest {

    public static void main(String[] args) {
        // 下面使用JDK的代理類,一個代理就可以代理很多接口
        Account account1 = (Account)new AccountProxyFactory().bind(new AccountImpl());
        System.out.println(account1);
        account1.queryAccount();
}

三、CGLIB動態代理
對於上面說到JDK僅支持對實現接口的委托類進行代理的缺陷,這個問題CGLIB給予了很好的補位,解決了這個問題,使其委托類也可是非接口實現類。
CGLIB內部使用到ASM,所以我們下面的例子需要引入asm-3.3.jar、cglib-2.2.2.jar

package com.shanhy.demo.proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class AccountCglibProxyFactory implements MethodInterceptor{

    private Object target;

    public Object getInstance(Object target){
        this.target = target;
        return Enhancer.create(this.target.getClass(), this);

//      Enhancer enhancer = new Enhancer();//該類用於生成代理對象
//      enhancer.setSuperclass(this.target.getClass());//設置父類
//      enhancer.setCallback(this);//設置回調用對象為本身
//      return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 排除Object類中的toString等方法
        boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object");
        if(!objFlag){
            System.out.println("before");
        }
        Object result = null;
//      我們一般使用proxy.invokeSuper(obj,args)方法。這個很好理解,就是執行原始類的方法。還有一個方法proxy.invoke(obj,args),這是執行生成子類的方法。
//      如果傳入的obj就是子類的話,會發生內存溢出,因為子類的方法不挺地進入intercept方法,而這個方法又去調用子類的方法,兩個方法直接循環調用了。
        result = methodProxy.invokeSuper(obj, args);
//      result = methodProxy.invoke(obj, args);
        if(!objFlag){
            System.out.println("after");
        }
        return result;
    }

}
package com.shanhy.demo.proxy;

public class Person {

    public void show(){
        System.out.println("showing");
    }
}
package com.shanhy.demo.proxy;

public class AccountProxyTest {

    public static void main(String[] args) {
        // 下面是用cglib的代理
        // 1.支持實現接口的類
        Account account2 = (Account)new AccountCglibProxyFactory().getInstance(new AccountImpl());
        account2.updateAccount();

        // 2.支持未實現接口的類
        Person person = (Person)new AccountCglibProxyFactory().getInstance(new Person());
        System.out.println(person);
        person.show();
    }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved