動態代理(Dynamic Proxy)Java Servlet

來源:互聯網
上載者:User
              從JDK1.3開始,Java就引入了動態代理的概念。動態代理(Dynamic Proxy)可以協助你減少程式碼數,真正提高代碼的可複用度。例如,你不必為所有的類的方法裡面都寫上相同的Log程式碼,取而代之的是實用類的動態代理類。當然,這種便利是有條件的。本文簡單介紹Java動態代理的原理,並實現一個被代理的Servlet建立,和調用的過程。 1.代理模式(Proxy Pattern)在JDK1.3以前,代理模式就已流行,所以得代理模式是產生一個和類相同介面的代理類,使用者通過使用代理類來封裝某個實作類別。1,其目的是加強實作類別的某個方法的功能,而不必改變原有的原始碼。

2.動態代理(Dynamic Proxy)隨著Proxy的流行,Sun把它納入到JDK1.3實現了Java的動態代理。動態代理和普通的代理模式的區別,就是動態代理中的代理類是由java.lang.reflect.Proxy類在運行期時根據介面定義,採用Java反射功能動態產生的。和java.lang.reflect.InvocationHandler結合,可以加強現有類的方法實現。2,圖中的自訂Handler實現InvocationHandler介面,自訂Handler執行個體化時,將實作類別傳入自訂Handler對象。自訂Handler需要實現invoke方法,該方法可以使用Java反射調用實作類別的實現的方法,同時當然可以實現其他功能,例如在調用實作類別方法前後加入Log。而Proxy類根據Handler和需要代理的介面動態產生一個介面實作類別的對象。當使用者調用這個動態產生的實作類別時,實際上是調用了自訂Handler的invoke方法。  


3.動態代理Servlet              雖然Web Application Server的產品很多,但Servlet的處理原理是相似的:動態載入Servlet,調用Servlet的init方法(只被調用一次),並儲存到Servlet容器;Servlet使用時,調用Servlet的service方法。本文動態代理Servlet介面,使其init和service被調用時會在控制台打出方法調用前後資訊。首先實現2個Servlet,DefaultServlet和UserServlet package org.colimas.servlet; import javax.servlet.Servlet;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet; public class DefaultServlet extends HttpServlet implements Servlet {              public void init() throws ServletException {                            super.init();                            System.out.println(DefaultServlet.class.getName()+":Running init");              }               public String getServletInfo() {                            return DefaultServlet.class.getName();              }} package org.colimas.servlet; import java.io.IOException; import javax.servlet.Servlet;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;  public class UserServlet extends HttpServlet implements Servlet {               private static final long serialVersionUID = -7016554795165038652L;                            public void init() throws ServletException {                            super.init();                            System.out.println(UserServlet.class.getName()+":Running init");              }              protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {                                                                      System.out.println(UserServlet.class.getName()+":Do UserSErvlet Get");              }              public String getServletInfo() {                            return UserServlet.class.getName();              }                            } 然後實現InvocationHandlerpackage org.colimas.webapp; import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method; import javax.servlet.Servlet; public class ServletHandler implements InvocationHandler {               private Servlet obj;                            public ServletHandler(Servlet obj){                            this.obj=obj;              }              public Object invoke(Object arg0, Method arg1, Object[] arg2)                                          throws Throwable {                                                        if(arg1.getName().compareTo("init")==0) //調用init時                            {                                          System.out.println(obj.getServletInfo()+":Init servlet starting..."); //增加控制台輸出。                                          arg1.invoke(obj,arg2); //調用init方法                                          System.out.println(obj.getServletInfo()+":Init servlet ending..."); //增加控制台輸出。                            }else if(arg1.getName().compareTo("service")==0){ //調用service時                                          System.out.println(obj.getServletInfo()+":service starting..."); //增加控制台輸出。                                           arg1.invoke(obj,arg2); //調用service方法。                                          System.out.println(obj.getServletInfo()+":service ending..."); //增加控制台輸出。                                                        }                            return null;              } } 實現Servlet的調用package org.colimas.webapp; import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy; import javax.servlet.Servlet;import javax.servlet.ServletConfig;import javax.servlet.ServletContext;import javax.servlet.ServletException; public class ServletWrapperImp {        private Class servletClass;    private ServletConfig config;    private String _servletname;    private Servlet _theServlet;              private ServletContext context;    public ServletWrapperImp(ServletConfig config){           this.config=config;           this._servletname=this.config.getServletName();           this.context=this.config.getServletContext();    }        public Servlet getServlet() throws ServletException{                               destroy();                              try {                                            WebAppClassLoader loader=new WebAppClassLoader(this.getClass().getClassLoader()); //自訂class loader                                            String name=getServletName(); //從ServletConfig中獲得Servlet Name                                            synchronized (context) {                                                          Servlet theServlet=context.getServlet(name); //在ServletContext中尋找Servlet                                                          if(theServlet==null){         //如果ServletContext沒有。                                                                                       servletClass = loader.loadClass(name); //由Class loader 載入Servlet class。                                                              theServlet = (Servlet) servletClass.newInstance(); //Servlet執行個體化。                                                              WebAppContext.addServlet(name,theServlet); //將Servlet執行個體存入ServletContext。                                                                        InvocationHandler handler=new ServletHandler(theServlet); //自訂ServletHandler,參見ServletHandler類。                                                                        _theServlet=(Servlet)Proxy.newProxyInstance(theServlet.getClass().getClassLoader(),                                                                                   new Class[]{Servlet.class},handler); //代理類執行個體化。                                                                        _theServlet.init(config); //Servlet代理對象調用init方法。參見ServletHandler的invoke方法。                                                                                                                                  }else{ //ServletContext裡已存在。                                                                        InvocationHandler handler=new ServletHandler(theServlet); //自訂ServletHandler,參見ServletHandler類。                                                                        _theServlet=(Servlet)Proxy.newProxyInstance(theServlet.getClass().getClassLoader(),                                                                                   new Class[]{Servlet.class},handler);                 //代理Servlet介面,動態組建代理程式類,並執行個體化。                                                                                             }                                            }                                            return _theServlet; //返回Servlet代理對象                              } catch( ClassNotFoundException ex1 ) {                                                               } catch( InstantiationException ex ) {                                                                }catch(IllegalAccessException ex2){                                                                          }                      return null;    }    public void destroy() {        if (_theServlet != null) {                      _theServlet.destroy();        }    }            protected String getServletName(){           return _servletname;    }} 其中的ServletConfig儲存Servlet相關資訊。ServletContext儲存所有的Servlet對象。WebAppClassLoader為自訂class loader,參見http://blog.csdn.net/tyrone1979/archive/2006/09/03/1164262.aspx。 最後編寫測試類別Main,該類類比10個使用者訪問Servlet,5人訪問DefaultServlet,5人訪問UserServlet。package org.colimas.main; import java.io.IOException; import javax.servlet.Servlet;import javax.servlet.ServletConfig;import javax.servlet.ServletException; import org.colimas.webapp.HttpServletRequestWrapper;import org.colimas.webapp.HttpServletResponseWrapper;import org.colimas.webapp.ServletConfigImpl;import org.colimas.webapp.ServletWrapper;import org.colimas.webapp.ServletWrapperImp;import org.colimas.webapp.WebAppContext; public class Main {               private ThreadGroup _threadGroup;              private Thread[] _threads;               String defaultServletName="org.colimas.servlet.DefaultServlet";              String userServletName="org.colimas.servlet.UserServlet";              WebAppContext context=WebAppContext.newInstance();                                  public void doStart(){                                          _threadGroup=new ThreadGroup("SERVLETS");                            int i=0;                            _threads=new ServletThread[10]; //類比10位使用者。                            for(i=0;i<5;i++){                                          _threads[i]=new ServletThread(_threadGroup,new Integer(i).toString(),                                                                      defaultServletName);                                           _threads[i].start();                            }                            for(i=5;i<10;i++){                                          _threads[i]=new ServletThread(_threadGroup,new Integer(i).toString(),                                                                      userServletName);                                          _threads[i].start();                            }                                                                     }              /**               * @param args               */              public static void main(String[] args) {                            Main _main=new Main();                            _main.doStart();                                          }              //類比使用者線程              private class ServletThread extends Thread{                                                        private String servletName;                                                        public ServletThread(ThreadGroup group,String threadname,String servletname){                                          super(group,threadname);                                          servletName=servletname;                            }                            //調用Servlet的service.                            public void run() { //使用者調用Servlet                                          ServletConfig config=new ServletConfigImpl(servletName,context); //調用的Servlet資訊。                                          ServletWrapperImp wrapper=new ServletWrapperImp(config);                                           try {                                                        Servlet defaultServlet=wrapper.getServlet(); //獲得Servlet對象,實際是Servlet的代理對象                                                        defaultServlet.service(new HttpServletRequestWrapper(),                                                                                     new HttpServletResponseWrapper()); 調用代理對象的service方法,參見ServletHandler的invoke方法。                                          } catch (ServletException e) {                                                                                                                e.printStackTrace();                                          } catch(IOException e){                                                                                                  }                            }                                          }}HttpServletRequestWrapper和HttpServletResponseWrapper實現HttpServletRequest,和HttpServletResponse。測試結果如下org.colimas.servlet.DefaultServlet:Init servlet starting...org.colimas.servlet.DefaultServlet:Running initorg.colimas.servlet.DefaultServlet:Init servlet ending...org.colimas.servlet.UserServlet:Init servlet starting...org.colimas.servlet.UserServlet:Running initorg.colimas.servlet.UserServlet:Init servlet ending...org.colimas.servlet.DefaultServlet:service starting...org.colimas.servlet.DefaultServlet:service ending...org.colimas.servlet.DefaultServlet:service starting...org.colimas.servlet.DefaultServlet:service ending...org.colimas.servlet.UserServlet:service starting...org.colimas.servlet.UserServlet:Do UserSErvlet Getorg.colimas.servlet.UserServlet:service ending...org.colimas.servlet.UserServlet:service starting...org.colimas.servlet.UserServlet:Do UserSErvlet Getorg.colimas.servlet.UserServlet:service ending...org.colimas.servlet.UserServlet:service starting...org.colimas.servlet.UserServlet:Do UserSErvlet Getorg.colimas.servlet.UserServlet:service ending...org.colimas.servlet.UserServlet:service starting...org.colimas.servlet.DefaultServlet:service starting...org.colimas.servlet.UserServlet:service starting...org.colimas.servlet.DefaultServlet:service starting...org.colimas.servlet.DefaultServlet:service starting...org.colimas.servlet.UserServlet:Do UserSErvlet Getorg.colimas.servlet.DefaultServlet:service ending...org.colimas.servlet.UserServlet:Do UserSErvlet Getorg.colimas.servlet.DefaultServlet:service ending...org.colimas.servlet.DefaultServlet:service ending...org.colimas.servlet.UserServlet:service ending...org.colimas.servlet.UserServlet:service ending... 2個Servlet第一次Load時初始化,被調用init,之後儲存到ServletContext中。第二次直接從ServletContext獲得,執行service。紅字表示代理類裡增加的輸出結果。 4.動態代理的限制              JDK的動態代理並不能隨心所欲的代理所有的類。Proxy.newProxyInstance方法的第二個參數只能是介面數組, 也就是Proxy只能代理介面。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.