1. 程式人生 > >Servlet執行過程和簡單模擬

Servlet執行過程和簡單模擬

一.什麼是Servlet

Servlet(Server Applet)是Java Servlet的簡稱,稱為小服務程式或服務聯結器,用Java編寫的伺服器端程式,主要功能在於互動式地瀏覽和修改資料,生成動態Web內容,它工作在客戶端請求與伺服器響應的中間層,處理請求和傳送響應的過程都是由Servlet的程式來完成的。

二.tomact和servlet的關係

Tomcat 是Web應用伺服器,是一個Servlet/JSP容器。Tomcat 作為Servlet容器,負責處理客戶請求,把請求傳送給Servlet,並將Servlet的響應傳送回給客戶。而Servlet是一種執行在支援Java語言的伺服器上的元件。Tomcat將http請求文字接收並解析,然後封裝成HttpServletRequest型別的request物件,所有的HTTP頭資料讀可以通過request物件呼叫對應的方法查詢到。Tomcat同時會要響應的資訊封裝為HttpServletResponse型別的response物件,通過設定response屬性就可以控制要輸出到瀏覽器的內容,然後將response交給tomcat,tomcat就會將其變成響應文字的格式傳送給瀏覽器。

三.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 通過呼叫 init () 方法進行初始化。

init 方法被設計成只調用一次。當用戶呼叫一個 Servlet 時,就會建立一個 Servlet 例項,每一個使用者請求都會產生一個新的執行緒,init() 方法簡單地建立或載入一些資料,這些資料將被用於 Servlet 的整個生命週期。在使用者傳送請求時呼叫init(),並不是容器啟動時載入(tomact)時呼叫。

第二步:Servlet 呼叫 service() 方法來處理客戶端的請求。

Servlet 容器(即 Web 伺服器)呼叫 service() 方法來處理來自客戶端(瀏覽器)的請求,並把格式化的響應寫回給客戶端,service() 方法檢查 HTTP 請求型別(GET、POST、PUT、DELETE 等),並在適當的時候呼叫 doGet、doPost、doPut,doDelete 等方法。service 方法不需要覆寫,如果覆寫了別忘了super.service(request,response),不然不會去呼叫doGet或者doPost方法

第三步:Servlet 通過呼叫 destroy() 方法終止(結束)。

destroy() 方法只會呼叫一次,在 Servlet 生命週期結束時被呼叫。destroy() 方法可以讓您的 Servlet 關閉資料庫連線、停止後臺執行緒、把 Cookie 列表或點選計數器寫入到磁碟,並執行其他類似的清理活動。

第四步:最後,Servlet 是由 JVM 的垃圾回收器進行垃圾回收的

在呼叫 destroy() 方法之後,servlet 物件被標記為垃圾回收。

五.Servlet部署

需要告訴容器把請求給哪個servlet,所以需要在web.xml部署路徑。

<servlet>

  <servlet-name>HelloWorld</servlet-name>

  <servlet-class>包名.類名</servlet-class>

</servlet>

<servlet-mapping>

  <servlet-name>HelloWorld</servlet-name>

  <url-pattern>/HelloWorld</url-pattern>

</servlet-mapping>

1.<servlet>

<servlet>標籤用於配置 Servlet 類資訊。<servlet-name>是 Servlet 的標識,必須保證在整個xml 中唯一。<servlet-class>是這個 Servlet 對應的類名。

2.<servlet-mapping>

<servlet-mapping>標籤用於配置Servlet 的訪問路徑。<servlet-name>需要和<servlet>中的<servlet-name>保持一致。<url-pattern>指定訪問路徑。

如上面配置的就能接收到http://localhost:8080/HelloWorld的請求。但是在servlet3.0後有了註解,可以用@WebServlet("/HelloWorld")來代替上面的配置檔案。

六.模擬Servlet

用註解方式來模擬Servlet開發。我簡單的說說註解是什麼,註解(Annotation)相當於一種標記,在程式中加入註解就等於為程式打上某種標記,沒有加,則等於沒有任何標記,以後,javac編譯器、開發工具和其他程式可以通過反射來了解你的類及各種元素上有無何種標記,看你的程式有什麼標記,就去幹相應的事,標記可以加在包、類,屬性、方法,方法的引數以及區域性變數上。@Retention(RetentionPolicy.RUNTIME)表示註解的生存週期,@Target(ElementType.TYPE)表示註解在哪。

1.先建立WebServlet註解

package Annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定義WebServlet註解,模擬Servlet3.0的WebServlet註解
 * @Target 註解的屬性值表明了 @WebServlet註解只能用於類或介面定義宣告的前面, 
 * @WebServlet註解有一個必填的屬性 value 。
 * 呼叫方式為: @WebServlet(value="/xxxx") ,
 * 因語法規定如果屬性名為 value 且只填 value屬性值時,可以省略 value屬性名,即也可以寫作:@WebServlet("/xxxx") 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface WebServlet {
    //Servlet的訪問URL
    String value();
    //Servlet的訪問URL
    String[] urlPatterns() default {""};
    //Servlet的描述
    String description() default "";
    //Servlet的顯示名稱
    String displayName() default "";
    //Servlet的名稱
    String name() default "";
    //Servlet的init引數
    WebInitParam[] initParams() default {};
}

建立WebInitParam註解

package Annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface WebInitParam {
    //引數名
    String paramName() default "";
    //引數的值
    String paramValue() default "";
}

2.因為要模擬servlet,所以不能直接去建立servlet,應該要實現filter。

package Server;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import Annotation.WebInitParam;
import Annotation.WebServlet;
import Tool.ScanClassUtil;

public class AnnotationHandleFilter implements Filter {

	private ServletContext servletContext = null;

	/*
	 * 過濾器初始化時掃描指定的包下面使用了WebServlet註解的那些類
	 * 
	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
	 */
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("---AnnotationHandleFilter過濾器初始化開始---");
		servletContext = filterConfig.getServletContext();
		Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
		// 獲取web.xml中配置的要掃描的包
		String basePackage = filterConfig.getInitParameter("basePackage");
		// 如果配置了多個包,例如:<param-value>me.gacl.web.controller,me.gacl.web.UI</param-value>
		if (basePackage.indexOf(",") > 0) {
			// 按逗號進行分隔
			String[] packageNameArr = basePackage.split(",");
			for (String packageName : packageNameArr) {
				addServletClassToServletContext(packageName, classMap);
			}
		} else {
			addServletClassToServletContext(basePackage, classMap);
		}
		System.out.println("----AnnotationHandleFilter過濾器初始化結束---");
	}

	/**
	 * @Method: addServletClassToServletContext
	 * @Description:新增ServletClass到ServletContext中
	 * @param packageName
	 * @param classMap
	 */
	private void addServletClassToServletContext(String packageName, Map<String, Class<?>> classMap) {
		Set<Class<?>> setClasses = ScanClassUtil.getClasses(packageName);
		for (Class<?> clazz : setClasses) {
			if (clazz.isAnnotationPresent(WebServlet.class)) {
				// 獲取WebServlet這個Annotation的例項
				WebServlet annotationInstance = clazz.getAnnotation(WebServlet.class);
				// 獲取Annotation的例項的value屬性的值
				String annotationAttrValue = annotationInstance.value();
				if (!annotationAttrValue.equals("")) {
					classMap.put(annotationAttrValue, clazz);
				}
				// 獲取Annotation的例項的urlPatterns屬性的值
				String[] urlPatterns = annotationInstance.urlPatterns();
				for (String urlPattern : urlPatterns) {
					classMap.put(urlPattern, clazz);
				}
				servletContext.setAttribute("servletClassMap", classMap);
				System.out.println("annotationAttrValue:" + annotationAttrValue);
				String targetClassName = annotationAttrValue.substring(annotationAttrValue.lastIndexOf("/") + 1);
				System.out.println("targetClassName:" + targetClassName);
				System.out.println(clazz);
			}
		}
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("---進入註解處理過濾器---");
		// 將ServletRequest強制轉換成HttpServletRequest
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse res = (HttpServletResponse) response;
		@SuppressWarnings("unchecked")
		Map<String, Class<?>> classMap = (Map<String, Class<?>>) servletContext.getAttribute("servletClassMap");
		// 獲取contextPath
		String contextPath = req.getContextPath();
		// 獲取使用者請求的URI資源
		String uri = req.getRequestURI();
		// 如果沒有指明要呼叫Servlet類中的哪個方法
		if (uri.indexOf("!") == -1) {
			// 獲取使用者使用的請求方式
			String reqMethod = req.getMethod();
			// 獲取要請求的servlet路徑
			String requestServletName = uri.substring(contextPath.length(), uri.lastIndexOf("."));
			// 獲取要使用的類
			Class<?> clazz = classMap.get(requestServletName);
			// 建立類的例項
			Object obj = null;
			try {
				obj = clazz.newInstance();
			} catch (InstantiationException e1) {
				e1.printStackTrace();
			} catch (IllegalAccessException e1) {
				e1.printStackTrace();
			}
			Method targetMethod = null;
			if (reqMethod.equalsIgnoreCase("get")) {
				try {
					targetMethod = clazz.getDeclaredMethod("doGet", HttpServletRequest.class,
							HttpServletResponse.class);
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				}
			} else {
				try {
					targetMethod = clazz.getDeclaredMethod("doPost", HttpServletRequest.class,
							HttpServletResponse.class);
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				}
			}

			try {
				// 呼叫物件的方法進行處理
				targetMethod.invoke(obj, req, res);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		} else {
			// 獲取要請求的servlet路徑
			String requestServletName = uri.substring(contextPath.length(), uri.lastIndexOf("!"));
			// 獲取要呼叫的servlet的方法
			String invokeMethodName = uri.substring(uri.lastIndexOf("!") + 1, uri.lastIndexOf("."));

			// 獲取要使用的類
			Class<?> clazz = classMap.get(requestServletName);
			// 建立類的例項
			Object obj = null;
			try {
				obj = clazz.newInstance();
			} catch (InstantiationException e1) {
				e1.printStackTrace();
			} catch (IllegalAccessException e1) {
				e1.printStackTrace();
			}
			// 獲得clazz類定義的所有方法
			Method[] methods = clazz.getDeclaredMethods();
			// 獲取WebServlet這個Annotation的例項
			WebServlet annotationInstance = clazz.getAnnotation(WebServlet.class);
			// 獲取註解上配置的初始化引數陣列
			WebInitParam[] initParamArr = annotationInstance.initParams();
			Map<String, String> initParamMap = new HashMap<String, String>();
			for (WebInitParam initParam : initParamArr) {
				initParamMap.put(initParam.paramName(), initParam.paramValue());
			}
			// 遍歷clazz類中的方法
			for (Method method : methods) {
				// 該方法的返回型別
				Class<?> retType = method.getReturnType();
				// 獲得方法名
				String methodName = method.getName();
				// 列印方法修飾符
				System.out.print(Modifier.toString(method.getModifiers()));
				System.out.print(" " + retType.getName() + " " + methodName + "(");
				// 獲得一個方法引數陣列(getparameterTypes用於返回一個描述引數型別的Class物件陣列)
				Class<?>[] paramTypes = method.getParameterTypes();
				for (int j = 0; j < paramTypes.length; j++) {
					// 如果有多個引數,中間則用逗號隔開,否則直接列印引數
					if (j > 0) {
						System.out.print(",");
					}
					System.out.print(paramTypes[j].getName());
				}
				System.out.println(");");
				if (method.getName().equalsIgnoreCase("init")) {
					try {
						// 呼叫Servlet的初始化方法
						method.invoke(obj, initParamMap);
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
				}
			}
			// 獲取WebServlet這個Annotation的例項
			System.out.println("invokeMethodName:" + invokeMethodName);
			try {
				try {
					// 利用反射獲取方法例項,方法的簽名必須符合:
					// public void 方法名(HttpServletRequest request,
					// HttpServletResponse response)
					// 例如:public void loginHandle(HttpServletRequest request,
					// HttpServletResponse response)
					Method targetMethod = clazz.getDeclaredMethod(invokeMethodName, HttpServletRequest.class,
							HttpServletResponse.class);
					// 呼叫物件的方法進行處理
					targetMethod.invoke(obj, req, res);
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}

	public void destroy() {

	}
}

AnnotationHandleFilter過濾器初始化時掃描指定的包下面使用了WebServlet註解的那些類,然後將類儲存到一個Map集合中,再將Map集合儲存到servletContext物件中

3.需要在web.xml裡配置過濾器

<filter>
        <description>註解處理過濾器</description>
        <filter-name>AnnotationHandleFilter</filter-name>
        <filter-class>Server.AnnotationHandleFilter</filter-class>
        <init-param>
            <description>配置要掃描包及其子包, 如果有多個包,以逗號分隔</description>
            <param-name>basePackage</param-name>
            <param-value>Test</param-value>
        </init-param>
    </filter>
    
    <filter-mapping>
        <filter-name>AnnotationHandleFilter</filter-name>
        <!-- 攔截字尾是.do的請求 -->
        <url-pattern>*.do</url-pattern>
    </filter-mapping>

4.需要的工具類

AnnotationHandleFilter過濾器初始化方法init(FilterConfig filterConfig)使用到了一個用於掃描某個包下面的類的工具類ScanClassUtil,通過上面的配置檔案需要去掃描哪些類帶有註解標籤的,有的話把類用map集合裝起來。

5.簡單測試

package Test;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import Annotation.WebServlet;

@WebServlet("/Login")
public class LoginServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        request.getRequestDispatcher("/Login.jsp").forward(request, response);
    }
    
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

總結:容器載入的時候,filter的init方法也載入了,當用戶請求時,容器會把請求給servlet,但在service方法執行前會先呼叫dofilter方法,在上面覆寫了dofilter方法,但在初始化filter的時候,獲得該專案路徑,獲得配置檔案中的basePackage,再通過ScanClassUtil類把需要掃描的包下面所有的類用集合裝起來。然後通過addServletClassToServletContext方法去遍歷這個類元素上是否加了WebServlet註解,如果有就得到WebServlet裡的value值和屬性值,並重新放到map集合裡。再執行dofilter方法,並得到請求的url路徑,通過拆分得到要使用的類,再去map集合裡取出來通過反射例項化,通過使用者的請求去呼叫doget還是dopost方法。

相關推薦

Servlet執行過程簡單模擬

一.什麼是Servlet Servlet(Server Applet)是Java Servlet的簡稱,稱為小服務程式或服務聯結器,用Java編寫的伺服器端程式,主要功能在於互動式地瀏覽和修改資料,生成動態Web內容,它工作在客戶端請求與伺服器響應的中間層,處理請求和傳送響

struts執行過程攔截器

strutsstruts執行過程和攔截器Struts2執行過程 Struts2攔截器概述1 struts2是框架,封裝了很多的功能,struts2裏面封裝的功能都是在攔截器裏面 2 struts2裏面封裝了很多的功能,有很多攔截器,不是每次這些攔截器都執行,每次執行默認的攔截器 3 struts2裏面默認攔截

運行c程序的過程簡單的c程序結構

執行文件 學習過程 單位 計算機 sta main函數 計算 行程 返回 運行c程序的過程 編譯 編譯器可以將源代碼轉換成機器語言,在編譯過程中,會找出錯誤並報告。這個階段的輸入是在編輯期間產生的文件,常稱為源文件。 編譯器能找出程序中很多無效的和無法識別的錯誤,包括結構錯

/etc/profile ~/.bash_profile ~/.bashrc 等文件的執行過程 區別

區別 TP nbsp 全部 HR 開發 blog IT AR /etc/profile :系統的所有用戶共享 ~/.bash_profile:僅針對當前用戶有效 我的電腦只用於開發,因此全部配置/etc/profile 參考鏈接: https://blog.csdn

JVM學習05-位元組碼執行過程JVM指令集

1. 位元組碼的Code部分 在執行位元組碼的時候,無非也就是對呼叫類中的函式。那麼下面將介紹下位元組碼函式的Code部分,Code部分的程式碼一個可以用java自帶的命令javap命令進行檢視。還可以在eclipse中安裝ByteCode visualize

對遞迴執行過程簡單描述

#include <stdio.h> void fun(int n) { printf("1th - Level: %d Address: %d\n", n, &n); if(n < 3)

實現分析sql語句執行過程編譯時間的方法

        有時候我們經常為我們的sql語句執行效率低下發愁,反覆優化後,可還是得不到提高。         那麼你就用這條語句找出你sql到底是在哪裡慢了         示例:         SET STATISTICS io ON        SET STATISTICS time ON   

回顧二分與bfs(或者說是遞推)簡單模擬

今天,陽光正好,適合敲程式碼,諸事皆宜。 先來兩道簡單的模擬題。 第一道 機器翻譯 輸出為5. 程式碼思路:很明顯需要用到佇列來存單詞,在建立一個bool陣列來儲存佇列中有沒有這個單詞,需不需要向外界查詢,如果需要並且佇列可以容下,則加入佇列並將bool陣列標記在佇列中有該單詞,如果佇列容不下,

Action的實現方式struts.xml配置的詳細解釋,Struts2的簡單執行過程(二)

我把你的頭像,設定成我的名字,此刻你便與我同在。我把你的名字,寫進我的程式碼裡面,以後,我的世界便存在著你。         “兩個蝴蝶飛”特別喜歡"java1234知識分享網"小峰的實用主義,所以本文及其系列文章均是採用實用主義,

瀏覽器的解析執行過程

們的 由於 繼續 動畫 table 就會 內嵌 cnblogs 內嵌腳本 當瀏覽器獲得一個html文件時,會“自上而下”加載,並在加載過程中進行解析渲染。 解析: 1. 瀏覽器會將HTML解析成一個DOM樹(display:none,visibility:hidden)。

Servlet的生命周期及執行過程

生命 tro font 接收 -m 方法 dog 服務器 一次 Servlet生命周期分為實例化、初始化、響應請求調用service()方法、消亡階段調用destroy()方法。 執行過程如下: 1)當瀏覽器發送一個請求地址,tomcat會接收這個請求 2)tomcat會讀

生成器接受返還功能在執行過程中的詳解

top exce ret one 代碼 stop ngx put rsa 1 import random 2 3 SENTENCES=[ 4 ‘How are you ?‘, 5 ‘Fine,thank you!‘, 6 ‘Nothi

VS中設定逐過程執行屬性運算子

在VS除錯程式碼的時候,想進入關鍵系或者屬性的具體實現的時候會彈出這個視窗 點選“是”之後,vs就直接跳過關鍵字,即使逐語句也無法進入具體實現,下次還是繼續彈出這個視窗。 點選“否”之後,再次除錯的時候就不會提示這個視窗,但是還是無法逐語句的進行除錯。 原因: VS在不知道

第4章:作為Servlet:請求響應/4.1 Servlet載入過程

Servlet載入過程 容器tomcat啟動讀取web.xml檔案,載入相關資源 讀取全域性servlet資源,這部分資源所有的servlet物件共用(比如 資料庫連線資訊 ),形成ServletContext物件 讀取<servlet>配

u-boot、kernelfilesystem 執行過程分析

標題:   Uboot -kerne-root 啟動流程 內容:   ※uboot啟動流程   ※Kernel啟動流程   ※Root啟動流程   ※構建根檔案系統   /********************************* *u-boot

SparkShuffle的分類執行過程的一些總結

什麼是Spark Shuffle? reduceByKey會將上一個RDD中的每一個key對應的所有value聚合成一個value,然後生成一個新的RDD,元素型別是<key,value>對的形式,這樣每一個key對應一個聚合起來的value。 在聚合之前,相同的key可能在

servlet 介紹簡單使用

1,servle介紹: html+css+js Servlet:用來開發動態web的技術 屬於javaee規範 【1】,javaEE Servers建立專案      a,new Dynamic Web Project  &nbs

Liunx----Shell指令碼簡單介紹 Shell執行環境Shell變數

Shell 指令碼(shell script),是一種為 shell 編寫的指令碼程式。 Shell 程式設計跟 java、php 程式設計一樣,只要有一個能編寫程式碼的文字編輯器和一個能解釋執行的指令碼直譯器就可以了。 Linux 的 Shell 種類眾多,常見的有: Bourne Shell(

MySQL-SQLyog執行儲存過程儲存函式等

1.開啟SQLyog在目錄樹-函式(儲存過程)資料夾上右鍵,建立函式(建立儲存過程) 2.按照MySQL定義函式的規則,新定義一個函式:   3. 然後新建查詢編輯器,呼叫自定義的函式。 命令:SELECT function_name(params1, ...),函式名稱

Java編譯程式執行過程詳解

java整個編譯以及執行的過程相當繁瑣,我就舉一個簡單的例子說明: 編譯原理簡單過程:詞法分析 --> 語法分析 --> 語義分析和中間程式碼生成 --> 優化 --> 目的碼生成 Java程式從原始檔建立到程式執行要經過兩大步驟: 1、Java檔案會由