java的三種代理模式

1.代理模式很好地將兩個直接關聯的類進行了解耦,而且還能夠在代理類中添加額外的代碼,以進行特殊的處理,若是不採用代理模式,當兩個類關聯式,就須要再代碼中直接調用另外一個類B,這樣若是須要添加一些特殊的處理,就通用要直接寫在某一個類A的代碼中,當有不少類都須要這些特殊處理時,每一個類都必須編寫相應的特殊處理的代碼,就不能進行代碼的複用,採用代理模式就能避免這些問題。java

在程序開發過程當中,常常會遇到一些和具體的業務邏輯無關的代碼控制,如日誌,權限,事務處理等,每段代碼都添加日誌或權限,事務處理的代碼,程序編寫起來會很麻煩,若是能有一個代理類,統一進行額外的處理,就能夠放更多精力在業務邏輯代碼中了。spring

代理模式的關鍵點:代理對象與目標對象。代理對象是對目標對象的擴展,並會調用目標對象。編程

代理模式就是給一個對象提供一個代理對象,由這個代理對象控制對原對象的引用,使代理類在客戶端和原對象之間起到一箇中介的做用。代理模式主要由3部分組成:抽象目標類,具體目標類和代理類。框架

2.靜態代理ide

靜態代理由咱們本身去生成的固定的代碼進行編譯。須要定義接口或者抽象的父類做爲抽象目標類,具體目標類和代理類一塊兒實現相同的接口或者是繼承相同的類,而後經過調用相同的方法來調用目標對象的方法。函數

代碼示例:測試

2.1)抽象目標類ui

/**
 * IUserDao interface
 * 接口
 * @author guanhuifang
 * @date 2018/2/24 下午3:35
 **/
public interface IUserDao {
    void save();
}

2.2 )具體目標類,即被代理對象this

/**
 * UserDao class
 *
 * @author guanhuifang
 * @date 2018/2/24 下午3:36
 * 目標對象
 **/
public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("==已經保存數據啦");
    }
}

2.3)代理類代理

/**
 * UserDaoProxy class
 *
 * @author guanhuifang
 * @date 2018/2/24 下午3:38
 **/
public class UserDaoProxy implements IUserDao {
    private IUserDao iUserDao;


    public UserDaoProxy(IUserDao iUserDao) {
        this.iUserDao = iUserDao;
    }

    @Override

    public void save() {
        System.out.println("開始事務。。。");
        iUserDao.save(); //執行目標對象
        System.out.println("提交事務。。。");
    }
}

測試類

public class App {

    public static void main(String[] args) {
        //目標對象
        UserDao user = new UserDao();
        //代理對象,將目標對象傳給代理對象,創建代理關係
        UserDaoProxy proxy = new UserDaoProxy(user);
        proxy.save(); //執行的是代理的方法
    }
}

運行結果:

開始事務。。。
==已經保存數據啦
提交事務。。。

靜態代理能夠在不修改目標對象的功能前提下,對目標功能進行擴展。

缺點是:

由於代理對象須要與目標對象實現同樣的接口,因此會有不少的代理類,類太多。同時,一旦接口增長方法,目標對象與代理對象都要維護。

解決此缺點的方法就是可使用動態代理方式

3.動態代理

動態代理模式:在程序運行期間動態生成代理類。spring的aop面向切面編程就是使用動態代理模式來實現的。

JVM能夠本身建立代理類,這樣不只提升了效率,並且更靈活。動態代理的3個主要的類:

proxy類、InvocationHandler、Method

經過proxy類和InvocationHandler接口能夠生成JDK動態代理類和動態代理對象。

對於代理的接口的實際處理,是一個java.lang.reflect.InvocationHandler,它提供了一個invoke方法供實現者提供相應的代理邏輯的實現。能夠兌實際的實現進行一些特殊的處理,像spring aop中的各類Adevice.

Proxy類中主要有3個方法:

protected Proxy(InvocationHandler h) //構造方法

 public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

每一個代理實例都必須指定一個調用處理器,代理對象調用方法時,該方法會指派到調用處理器的invoke()中去。代理的方法封裝成invoke中的method對象,其中的參數封裝成Object[].

動態代理實現的步驟:

1)寫一個代理類實現InvocationHandler接口,經過構造函數把代理對象(具體目標類)傳入到此處理器中,在invoke方法中增長method.invoke(realSubject,args)。

2)在調用方法時,經過java.lang.reflect.Proxy的newProxyInstance來獲取代理實現類,生成代理對象時,直接調用方法便可。

代碼示例:

3.1)被代理的接口

/**
 * BusinessInterface class
 * 代理接口
 * @author guanhuifang
 * @date 2018/2/24 下午5:49
 **/
public interface BusinessInterface {

    public void doWork();
}

3.2)具體實現類,被代理對象

/**
 * Business class
 *具體實現類,被代理對象
 * @author guanhuifang
 * @date 2018/2/24 下午5:50
 **/
public class Business implements BusinessInterface {
    @Override
    public void doWork() {
        System.out.println("進行業務邏輯的處理");
    }
}

3.3)動態代理類

/**
 * BusinessHandler class
 *
 * @author guanhuifang
 * @date 2018/2/24 下午5:51
 **/
public class BusinessHandler implements InvocationHandler {

    private BusinessInterface businessInterface;

    public BusinessHandler(BusinessInterface businessInterface) {
        this.businessInterface = businessInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("處理業務邏輯以前。");
        method.invoke(businessInterface, args);
        System.out.println("處理業務邏輯以後。");
        return null;
    }
}

3.4)調用

/**
 * Client class
 *
 * @author guanhuifang
 * @date 2018/2/24 下午5:54
 **/
public class Client {

    public static void main(String[] args) {
        //具體實現類對象
        Business business = new Business();

        //生成代理類對象,利用proxy的靜態方法Proxy.newProxyInstance()來爲一組接口動態的生成代理類及對象,也就是動態生成代理對象的方法
        BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(business.getClass().getClassLoader(),business.getClass().getInterfaces(),new BusinessHandler(business));
        proxy.doWork();  //調用代理類的對象
      
     
    }
}

參數說明:
 business.getClass().getClassLoader()  爲具體實現類business的classLoader,負責加載動態代理類
 business.getClass().getInterfaces()   爲具體實現類business的全部接口
 new BusinessHandler(business)   代理類,把方法調用轉到代理上

運行結果:

處理業務邏輯以前。
進行業務邏輯的處理
處理業務邏輯以後。

Process finished with exit code 0

4.Cglib代理

因爲靜態代理和動態代理模式的目標對象都是實現一個接口的目標對象,但有時候目標對象是一個單獨的對象,並無任何實現的接口,這個時候就用上了CGLIB代理模式。

其實spring中的AOP,有兩種代理模式,一種是jdk動態代理,另外一種是cglib代理。jdk動態代理要求被代理的目標對象必須是接口,cglib動態代理,也叫子類代理,它是在內存中建立一個子類對象來實現對目標類功能的擴展。CGLIB代理會讓生成的代理類繼承被代理類,並在代理類中對代理方法進行強化處理,如前置處理和後置處理。在CGLIB底層,實際上是藉助了ASM這個強大的java字節碼生成框架。

示例代碼:

引入cglib依賴

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.4</version>
        </dependency>

被代理類:

/**
 * Hello class
 *
 * @author guanhuifang
 * @date 2018/2/25 上午11:52
 **/
public class Hello {
    public void sayHello() {
        System.out.println("Hello World!");
    }
}

實現MethodInterceptor接口生成方法攔截器

/**
 * HelloMethodInterceptor class
 *
 * @author guanhuifang
 * @date 2018/2/25 上午11:55
 **/
public class HelloMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("以前"+method.getName());
        methodProxy.invokeSuper(o,objects);
        System.out.println("以後"+method.getName());
        return null;
    }
}

客戶端驗證:

/**
 * Client class
 *
 * @author guanhuifang
 * @date 2018/2/25 上午11:59
 **/
public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Hello.class); //繼承被代理類
        enhancer.setCallback(new HelloMethodInterceptor()); //設置回調
        Hello hello = (Hello) enhancer.create(); //生成代理類對象
        hello.sayHello();
    }
}

運行結果:

以前sayHello
Hello World!
以後sayHello

Process finished with exit code 0

在enhance.create()建立完代理類對象以後,在代理類調用方法中會被咱們實現的方法攔截器HelloMethodInterceptor攔截。若是被代理類hello被final修飾,則hello類不能被繼承,即不能被代理。一樣,若是被代理類hello類存在final修飾的方法,那麼該方法不能被代理。

相關文章
相關標籤/搜索