1. 程式人生 > >設計模式之---代理模式(AOP的原理)

設計模式之---代理模式(AOP的原理)

代理模式有三種:靜態代理,動態代理,Cglib代理。

代理模式的功能主要是起到增強方法和許可權攔截的作用。

1.靜態代理:

其實代理模式根據這個名字就很好理解,舉個簡單例子:小明要去租房,但他找不到的房東,只能去找中介,中介再幫他找房東,而這個中介就起到了代理的作用。


如圖所示:小明的是使用者類,呼叫者,中介就是代理類,房東就是目標類真正需要呼叫的類,這時代理類就可以在中間做點手腳,起到增強方法的作用。

但有個問題就是如果目標類有很多方法,代理類應該也有這麼多方法,這時代理類和目標應該要有一種約定,所以代理類和目標類都應該實現同一個介面。下面是程式碼實現:

目標類和代理類實現的同一介面,約束規定。

/**
 * Created by Ming on 2017/11/25.
 */
public interface LetOutService { 
    Integer letOut();
}

房東類(目標類):
/**
 * Created by Ming on 2017/11/25.
 */
public class Landlady implements LetOutService {


    @Override
    public Integer letOut() {
        System.out.println("租房方法 running...");
        return 100;
    }
}

中介類(代理類):

/**
 * Created by Ming on 2017/11/25.
 */
public class Proxy implements LetOutService {

    @Override
    public Integer letOut() {
        Landlady landlady = new Landlady();
        Integer integer = landlady.letOut();
        return integer + 100;
    }
}

使用者類:
public class User {

    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        Integer integer = proxy.letOut();
        System.out.println(integer);


    }
}

輸出:


2.動態代理:

靜態代理缺點:因為代理物件需要與目標物件實現一樣的介面,所以會有很多代理類,類太多.同時,一旦介面增加方法,目標物件與代理物件都要維護。

解決辦法動態代理:其實動態代理和靜態代理的思想是不變的,動態代理和靜態代理的區別就是,動態代理不用我們去手編寫代理類,在執行時,動態的在記憶體中生產代理類。(位元組碼物件級別的代理物件)。

動態代理的API:

 在java.lang.reflect包中有一個代理類。

  • java.lang.reflect.Proxy     

返回值:Object就是代理物件

引數:

loader:代表與目標物件相同的類載入器-------目標物件.getClass().getClassLoader()

interfaces:代表與目標物件實現的所有的介面位元組碼物件陣列----陣列因為目標類可以有多個介面

h:具體的代理的操作,InvocationHandler介面

注意:JDK的Proxy方式實現的動態代理 目標物件必須有介面 沒有介面不能實現jdk版動態代理!

介紹完方法,我們還是以程式碼為例看看怎麼實現吧:

統一介面 :

/**
 * Created by Ming on 2017/11/25.
 */
public interface TargetInterface {

    void method1();
    void method2();
    int method3(Integer i);

}

目標類:
/**
 * Created by Ming on 2017/11/25.
 */
public class Target implements TargetInterface {
    @Override
    public void method1() {
        System.out.println("method1 running ...");
    }

    @Override
    public void method2() {
        System.out.println("method2 running ...");
    }

    @Override
    public int method3(Integer i) {
        System.out.println("method3 running ...");
        return i;
    }
}

代理工廠類:

/**
 * Created by Ming on 2017/11/23.
 */
public class ProxyFactory {

    static <T> Object getProxy(T t){
        
        //返回一個代理物件
        Object object = Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // proxy就是目標物件,method就是呼叫目標物件中方法,args就是呼叫目標物件中方法的引數。
                //比如說:代理物件.method1(),這時proxy就是目標類,method1就是method,args就是method1方法引數。
                System.out.println("執行方法前...");
                Object invoke = method.invoke(t, args);
                System.out.println("執行方法後...");
                return invoke;
            }
        });

        return object;
    }

}

呼叫者:

/**
 * Created by Ming on 2017/11/25.
 */
public class User {

    public static void main(String[] args) {
        Target target = new Target();

        TargetInterface proxy = (TargetInterface)ProxyFactory.getProxy(target);

        proxy.method1();
        System.out.println("-------------------------");
        proxy.method2();
        System.out.println("-------------------------");
        int i = proxy.method3(100);
        System.out.println(i);

    }
}
輸出:



3.Cglib代理:


第三方代理技術,Cglib代理.可以對任何類生成代理.代理的原理是對目標物件進行繼承代理。如果目標物件被final修飾.那麼該類無法被Cglib代理。

需要引入cglib的jar檔案,但是Spring的核心包中已經包括了Cglib功能,所以直接引入spring-core包

程式碼實現:

目標類  (可以不用實現介面,因為生成的代理類 是目標類的子類):

/**
 * Created by Ming on 2017/11/25.
 */
public class Target {
    public void method1() {
        System.out.println("method1 running ...");
    }

    public void method2() {
        System.out.println("method2 running ...");
    }

    public int method3(Integer i) {
        System.out.println("method3 running ...");
        return i;
    }
}


CglibFactory類:

/**
 * Created by Ming on 2017/11/25.
 */
public class CglibFactory {

    static <T> Object getProxy(T t){
        Enhancer en = new Enhancer(); //幫我們生成代理物件
        en.setSuperclass(t.getClass());//設定要代理的目標類
        en.setCallback(new MethodInterceptor() {//代理要做什麼
            @Override
            public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("執行方法前。。。");
                //呼叫原有方法  
                Object invoke = methodProxy.invokeSuper(object, args);
//              Object invoke = method.invoke(t,args); 作用等同與上面。
                System.out.println("執行方法後。。。");
                return invoke;
            }
        });
        Object proxyObj = en.create();//生成代理物件
        return proxyObj;
    }

}

呼叫類:
/**
 * Created by Ming on 2017/11/25.
 */
public class User {

    public static void main(String[] args) {
        Target target = new Target();


        TargetInterface proxy = (TargetInterface) CglibFactory.getProxy(target);

        System.out.println(proxy.method3(100));

    }
}

輸出:


案例:

使用動態代理設定全域性編碼:

 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        resp.setContentType("text/html;charset=utf-8");
        HttpServletRequest request = (HttpServletRequest) req;
        //使用動態代理 設定全域性編碼
        HttpServletRequest proxyRequest  = (HttpServletRequest) Proxy.newProxyInstance(request.getClass().getClassLoader(),
                request.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object object, Method method, Object[] args) throws Throwable {
                        String name = method.getName();
                        if("getParameter".equals(name)){
                            String invoke = (String) method.invoke(request, args);
                            invoke = new String(invoke.getBytes("iso8859-1"),"UTF-8");
                            return invoke;
                        }
                        return method.invoke(request,args);
                    }
                }
        );
        chain.doFilter(proxyRequest,resp);
    }


重點:

Spring AOP 程式設計的實現原理就是 動態代理和Cglib 代理,當目標類實現介面時使用動態代理,沒有則Cglib代理。

總結:

靜態代理需要自己手動編寫代理類和目標方法。

動態代理就不需要自己手動實現代理類和目標方法,但動態代理的目標類要必須實現介面!

Cglib 代理的目標類就不需要實現介面!但目標類不能被final修飾!