1. 程式人生 > >java中Proxy(代理與動態代理)

java中Proxy(代理與動態代理)

一、代理的概念

  動態代理技術是整個java技術中最重要的一個技術,它是學習java框架的基礎,不會動態代理技術,那麼在學習Spring這些框架時是學不明白的。

  動態代理技術就是用來產生一個物件的代理物件的。在開發中為什麼需要為一個物件產生代理物件呢?
  舉一個現實生活中的例子:歌星或者明星都有一個自己的經紀人,這個經紀人就是他們的代理人,當我們需要找明星表演時,不能直接找到該明星,只能是找明星的代理人。比如劉德華在現實生活中非常有名,會唱歌,會跳舞,會拍戲,劉德華在沒有出名之前,我們可以直接找他唱歌,跳舞,拍戲,劉德華出名之後,他乾的第一件事就是找一個經紀人,這個經紀人就是劉德華的代理人(代理),當我們需要找劉德華表演時,不能直接找到劉德華了(劉德華說,你找我代理人商談具體事宜吧!),只能是找劉德華的代理人,因此劉德華這個代理人存在的價值就是攔截我們對劉德華的直接訪問!
  這個現實中的例子和我們在開發中是一樣的,我們在開發中之所以要產生一個物件的代理物件,主要用於攔截對真實業務物件的訪問。那麼代理物件應該具有什麼方法呢?代理物件應該具有和目標物件相同的方法

  所以在這裡明確代理物件的兩個概念:
    1、代理物件存在的價值主要用於攔截對真實業務物件的訪問
    2、代理物件應該具有和目標物件(真實業務物件)相同的方法。劉德華(真實業務物件)會唱歌,會跳舞,會拍戲,我們現在不能直接找他唱歌,跳舞,拍戲了,只能找他的代理人(代理物件)唱歌,跳舞,拍戲,一個人要想成為劉德華的代理人,那麼他必須具有和劉德華一樣的行為(會唱歌,會跳舞,會拍戲),劉德華有什麼方法,他(代理人)就要有什麼方法,我們找劉德華的代理人唱歌,跳舞,拍戲,但是代理人不是真的懂得唱歌,跳舞,拍戲的,真正懂得唱歌,跳舞,拍戲的是劉德華,在現實中的例子就是我們要找劉德華唱歌,跳舞,拍戲,那麼只能先找他的經紀人,交錢給他的經紀人,然後經紀人再讓劉德華去唱歌,跳舞,拍戲。

二、java中的代理

2.1、"java.lang.reflect.Proxy"類介紹

  現在要生成某一個物件的代理物件,這個代理物件通常也要編寫一個類來生成,所以首先要編寫用於生成代理物件的類。在java中如何用程式去生成一個物件的代理物件呢,java在JDK1.5之後提供了一個"java.lang.reflect.Proxy"類,通過"Proxy"類提供的一個newProxyInstance方法用來建立一個物件的代理物件,如下所示:

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

  newProxyInstance方法用來返回一個代理物件,這個方法總共有3個引數,ClassLoader loader用來指明生成代理物件使用哪個類裝載器,Class<?>[] interfaces用來指明生成哪個物件的代理物件,通過介面指定,InvocationHandler h用來指明產生的這個代理物件要做什麼事情。所以我們只需要呼叫newProxyInstance方法就可以得到某一個物件的代理物件了。

2.2、編寫生成代理物件的類

  在java中規定,要想產生一個物件的代理物件,那麼這個物件必須要有一個介面,所以我們第一步就是設計這個物件的介面,在介面中定義這個物件所具有的行為(方法)

  1、定義物件的行為介面

複製程式碼
 1 package cn.gacl.proxy;
 2 
 3 /**
 4 * @ClassName: Person
 5 * @Description: 定義物件的行為
 6 * @author: 孤傲蒼狼
 7 * @date: 2014-9-14 下午9:44:22
 8 *
 9 */ 
10 public interface Person {
11 
12     /**
13     * @Method: sing
14     * @Description: 唱歌
15     * @Anthor:孤傲蒼狼
16     *
17     * @param name
18     * @return
19     */ 
20     String sing(String name);
21     /**
22     * @Method: sing
23     * @Description: 跳舞
24     * @Anthor:孤傲蒼狼
25     *
26     * @param name
27     * @return
28     */ 
29     String dance(String name);
30 }
複製程式碼

  2、定義目標業務物件類

複製程式碼
 1 package cn.gacl.proxy;
 2 
 3 /**
 4 * @ClassName: LiuDeHua
 5 * @Description: 劉德華實現Person介面,那麼劉德華會唱歌和跳舞了
 6 * @author: 孤傲蒼狼
 7 * @date: 2014-9-14 下午9:22:24
 8 *
 9 */ 
10 public class LiuDeHua implements Person {
11 
12     public String sing(String name){
13         System.out.println("劉德華唱"+name+"歌!!");
14         return "歌唱完了,謝謝大家!";
15     }
16     
17     public String dance(String name){
18         System.out.println("劉德華跳"+name+"舞!!");
19         return "舞跳完了,多謝各位觀眾!";
20     }
21 }
複製程式碼

  3、建立生成代理物件的代理類

複製程式碼
 1 package cn.gacl.proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 
 7 /**
 8 * @ClassName: LiuDeHuaProxy
 9 * @Description: 這個代理類負責生成劉德華的代理人
10 * @author: 孤傲蒼狼
11 * @date: 2014-9-14 下午9:50:02
12 *
13 */ 
14 public class LiuDeHuaProxy {
15 
16     //設計一個類變數記住代理類要代理的目標物件
17     private Person ldh = new LiuDeHua();
18     
19     /**
20     * 設計一個方法生成代理物件
21     * @Method: getProxy
22     * @Description: 這個方法返回劉德華的代理物件:Person person = LiuDeHuaProxy.getProxy();//得到一個代理物件
23     * @Anthor:孤傲蒼狼
24     *
25     * @return 某個物件的代理物件
26     */ 
27     public Person getProxy() {
28         //使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)返回某個物件的代理物件
29         return (Person) Proxy.newProxyInstance(LiuDeHuaProxy.class
30                 .getClassLoader(), ldh.getClass().getInterfaces(),
31                 new InvocationHandler() {
32                     /**
33                      * InvocationHandler介面只定義了一個invoke方法,因此對於這樣的介面,我們不用單獨去定義一個類來實現該介面,
34                      * 而是直接使用一個匿名內部類來實現該介面,new InvocationHandler() {}就是針對InvocationHandler介面的匿名實現類
35                      */
36                     /**
37                      * 在invoke方法編碼指定返回的代理物件乾的工作
38                      * proxy : 把代理物件自己傳遞進來 
39                      * method:把代理物件當前呼叫的方法傳遞進來 
40                      * args:把方法引數傳遞進來
41                      * 
42                      * 當呼叫代理物件的person.sing("冰雨");或者 person.dance("江南style");方法時,
43                      * 實際上執行的都是invoke方法裡面的程式碼,
44                      * 因此我們可以在invoke方法中使用method.getName()就可以知道當前呼叫的是代理物件的哪個方法
45                      */
46                     @Override
47                     public Object invoke(Object proxy, Method method,
48                             Object[] args) throws Throwable {
49                         //如果呼叫的是代理物件的sing方法
50                         if (method.getName().equals("sing")) {
51                             System.out.println("我是他的經紀人,要找他唱歌得先給十萬塊錢!!");
52                             //已經給錢了,經紀人自己不會唱歌,就只能找劉德華去唱歌!
53                             return method.invoke(ldh, args); //代理物件呼叫真實目標物件的sing方法去處理使用者請求
54                         }
55                         //如果呼叫的是代理物件的dance方法
56                         if (method.getName().equals("dance")) {
57                             System.out.println("我是他的經紀人,要找他跳舞得先給二十萬塊錢!!");
58                             //已經給錢了,經紀人自己不會唱歌,就只能找劉德華去跳舞!
59                             return method.invoke(ldh, args);//代理物件呼叫真實目標物件的dance方法去處理使用者請求
60                         }
61 
62                         return null;
63                     }
64                 });
65     }
66 }
複製程式碼

  測試程式碼:

複製程式碼
 1 package cn.gacl.proxy;
 2 
 3 public class ProxyTest {
 4     
 5     public static void main(String[] args) {
 6         
 7         LiuDeHuaProxy proxy = new LiuDeHuaProxy();
 8         //獲得代理物件
 9         Person p = proxy.getProxy();
10         //呼叫代理物件的sing方法
11         String retValue = p.sing("冰雨");
12         System.out.println(retValue);
13         //呼叫代理物件的dance方法
14         String value = p.dance("江南style");
15         System.out.println(value);
16     }
17 }
複製程式碼

  執行結果如下:

  
  Proxy類負責建立代理物件時,如果指定了handler(處理器),那麼不管使用者呼叫代理物件的什麼方法,該方法都是呼叫處理器的invoke方法。
  由於invoke方法被呼叫需要三個引數:代理物件、方法、方法的引數,因此不管代理物件哪個方法呼叫處理器的invoke方法,都必須把自己所在的物件、自己(呼叫invoke方法的方法)、方法的引數傳遞進來。

三、動態代理應用

  在動態代理技術裡,由於不管使用者呼叫代理物件的什麼方法,都是呼叫開發人員編寫的處理器的invoke方法(這相當於invoke方法攔截到了代理物件的方法呼叫)。並且,開發人員通過invoke方法的引數,還可以在攔截的同時,知道使用者呼叫的是什麼方法,因此利用這兩個特性,就可以實現一些特殊需求,例如:攔截使用者的訪問請求,以檢查使用者是否有訪問許可權、動態為某個物件新增額外的功能。

3.1、在字元過濾器中使用動態代理解決中文亂碼

  在平時的JavaWeb專案開發中,我們一般會寫一個CharacterEncodingFilter(字元過濾器)來解決整個JavaWeb應用的中文亂碼問題,如下所示:

複製程式碼
 1 package me.gacl.web.filter;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.Filter;
 6 import javax.servlet.FilterChain;
 7 import javax.servlet.FilterConfig;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 
12 /**
13 * @ClassName: CharacterEncodingFilter
14 * @Description: 解決中文亂碼的字元過濾器
15 * @author: 孤傲蒼狼
16 * @date: 2014-9-14 下午10:38:12
17 *
18 */ 
19 public class CharacterEncodingFilter implements Filter {
20 
21     @Override
22     public void init(FilterConfig filterConfig) throws ServletException {
23 
24     }
25 
26     @Override
27     public void doFilter(ServletRequest request, ServletResponse response,
28             FilterChain chain) throws IOException, ServletException {
29         //解決以Post方式提交的中文亂碼問題
30         request.setCharacterEncoding("UTF-8");
31         response.setCharacterEncoding("UTF-8");
32         response.setContentType("text/html;charset=UTF-8");
33         chain.doFilter(request, response);
34     }
35 
36     @Override
37     public void destroy() {
38 
39     }
40 }
複製程式碼

  但是這種寫法是沒有辦法解決以get方式提交中文引數時的亂碼問題的,我們可以用如下的程式碼來證明上述的解決中文亂碼過濾器只對以post方式提交中文引數時有效,而對於以get方式提交中文引數時無效

  jsp測試頁面如下:

複製程式碼
 1 <%@ page language="java" pageEncoding="UTF-8"%>
 2 <%--引入jstl標籤庫 --%>
 3 <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
 4 <!DOCTYPE HTML>
 5 <html>
 6   <head>
 7     <title>使用字元過濾器解決解決get、post請求方式下的中文亂碼問題</title>
 8   </head>
 9   <body>
10        <%--使用c:url標籤構建url,構建好的url儲存在servletDemo1變數中--%>
11        <c:url value="/servlet/ServletDemo1" scope="page" var="servletDemo1">
12            <%--構建的url的附帶的中文引數 ,引數名是:username,值是:孤傲蒼狼--%>
13            <c:param name="username" value="孤傲蒼狼"></c:param>
14        </c:url>
15       <%--使用get的方式訪問 --%>
16        <a href="${servletDemo1}">超連結(get方式請求)</a>
17        <hr/>
18        <%--使用post方式提交表單 --%>
19        <form action="${pageContext.request.contextPath}/servlet/ServletDemo1" method="post">
20            使用者名稱:<input type="text" name="username" value="孤傲蒼狼" />
21            <input type="submit" value="post方式提交">
22        </form>
23        
24   </body>
25 </html>
複製程式碼

  處理請求的ServletDemo1程式碼如下:

複製程式碼
 1 package me.gacl.web.controller;
 2 
 3 import java.io.IOException;
 4 import java.io.PrintWriter;
 5 
 6 import javax.servlet.ServletException;
 7 import javax.servlet.http.HttpServlet;
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 
11 public class ServletDemo1 extends HttpServlet {
12 
13     public void doGet(HttpServletRequest request, HttpServletResponse response)
14             throws ServletException, IOException {
15         // 接收引數
16         String username = request.getParameter("username");
17         // 獲取請求方式
18         String method = request.getMethod();
19         // 獲取輸出流
20         PrintWriter out = response.getWriter();
21         out.write("請求的方式:" + method);
22         out.write("<br/>");
23         out.write("接收到的引數:" + username);
24     }
25 
26     public void doPost(HttpServletRequest request, HttpServletResponse response)
27             throws ServletException, IOException {
28         doGet(request, response);
29     }
30 }
複製程式碼

  在web.xml中註冊上述的CharacterEncodingFilter和ServletDemo1

複製程式碼
 1  <filter>
 2       <filter-name>CharacterEncodingFilter</filter-name>
 3       <filter-class>me.gacl.web.filter.CharacterEncodingFilter</filter-class>
 4   </filter>
 5   
 6   <filter-mapping>
 7       <filter-name>CharacterEncodingFilter</
            
           

相關推薦

javaProxy(代理動態代理)

一、代理的概念  動態代理技術是整個java技術中最重要的一個技術,它是學習java框架的基礎,不會動態代理技術,那麼在學習Spring這些框架時是學不明白的。  動態代理技術就是用來產生一個物件的代理物件的。在開發中為什麼需要為一個物件產生代理物件呢?  舉一個現實生活中的

Java 的靜態代理動態代理

什麼是代理模式 人話來講就是由代理物件來執行目標物件的方法,且還可以在代理物件中增強目標物件方法的一種設計模式。類比生活,像是房產中介。代理模式存在的意義和一個架構設計原則息息相關 —— 開閉原則(對擴充套件開放,對修改關閉),即一種好的設計模式,都是在不修改原有形態的基礎上擴展出新

Java靜態代理動態代理模式的實現

loader 兩個 如何 圖片 ide 相同 catch 規範 png 前言: 在現實生活中,考慮以下的場景:小王打算要去租房,他相中了一個房子,準備去找房東洽談相關事宜。但是房東他很忙,平時上班沒時間,總沒有時間見面,他也沒辦法。後來,房東想了一個辦法,他找到了一個

JAVA框架學習——基礎準備(log4j,靜態代理動態代理,列舉,註解)

一、log4j。 1.log4j基礎科普:記錄日誌。 有兩種日誌模式: a.Apatcha提供:Log4j(MyBatis使用這種)和Log4j2(Hibernate使用這個) b.JDK自帶,由於自帶不好用所以用上一個

25(java的反射和動態代理

1 概述 反射獲取的都是class物件,以下是在不同的階段獲取物件的方式。 2 原始檔階段class物件的作用 可以利用全類名創造物件,具體程式碼為: 3 class物件獲取類中的欄位(即成員變數) 註釋:通過Class.forName()獲取到了Pe

JAVA代理動態代理(轉載)

關於Java中的動態代理,我們首先需要了解的是一種常用的設計模式--代理模式,而對於代理,根據建立代理類的時間點,又可以分為靜態代理和動態代理。  一、代理模式 代理模式是常用的java設計模式,他的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊

java靜態代理動態代理理解

#1.靜態代理 其實就是一個典型的代理模式實現,在代理類中包裝一個被代理物件,然後影響被代理物件的行為 程式碼示例: // 介面 public interface Hello { public void sayHello(String name); } // 實現類 @Slf4

JAVA的JDK的動態代理

在java的動態代理機制中,有兩個重要的類或介面,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class) 當我們通過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行呼叫。

秒懂Java代理動態代理模式

概述 什麼是代理模式?解決什麼問題(即為什麼需要)?什麼是靜態代理?什麼是動態代理模式?二者什麼關係?具體如何實現?什麼原理?如何改進?這即為我們學習一項新知識的正確開啟方式,我們接下來會以此展開,讓你秒懂。 概念 什麼是代理模式

java靜態代理動態代理用法

代理模式是指通過代理物件來訪問目標物件,這樣便於在目標物件的功能基礎之上新增額外的功能擴充套件,並且不需要改變原目標物件的程式碼。 1、靜態代理 靜態代理比較簡單,代理類與目標類實現同一個介面,把目標類的物件新增進代理類中,然後在代理類的方法中呼叫目標類的方法,程式碼如下: 介面

java 代理模式-靜態代理動態代理

最近在研究SpringAOP,當然要學習AOP就要知道這麼健碩、強大的功能的背後究竟隱藏著怎樣不可告人的“祕密”?? 接下來就是查閱了許多資料詳細的研究了一下Java的代理模式,感覺還是非常非常重要的, 我們作為一個有“內涵的”程式設計師就更應該掌握啦!(本文需要細心、帶有審視的目光來甄別其中的內容) 在學

代理模式-靜態代理動態代理

應用 代碼 creat HR print tps 結果 inter OS 簡介 首先感謝沽泡學院 tom 老師 代理模式是一種結構型模式 代理模式就是代理對象幫被代理對象處理一些問題, 類似中介, 客戶只要結果, 中介怎麽搞是他的事兒, 他可能再處理過程中賺外快什麽的 代

靜態代理動態代理模式

png 代理 before ring program 直接 factor 設計模式 messaging 代理(Proxy)設計模式簡介: 提供了對目標對象另外的訪問方式;即通過代理對象訪問目標對象.這樣做的好處是:可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標

Android小知識-剖析Retrofit前的預備知識(靜態代理動態代理

本平臺的文章更新會有延遲,大家可以關注微信公眾號-顧林海,包括年底前會更新kotlin由淺入深系列教程,目前計劃在微信公眾號進行首發,如果大家想獲取最新教程,請關注微信公眾號,謝謝! 代理設計模式主要分為靜態代理與動態代理,代理模式的定義是為其他物件提供一種代理,用以控制對這個物件的訪問。打個比方

靜態代理動態代理

Proxy代理模式是一種結構型設計模式,主要解決的問題是:在直接訪問物件時帶來的問題 代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。 比如

AOP靜態代理動態代理

AOP面相切面程式設計,是作為面向物件的一種補充,用於處理系統中分佈於各個模組的橫切關注點,比如事務管理,日誌,快取,異常等。AOP代理分類靜態代理和動態代理靜態代理靜態代理就是AOP框架會在編譯階段將Aspect植入java位元組碼中,生成AOP代理類,在執行的時候,是直接

淺談java內置的觀察者模式動態代理的實現

所有 代理 notify play ani effect 一個 indicate protected 一.關於觀察者模式 1.將觀察者與被觀察者分離開來,當被觀察者發生變化時,將通知所有觀察者,觀察者會根據這些變化做出對應的處理。 2.jdk裏已經提供對應的Observer

反射包 java.lang.reflect⑦ 之 Java Proxy 動態代理類 探祕(三)

這個系列好久沒有續作了,你以為完了,錯了。這個動態代理有說不完的知識點,我也是在不斷的學習中才瞭解得到更多的知識。但無可否認的一點是它需更多其他的知識的支援,比如設計模式,設計思想。工作越久越覺得設計模式這個東西的重要性。  其實動態代理的前兩個例子只是簡單的列出了它的一個

輕鬆學,Java 代理模式(proxy)及動態代理

我們先來分析代理這個詞。 代理 代理是英文 Proxy 翻譯過來的。我們在生活中見到過的代理,大概最常見的就是朋友圈中賣面膜的同學了。 她們從廠家拿貨,然後在朋友圈中宣傳,然後賣給熟人。 按理說,顧客可以直接從廠家購買產品,但是現實生活中,很少有