代理模式【介紹、靜態代理、動態代理、入門、應用】

代理介紹

代理(Proxy)是一種設計模式, 提供了對目標對象另外的訪問方式;即經過代理訪問目標對象。 這樣好處: 能夠在目標對象實現的基礎上,加強額外的功能操做。(擴展目標對象的功能)。html

能夠作到在不修改目標對象的功能前提下,對目標對象功能擴展。java

這裏寫圖片描述

簡單理解代理

很簡單舉個例子:設計模式

  • 如今我是一個明星,擁有不少粉絲。粉絲但願我唱歌給他們聽,可是若是都是我來接應他們,我豈不是很忙….因而乎,我就去找了個經紀人。這個經紀人就表明了我。當粉絲想要我唱歌的時候,應該是找經紀人,告訴經紀人想讓我唱歌。markdown

  • 如今我愈來愈紅了,不是粉絲想要我唱歌,我就唱了。我要收費了。可是呢,做爲一個公衆人物,不多是我本身說:我要收10000萬,我纔會去唱歌。因而這就讓經紀人對粉絲說:只有10000萬,我纔會唱歌。ide

  • 不管外界是想要我幹什麼,都要通過個人經紀人。個人經紀人也會在其中考慮收費、推脫它們的請求。學習

經紀人就是代理,實際上臺唱歌、表演的仍是我測試

靜態代理

直接使用例子來講明吧…如今我有一個IUserDao的接口,擁有save方法()this

// 接口
public interface IUserDao {

    void save();
}
  • UserDao實現該接口,重寫save()方法
public class UserDao implements IUserDao{ @Override public void save() { System.out.println("-----已經保存數據!!!------"); } } 

如今,我想要在save()方法保存數據前開啓事務、保存數據以後關閉事務…(固然啦,直接再上面寫不就好了嗎…業務方法少的時候,確實沒毛病…)spa

public void save() {

        System.out.println("開啓事務");
        System.out.println("-----已經保存數據!!!------");

        System.out.println("關閉事務");
    }

可是呢,如今若是我有好多好多個業務方法都須要開啓事務、關閉事務呢?設計

 public void save() { System.out.println("開啓事務"); System.out.println("-----已經保存數據!!!------"); System.out.println("關閉事務"); } public void delete() { System.out.println("開啓事務"); System.out.println("-----已經保存數據!!!------"); System.out.println("關閉事務"); } public void update() { System.out.println("開啓事務"); System.out.println("-----已經保存數據!!!------"); System.out.println("關閉事務"); } public void login() { System.out.println("開啓事務"); System.out.println("-----已經保存數據!!!------"); System.out.println("關閉事務"); } 

…..咱們發現就有了不少不少的重複代碼了…咱們要作的就是:當用戶調用UserDao方法的時候,找的是代理對象、而代理幫我在解決這麼繁瑣的代碼

因而呢,咱們就請了一個代理了

  • 這個代理要和userDao有相同的方法…沒有相同的方法的話,用戶怎麼調用啊??
  • 代理只是對userDao進行加強,真正作事的仍是userDao..

所以,咱們的代理就要實現IUserDao接口,這樣的話,代理就跟userDao有相同的方法了。

public class UserDaoProxy implements IUserDao{

    // 接收保存目標對象【真正作事的仍是UserDao】,所以須要維護userDao的引用
    private IUserDao target;
    public UserDaoProxy(IUserDao target) {
        this.target = target;
    }

    @Override
    public void save() {
        System.out.println("開始事務...");

        target.save();          // 執行目標對象的方法

        System.out.println("提交事務...");
    }
}

外界並非直接去找UserDao,而是要經過代理才能找到userDao

public static void main(String[] args) {
        // 目標對象
        IUserDao target = new UserDao();

        // 代理
        IUserDao proxy = new UserDaoProxy(target);
        proxy.save();  // 執行的是,代理的方法
    }

這樣一來,咱們在UserDao中就不用寫那麼傻逼的代碼了…傻逼的事情都交給代理去幹了…

爲何要用動態代理?

咱們首先來看一下靜態代理的不足

  • 若是接口改了,代理的也要跟着改,很煩!
  • 由於代理對象,須要與目標對象實現同樣的接口。因此會有不少代理類,類太多。

動態代理比靜態代理好的地方:

  • 代理對象,不須要實現接口【就不會有太多的代理類了】
  • 代理對象的生成,是利用JDKAPI, 動態地在內存中構建代理對象(須要咱們指定建立 代理對象/目標對象 實現的接口的類型;)

動態代理快速入門

Java提供了一個Proxy類,調用它的newInstance方法能夠生成某個對象的代理對象,該方法須要三個參數:

這裏寫圖片描述

  • 參數一:生成代理對象使用哪一個類裝載器【通常咱們使用的是代理類的裝載器】
  • 參數二:生成哪一個對象的代理對象,經過接口指定【指定要代理類的接口】
  • 參數三:生成的代理對象的方法裏幹什麼事【實現handler接口,咱們想怎麼實現就怎麼實現】

在編寫動態代理以前,要明確兩個概念:

  • 代理對象擁有目標對象相同的方法【由於參數二指定了對象的接口】
  • 用戶調用代理對象的什麼方法,都是在調用處理器的invoke方法。
  • 使用JDK動態代理必需要有接口【參數二須要接口】

對象

小明是一個明星,擁有唱歌和跳舞的方法。實現了人的接口

public class XiaoMing implements Person {

    @Override
    public void sing(String name) {

        System.out.println("小明唱" + name);
    }

    @Override
    public void dance(String name) {

        System.out.println("小明跳" + name);

    }
}

接口

public interface Person {
    void sing(String name);

    void dance(String name);
}

代理類

public class XiaoMingProxy {

    //代理只是一箇中介,實際幹活的仍是小明,因而須要在代理類上維護小明這個變量
    XiaoMing xiaoMing = new XiaoMing();


    //返回代理對象
    public Person getProxy() {

        /** * 參數一:代理類的類加載器 * 參數二:被代理對象的接口 * 參數三:InvocationHandler實現類 */
        return (Person)Proxy.newProxyInstance(XiaoMingProxy.class.getClassLoader(), xiaoMing.getClass().getInterfaces(), new InvocationHandler() {

            /** * proxy : 把代理對象本身傳遞進來 * method:把代理對象當前調用的方法傳遞進來 * args:把方法參數傳遞進來 */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                //若是別人想要讓小明唱歌
                if (method.getName().equals("sing")) {

                    System.out.println("給1000萬來再唱");

                    //實際上唱歌的仍是小明
                    method.invoke(xiaoMing, args);
                }
                return null;
            }
        });

    }
}

測試類

public static void main(String[] args) {

        //外界經過代理才能讓小明唱歌
        XiaoMingProxy xiaoMingProxy = new XiaoMingProxy();
        Person proxy = xiaoMingProxy.getProxy();


        proxy.sing("我愛你");

    }


動態代理應用

咱們以前寫中文過濾器的時候,須要使用包裝設計模式來設計一個request類。若是不是Servlet提供了實現類給咱們,咱們使用包裝設計模式會出現麻煩

如今咱們學習了動態代理了,動態代理就是攔截直接訪問對象,能夠給對象進行加強的一項技能

中文過濾器

public void doFilter(final ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        final HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        response.setContentType("text/html;charset=UTF-8");
        request.setCharacterEncoding("UTF-8");


        //放出去的是代理對象
        chain.doFilter((ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                //判斷是否是getParameter方法
                if (!method.getName().equals("getParameter")) {

                    //不是就使用request調用
                   return method.invoke(request, args);
                }

                //判斷是不是get類型的
                if (!request.getMethod().equalsIgnoreCase("get")) {
                   return method.invoke(request, args);
                }

                //執行到這裏,只能是get類型的getParameter方法了。
                String value = (String) method.invoke(request, args);
                if (value == null) {
                    return null;
                }
                return new String(value.getBytes("ISO8859-1"), "UTF-8");
            }

        }), response);

    }
相關文章
相關標籤/搜索