Servlet

Servlet概述

什麼是Servlet

    就是一個運行在WEB服務器上的小的Java程序,用來接收和響應從客戶端發送過來的請求,一般使用HTTP協議。
    Servlet就是SUN公司提供的一個動態網頁開發技術。html

Servlet的做用

    用來處理從客戶端瀏覽器發送的請求,而且能夠對請求做出響應.web

使用Servlet

    編寫一個類實現Servlet接口。
    將編寫的這個類配置到服務器中。瀏覽器

編寫類:
public class ServletDemo1 implements Servlet{

    @Override
    /**
     * 爲用戶處理請求和響應的方法.
     */
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        res.getWriter().println("Hello Servlet...");
    }
...
}

配置:
  <!-- 配置Servlet -->
  <servlet>
    <!-- Servlet的名稱 -->
    <servlet-name>test1</servlet-name>
    <!-- SErvlet的全路徑 -->
    <servlet-class>com.cnblogs.a_servlet.ServletDemo1</servlet-class>
  </servlet>
  
  <!-- Servlet的映射 -->
  <servlet-mapping>
    <!-- Servlet的名稱 -->
    <servlet-name>test1</servlet-name>
    <!-- Servlet的訪問路徑 -->
    <url-pattern>/ServletDemo1</url-pattern>
  </servlet-mapping>

訪問:http://localhost:8080/day09/ServletDemo1
servlet簡單編寫

使用ServletRequest接收參數

String getParameter(String name);         ---用於接收一個名稱對應一個值的數據.
String[] getParameterValues(String name); ---用於接收一個名稱對應多個值的數據.
Map getParameterMap();                    ---用於接收表單中的全部的數據,Map的key是表單提交的參數名稱,Map的value是提交參數的值.
Enumeration getParameterNames()           ---用於獲取表單中提交的全部的參數的名稱.tomcat

Servlet的訪問流程

一、http://localhost:8080/ 找到對應地址的 tomcat,tomcat 解析 url 中的 day09 就找到 部署的 day09 應用。
二、找到 day09 以後,就能夠找到 day09/WEB-INF/web.xml。
三、在web.xml 中 tomcat 找 servlet-mapping下的 url-pattern,把/ServletDemo1 和 url-patten 的內容比較。
四、找到對應的 url-patten,就能夠找到 servlet-name 標籤,讀取 servlet-name 裏面的內容。再依照 servlet-name 尋找 servlet 標籤對應的 servlet-calss。
五、tomcat 實例化 servlet-class 路徑的對象。而後調用改 servlet 對象的 service 方法。安全

Servlet的實現的關係

Servlet:接口
   |
GenericServlet:通用的Servlet
   |
HttpServlet:HttpServlet
  編寫一個類繼承HttpServlet,重寫doGet和doPost方法.
  配置服務器

 

Servlet 的運行過程

Servlet 程序是由 WEB 服務器調用,web 服務器收到客戶端的 Servlet 訪問請求後:
  ① Web 服務器首先檢查是否已經裝載並建立了該 Servlet 的實例對象。若是是,則直接執行第④步,不然,執行第②步。
  ② 裝載並建立該 Servlet 的一個實例對象。 
  ③ 調用 Servlet 實例對象的 init() 方法。
  ④ 建立一個用於封裝 HTTP 請求消息的 HttpServletRequest 對象和一個表明 HTTP 響應消息的 HttpServletResponse 對象,而後調用 Servlet 的 service() 方法並將請求和響應對象做爲參數傳遞進去。
  ⑤ WEB 應用程序被中止或從新啓動以前,Servlet 引擎將卸載 Servlet,並在卸載以前調用 Servlet 的 destroy() 方法。多線程

 

servlet 的生命週期

生命週期:就是一個對象從建立到銷燬的過程。
Servlet 生命週期:Servlet 從建立到銷燬的過程。
  什麼時候建立:用戶第一次訪問 Servlet 建立 Servlet 的實例。
  什麼時候銷燬:當項目從服務器中移除的時候,或者關閉服務器的時候。
用戶第一次訪問 Servlet 的時候,服務器會建立一個 Servlet 的實例,那麼 Servlet 中 init 方法就會執行。任何一次請求服務器都會建立一個新的線程訪問 Servlet 中的 service 的方法。在 service 方法內部根據請求的方式的不一樣調用doXXX的方法 。(get 請求調用 doGe,post 請求調用 doPost)。當 Servlet 中服務器中移除掉,或者關閉服務器,Servlet 的實例就會被銷燬,那麼 destroy 方法就會執行。併發

 

Servlet 的相關的配置

 3.1 啓動時建立 Servlet

Servlet 默認是在第一次訪問的時候建立的。如今讓 Servlet 在服務器啓動的時候建立好,對 Servlet 進行配置,
在 web.xml 中在 <servlet></servlet> 標籤中配置:
<load-on-startup>2</load-on-startup>  --- 傳入正整數,整數越小,被建立的優先級就越高。app

 

3.3 url-pattern 的配置

url-pattern 配置方式共有三種:ide

徹底路徑匹配 :以 / 開始。                      例如:    /ServletDemo4 , /aaa/ServletDemo5 , /aaa/bbb/ServletDemo6
目錄匹配:以 / 開始 須要以 * 結束。     例如: /* ,/aaa/* ,/aaa/bbb/*
擴展名匹配 :不能以 / 開始 以 * 開始的。  例如: *.do , *.action 

徹底路徑匹配 > 目錄匹配 > 擴展名匹配

 

3.5 ServletContext 對象

Servlet 對象繼承 HttpServlet/GenericServlet,能夠用 this.getServletContext() 得到 ServletContext 對象。它可做爲域對象,用來存儲數據。例:this.getServletContext().setAttribute("count", count); ServletConfig 對象中維護了 ServletContext 對象的引用,也能夠這樣 this.getServletConfig().getServletContext().setAttribute();

void setAttribute(String name, Object object)  --向ServletContext中存入數據
Object getAttribute(String name)  --從ServletContext中獲取數據
void removeAttribute(String name)  --從ServletContext中移除數據

3.6 ServletContext 的做用

1.用來得到全局初始化參數。
2.用來得到文件的MIME的類型。
3.做爲域對象存取數據。
  ServletContext 是一個域對象。
    * 做用範圍:整個 web 工程。
    * 建立:服務器啓動的時候,tomcat 服務器爲每一個 web 項目建立一個單獨 ServletContext 對象。
    * 銷燬:服務器關閉的時候,或者項目從服務器中移除的時候。
4.用來讀取 web 項目下的文件。

/**
 * 登陸代碼的Servlet
 */
public class UserCountServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    public void init() throws ServletException {
        // 初始化一個變量count的值爲0.
        int count = 0;
        // 將這個值存入到ServletContext中.
        this.getServletContext().setAttribute("count", count);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            response.setContentType("text/html;charset=UTF-8");
            // 1.接收表單提交的參數.
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            // 2.封裝到實體對象中.
            User user = new User();
            user.setUsername(username);
            user.setPassword(password);
            
            // 3.調用業務層處理數據.
            UserService userService = new UserService();
        
            User existUser = userService.login(user);
            // 4.根據處理結果顯示信息(頁面跳轉).
            if(existUser == null){
                // 登陸失敗
                response.getWriter().println("<h1>登陸失敗:用戶名或密碼錯誤!</h1>");
            }else{
                // 登陸成功
                
                // 記錄次數:
                int count = (int) this.getServletContext().getAttribute("count");
                count++;
                this.getServletContext().setAttribute("count", count);
                
                response.getWriter().println("<h1>登陸成功:您好:"+existUser.getNickname()+"</h1>");
                response.getWriter().println("<h3>頁面將在5秒後跳轉!</h3>");
                response.setHeader("Refresh", "5;url=/day09/CountServlet");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}


public class CountServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 得到Count的值。
        response.setContentType("text/html;charset=UTF-8");
        int count = (int) this.getServletContext().getAttribute("count");
        response.getWriter().println("<h1>您是第"+count+"位登陸成功的用戶!</h1>");
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
servletContext記錄訪問人次

 

ServletContext 對象讀取 WEB 項目文件

ServletContext context = this.getServletContext();
InputStream is = context. getResourceAsStream("/WEB-INF/classes/db.properties");

ServletContext context = this.getServletContext();
String realPath = context.getRealPath("/WEB-INF/classes/db.properties");
InputStream is = new FileInputStream(realPath);

    /**
     * 使用ServletContext中的getResourceAsStream讀取.
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void test2() throws FileNotFoundException, IOException {
    
        // 得到ServletContext:
        ServletContext context = this.getServletContext();
        InputStream is = context. getResourceAsStream("/WEB-INF/classes/db.properties");
        Properties properties = new Properties();
        properties.load(is);
        
        String driverClass = properties.getProperty("driverClass");
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        
        System.out.println(driverClass);
        System.out.println(url);
        System.out.println(username);
        System.out.println(password);
    }

    /**
     * 使用ServletContext中的getRealPath讀取.
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void test3() throws FileNotFoundException, IOException {
        // 得到ServletContext:
        ServletContext context = this.getServletContext();
        String realPath = context.getRealPath("/WEB-INF/classes/db.properties");
        // 得到該文件的磁盤絕對路徑.
        System.out.println(realPath);
        InputStream is = new FileInputStream(realPath);
        
        Properties properties = new Properties();
        properties.load(is);
        
        String driverClass = properties.getProperty("driverClass");
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        
        System.out.println(driverClass);
        System.out.println(url);
        System.out.println(username);
        System.out.println(password);
    }
servletContext讀取web文件

類加載器讀取文件:
InputStream is = ReadFileUtils.class.getClassLoader().getResourceAsStream("db.properties");

 

Servlet 的線程安全問題 

一種作法是給 Servlet 對象加了一把鎖,這種作法雖然解決了線程安全問題,可是必須按前後順序排隊輪流訪問,效率極低,不可能使用。

一種是讓 Servlet 去實現一個 SingleThreadModel 接口,若是某個 Servlet 實現了 SingleThreadModel 接口,那麼 Servlet 引擎將以單線程模式來調用其 service 方法。
  查看 Sevlet 的 API 能夠看到,SingleThreadModel 接口中沒有定義任何方法和常量,在 Java 中,把沒有定義任何方法和常量的接口稱之爲標記接口,常常看到的一個最典型的標記接口就是"Serializable",這個接口也是沒有定義任何方法和常量的,標記接口在 Java 中有什麼用呢?主要做用就是給某個對象打上一個標誌,告訴 JVM,這個對象能夠作什麼,好比實現了"Serializable"接口的類的對象就能夠被序列化,還有一個"Cloneable"接口,這個也是一個標記接口,在默認狀況下,Java 中的對象是不容許被克隆的,就像現實生活中的人同樣,不容許克隆,可是隻要實現了"Cloneable"接口,那麼對象就能夠被克隆了。

  讓 Servlet 實現了 SingleThreadModel 接口,只要在 Servlet 類的定義中增長實現 SingleThreadModel 接口的聲明便可。  
  對於實現了 SingleThreadModel 接口的 Servlet,Servlet 引擎仍然支持對該 Servlet 的多線程併發訪問,其採用的方式是產生多個 Servlet 實例對象,併發的每一個線程分別調用一個獨立的 Servlet 實例對象。
  實現 SingleThreadModel 接口並不能真正解決 Servlet 的線程安全問題,由於 Servlet 引擎會建立多個 Servlet 實例對象,而真正意義上解決多線程安全問題是指一個 Servlet 實例對象被多個線程同時調用的問題。事實上,在 Servlet API 2.4 中,已經將 SingleThreadModel 標記爲 Deprecated(過期的)。

 最合適的一種方式是,和 threadlocal 一塊兒來處理,連接中的三篇皆爲採用此方式的處理例子示例。再也不贅述。

https://blog.csdn.net/enterys/article/details/49465295

https://blog.csdn.net/yx0628/article/details/21172473

http://www.javashuo.com/article/p-grerwtod-n.html

相關文章
相關標籤/搜索