1. 程式人生 > >Servlet要點、請求與響應

Servlet要點、請求與響應

一: Servlet要點

01.Servlet規範和搭建JavaWeb專案

什麼是Service:

        Service是JavaEE的元件,service是一個程式類,要求必須實現javax.service.Service介面.

        也是JavaEE中的規範.伺服器其實是Service的實現,

搭建標準的JavaWeb的專案結構:

        1.建立一個java專案:HelloServletWeb:

        2.在HelloServletWeb中建立一個資料夾webapp,表示web專案的根;

        3.在webAPP中建立WEB-INF
資料夾, 4.在WEB-INF中建立資料夾:lib,classes 5.在WEB-INF中去Tomcat根/conf拷貝web.xml檔案,只需要保留根元素. 6.把當前專案的classpath路徑改成webapp/WEB-INF下的classes中.

02.第一個Servlet的程式

Service的第一個程式編寫步驟:

            1):拷貝Tomcat根/lib/servlet-api.jar到專案的WEB-INF/lib目錄中,並做build path.

            2):編寫Servlet程式,使之實現javax.servlet.Servlet介面,並覆蓋介面中的方法.
                public class HelloServlet implement javax.servlet.Servilet(...
實現方法...) 3):發現方法中的引數出現arg0,arg1的情況是因為沒有關聯Servlet的原始碼. 是否關聯原始碼和程式最終的執行沒有關係,只是在開發階段引數美觀,可閱讀原始碼. 4):在service(ServletRequest req, ServletResponse res)方法,列印一句話. 引導:此時HelloServlet類,和Tomcat一點關係都沒有:我們需要告訴Tomcat來幫我們管理HelloServlet類,(Tomcat是Servlet的容器,會負責Servlet物件的宣告)所以,得以配置的形式,告訴Tomcat來幫我們管理自定義的Servlet類. 5
):Servlet配置:在web.xml檔案中的配置,切記<url-pattern>/hello</url-pattern>中的hello要加"/" 6):部署專案並訪問:server.xml中:<Context docBase="D:\Java EE\Servlet\webapp" path="day3"> 訪問:http://ip:port/contextPath/資源名 http://localhost:80/day3/hello

03.Servlet的生命週期方法

Servlet的生命週期:建立物件,初始化操作,執行操作,銷燬操作.

javax.servlet.Servlet介面中的方法:

        String getServletInfo():獲取Servlet的資訊(Servlet的作者,版本,版權相關).

        ServletConfig getServletConfig();獲取Servlet的配置資訊物件.

生命週期方法:在WEB的生命週期中(Tomcat啟動-->Tomcat關閉),Servlet是單例的.

        構造器:在服務端程式第一次被請求的時候,呼叫,只被呼叫一次.

        void init(ServletConfig config);在構造器執行完畢之後,呼叫init方法,也只會執行一次.

        void service(ServletRequest req, ServletResponse resp):每一次請求都會執行該方法.

        void destroy();正常關閉Tomcat才會執行(該方法不一定會被執行,我們沒有必要在其中編寫掃尾的操作).

總結:   構造器--->init方法---> {servlet方法}迴圈 ---->destory方法

注意: 
     1):Servlet類的構造器必須使用public修飾.

     2):Servlet類必須是無引數構造器.

    原因:底層建立Servlet物件:class.newInstance()方式.--->公共的無引數構造器.

    總結:保證Servlet必須有一個公共的無引數構造器,方便Tomcat底層建立Servlet的物件.

04.Servlet的請求流程-圖片版

![Servlet的請求流程圖](https://img-blog.csdn.net/20180214221646461?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VpeGluXzQwMTYxNzA4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

05.Servlet的請求流程-文字版

    1:瀏覽器先發送請求:http://localhost:80/day3/hello

    2:DNS解析域名(忽略)

    3:Tomcat解析請求:/day3/hello.  上下文路徑:/day3   資源的名稱/hello

    4:解析Tomcat根/conf/server.xml檔案,獲取其中所有的<Context/>元素,並找到path屬性為/day3的元素.

        <Content docBase="D:\Java EE\Servlet\webapp" path="day3"/>.

        再讀取該<Content/>元素,再獲取docBase屬性值,該屬性值就是當前訪問的WEB專案的根路徑.

    5:從該web的根路徑/WEB-INF下找到web.xml檔案.

    6:讀取web.xml檔案,獲取所有的<url-patten>元素,並判斷哪一個<url-pattern>的文字內容為:/hello.

        找不到:404錯誤.    找到:GOTO  7.

    7:通過/hello ,找到當前Servlet的全限定名:com._520it._01_hello.HelloServlet

    8:Servlet的例項快取池中去獲取com._520it._01_hello.HelloServlet對應的物件

     Map<String,Servlet> cache = ....;

     Servlet obj = cache.get("com._520it._01_hello.HelloServlet");

     if(obj==null){
          //第一次請求:GOTO 9
     }else{
          //非第一次請求:GOTO 12
     }

    9:使用反射建立Servlet物件.

     Servlet obj = Class.forName("com._520it._01_hello.HelloServlet").newInstance();

    10:把建立的Servlet物件,儲存到Servlet例項快取池中,供下一次請求使用.

     cache.put("com._520it._01_hello.HelloServlet",obj);

    11:容器建立ServletConfig物件,並呼叫init方法,來完成初始化操作.

     obj.init(config);

    12:容器建立ServletRequestServletResponse物件,並呼叫service方法.

     obj.service(req,resp);

    13:在service方法中對,當前請求的客戶端做響應

06.ServletConfig介面獲取初始化引數

ServletConfig介面:表示Servlet的資訊配置物件(web.xml中,當前Servlet的配置資訊).

其中方法:

    String getServletName();獲取當前Servlet的名字,<servlet-name>元素的文字內容.

    ServletContext getServletContext();獲取當前Servlet的指定的引數名獲取初始化引數值.

    Enumeration<String> getInitParameterName();獲取當前Servlet的所有初始化引數的名字.

07.Servlet繼承體深入講解系列

public class Servlet1 extends MyHttpServlet{

    private static final long serialVersionUID = 1L;

    public void service(HttpServletRequest req, HttpServletResponse resp) {

        super.service(req, resp);
        System.out.println("utf");
    }
}
public class MyHttpServlet extends MyGenericServlet{
    private static final long serialVersionUID = 1L;
    //只能處理一般的請求
    public void service(ServletRequest req, ServletResponse resp){
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        service(request,response);//獲取請求方式,GET/POST
    }

    //專門處理Http的請求
    public void service(HttpServletRequest req, HttpServletResponse resp){
        String method = req.getMethod();//獲取請求方式,GET/POST
        if("GET".equals(method)){
            doGet(req,resp);
        }else{
            doPost(req,resp);
        }
    }
    //專門用於處理POST請求
    private void doPost(HttpServletRequest req, HttpServletResponse resp) {
        System.out.println("Servlet1.doPost()");
    }
    //專門用於處理GET請求
    private void doGet(HttpServletRequest req, HttpServletResponse resp) {
        System.out.println("Servlet1.doGet()");
    }
}
abstract public class MyGenericServlet implements Serializable,Servlet,ServletConfig{

    private static final long serialVersionUID = 1L;
    private ServletConfig config = null;

    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    //專門暴露給子類,讓子類編寫自身的初始化程式碼
    public void init(){
        //NO OP
    }
    @Override
    abstract public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException;
    //把ServletConfig物件暴露給子類訪問
    public ServletConfig getServletConfig() {
        return this.config;
    }

    public String getServletInfo() {
        return null;
    }

    public void destroy() {

    }

    public String getServletName() {
        return config.getServletName();
    }

    public ServletContext getServletContext() {
        return config.getServletContext();
    }

    public String getInitParameter(String name) {
        return config.getInitParameter(name);
    }

    public Enumeration<String> getInitParameterNames() {
        return config.getInitParameterNames();
    }
}

8.Servlet繼承體系總結和開發Servlet

![servlet的繼承體系圖](https://img-blog.csdn.net/20180215180723738?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VpeGluXzQwMTYxNzA4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

前端web實際開發的時候只需繼承HttpServlet類,呼叫service()方法,並記得刪除方法中的super.service(req,resp);

9.HttpServletRequest介面和常用方法

    String getInitParameter(String name):獲取初始化引數,初始化引數是開發人員設定的

    String getParameter(String name):獲取使用者輸入的引數,引數是不同的使用者輸入的(通過表單) 

    常用方法:

    01.String getMethod():返回請求方式:如GET/POST  只有表單中method=post才是post

    02.String getRequestURI();返回請求行中的資源名字部分:如/test/index.html

    03.StringBuffer getRequestURL():返回瀏覽器位址列中所有的資訊

    04.String getContextPath():返回當前專案的上下文路徑(<Context/>元素的path屬性值.)

    05.String getRemoteAddr():返回發出請求的客戶機的IP地址

    06.String getHeader(String name):返回指定名稱的請求頭的值

    獲取請求引數的方法:

    01.String getParameter(String name):返回指定引數名字的值

    02.String[] getParameterValues(String name):返回指定名字引數的多個引數值.

    03.Enumeration<String> getParameterNames()返回所有的引數名的Enumeration物件

    04.Map<String,String[]> getParameterMap():返回所有的引數和值所組成的Map物件.

10.處理請求中文亂碼問題

    在Tomcat中,對於POST和GET請求,都預設才有ISO-8859-1的編碼方式.

    而ISO-8850-1不支援中文,所以亂碼.

    解決方案:

        1:按照ISO-8859-1把亂碼恢復成二進位制形式

          byte[] data = username.getBytes("ISO-8859-1");

        2:再把二進位制形式的資料使用UTF-8重新編寫

          String username = new String(data,"UTF-8");

        一個引數需要使用兩行程式碼來轉碼,如果有N個引數,需要轉N次程式碼重複.

    解決方案:

        針對於POST的請求方式: req.setCharacterEncoding("UTF-8");

                            注意:1.只對POST有效. 2:必須放在獲取任意引數之前.

        針對於GET的請求方式:修改Tomcat中的server.xml配置檔案,對GET方式的預設編碼.

                            URIEncoding="ISO-8859-1"改為"UTF-8"

11.HttpServletResponse介面和常用方法

    ServletResponse介面:響應物件.封裝了獲取響應資訊的方法.

    HttpServletResponse介面:ServletResponse的子介面,可以處理HTTP響應的方法.

    常用方法:

            01.OutputStream getOutputStream();獲取位元組輸出流物件.   輸出圖片等

               注意:可以一起輸出多個getOutputStream(),但是不可以跟getWriter()一起使用:

            02.PrintWriter getWriter():獲取字元流輸出物件      輸出英文或者中文等

            03.設定輸出資料的MIME型別:resp.setContentType("text/html");

            04.設定輸出資料的編碼方式:resp.setCharacterEncoding("UTF-8");

            可以將上述3,4兩行程式碼合併成一行程式碼:resp.setContentType("text/html;charset=UTF-8");

            注意:必須先設定MIME型別和編碼,再獲取輸出流,否則沒有效果.

12.網頁版的簡易計算器

靜態網頁版

<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>Insert title here</title>
</head>
<body>
    <form action='/cal' method='post'>
            <input type='number' name='num1' />
            <select name='op'>
                <option>+</option>
                <option>-</option>
                <option>*</option>
                <option>/</option>
            </select>
            <input type='number' name='num2' />
            <input type='submit' value=' = '/>  
            <input type='text' value='' disabled />
    </form>
</body>
</html>

動態的Servlet網頁

public class CalServlet extends HttpServlet{

    private static final long serialVersionUID = 1L;
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        //================================
        //2:接受表單中的資料
        String snum1 = req.getParameter("num1");
        String op = req.getParameter("op");
        String snum2 = req.getParameter("num2");
        String result = "";
        if(haslength(snum1) && haslength(snum2)){
            Integer num1 = Integer.valueOf(snum1);
            Integer num2 = Integer.valueOf(snum2);
            if("+".equals(op)){
                result = num1+num2+"";
            }else if("-".equals(op)){
                result = num1*num2+"";
            }
        }
        //================================
        //1:輸出一個計算器的介面
        out.print("<form action='/cal' method='post'>");
        out.print("<input type='number' name='num1' value='"+snum1+"'/>");
        out.print("<select name='op'>");
        out.print("<option>+</option>");
        out.print("<option>-</option>");
        out.print("<option>*</option>");
        out.print("<option>/</option>");
        out.print("</select>");
        out.print("<input type='number' name='num2' value='"+snum2+"'/>");
        out.print("<input type='submit' value=' = ' />");
        out.print("<input type='text' value='"+result+"' disabled/>");
    }
    private boolean haslength(String str){
        return str!=null && !"".equals(str.trim());
    }
}