一、Servlet的傳統配置方式
在JavaWeb開發中, 每次編寫一個Servlet都需要在web.xml文件中進行配置,如下所示:
<servlet> <servlet-name>ActionServlet</servlet-name> <servlet-class>com.web.controller.ActionServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ActionServlet</servlet-name> <url-pattern>/servlet/ActionServlet</url-pattern> </servlet-mapping>
發一個Servlet,都要在web.xml中配置Servlet才能夠使用,這實在是很頭疼的事情,所以Servlet3.0之后提供了注解(annotation),使得不再需要在web.xml文件中進行Servlet的部署描述,簡化開發流程。本文所講的基於注解方式配置Servlet不是針對Servlet3.0的,而是基於Servlet2.5的,通過開發自定義注解和注解處理器來模擬實現類似於Servlet3.0的注解方式配置Servlet。
二、基於注解的方式配置Servlet
JDK1. 5版本之后, JAVA提供了一種叫做Annotation的新數據類型,中文譯為注解或標注,它的出現為鋪天蓋地的XML配置文件提供了一個完美的解決方案,讓 JAVA EE開發更加方便快速,也更加干凈了。不過Servlet2.5默認情況下是不支持注解方式的配置的,但是我們可以開發自定義注解,然后將注解標注到Servlet上,再針對我們自定義的注解寫一個注解處理器.
原理:
首先,瀏覽器發出請求后,經過過濾器。過濾器執行初始init方法。工具類掃描配置文件中指定的包。掃描工具把指定包下的所有類的字節碼文件存放到Set集合中,然后遍歷set集合,如果set集合中的元素(包中的各個類的字節碼文件)用的是WebServlet.class這個注解類型編寫開發的,那么就獲取當前set元素的在注解中定義的value值(URI地址),這個value值在注解接口WebServlet中定義的是Servlet的訪問URI。因此過濾器的初始化init()方法就把"指定包下的所有使用WebServlet注解開發的普通類的字節碼文件"作為值value,使用注解中的String value()定義了Servlet訪問的URI 作為鍵key, 存放到 Map<String, Class<?>> classMap 這個Map集合中。map集合存放到全局對象ServeltContext中。然后在過濾器的doFilter方法中,在ServeltContext中獲取到這個map。過濾器如果執行了,那么肯定是用戶發出request請求了。先通過request對象獲取用戶的URI,在map集合中找到此使用注解的java普通類,反射創建此java類的對象。如果沒有指定調用哪個方法,那么就根據用戶的請求方式get還是post,調用相對應的方法。如果指定了方法,先根據URI在map集合中找到對應的字節碼文件,創建java普通類的對象。根據地址中"!"后面的方法,反射執行java普通類對象的方法。當然,有了字節碼文件,就能獲取到java類中的所有方法,當然也能獲取到注解中定義的參數值,從而作為參數執行java類的init方法。
具體的做法如下:
2.1、開發用於配置Servlet的相關注解
1、開發WebServlet注解,用於標注處理請求的Servlet類
package com.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 {}; }
將Servlet在web.xml中的配置信息使用WebServlet注解來表示,使用注解后,只需要在相應Servlet 類的前面使用類似@WebServlet("/servlet/LoginServlet") 注解就可以達到和上述 web.xml 文件中配置信息一樣的目的。注解@WebServlet中的屬性值"/servlet/LoginServlet"表示了web.xml 配置文件中 <servlet-mapping> 元素的子元素 <url-pattern> 里的值。通過這樣的注解能簡化在 XML 文件中配置 Servlet 信息,整個配置文件將會非常簡潔干凈,開發人員的工作也將大大減少。
2、開發WebInitParam注解,用於配置Servlet初始化時使用的參數
package com.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @ClassName: WebInitParam * @Description: 定義Servlet的初始化參數注解 * @author: hdb * @date: 2017-10-1 下午3:25:53 * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface WebInitParam { //參數名 String paramName() default ""; //參數的值 String paramValue() default ""; }
2.2、編寫處理注解的處理器
上面簡要地介紹了注解的定義聲明與使用方式,注解在后台需要一個處理器才能起作用,所以還得針對上面的注解編寫處理器,在這里我們使用Filter作為注解的處理器,編寫一個AnnotationHandleFilter,代碼如下:
package com.web.filter; 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 com.annotation.WebInitParam; import com.annotation.WebServlet; import com.util.ScanClassUtil; /** * @ClassName: AnnotationHandleFilter * @Description: 使用Filter作為注解的處理器 * @author: hdb * @date: 2017-11-12 下午10:15:19 * */ 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>com.web.controller,com.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中 * @Anthor:hdb * * @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; 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對象中。
在web.xml文件中配置AnnotationHandleFilter過濾器和需要掃描的包
<filter> <description>注解處理過濾器</description> <filter-name>AnnotationHandleFilter</filter-name> <filter-class>com.web.filter.AnnotationHandleFilter</filter-class> <init-param> <description>配置要掃描包及其子包, 如果有多個包,以逗號分隔</description> <param-name>basePackage</param-name> <param-value>com.web.controller,com.web.UI</param-value> <!-- <param-value>com.web.controller</param-value> --> </init-param> </filter> <filter-mapping> <filter-name>AnnotationHandleFilter</filter-name> <!-- 攔截后綴是.do的請求 --> <url-pattern>*.do</url-pattern> </filter-mapping>
AnnotationHandleFilter過濾器初始化方法init(FilterConfig filterConfig)使用到了一個用於掃描某個包下面的類的工具類ScanClassUtil,ScanClassUtil的代碼如下:
package com.util; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.net.URLDecoder; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class ScanClassUtil { /** * 從包package中獲取所有的Class * * @param pack * @return */ public static Set<Class<?>> getClasses(String pack) { // 第一個class類的集合 Set<Class<?>> classes = new LinkedHashSet<Class<?>>(); // 是否循環迭代 boolean recursive = true; // 獲取包的名字 並進行替換 String packageName = pack; String packageDirName = packageName.replace('.', '/'); // 定義一個枚舉的集合 並進行循環來處理這個目錄下的things Enumeration<URL> dirs; try { dirs = Thread.currentThread().getContextClassLoader().getResources( packageDirName); // 循環迭代下去 while (dirs.hasMoreElements()) { // 獲取下一個元素 URL url = dirs.nextElement(); // 得到協議的名稱 String protocol = url.getProtocol(); // 如果是以文件的形式保存在服務器上 if ("file".equals(protocol)) { System.err.println("file類型的掃描"); // 獲取包的物理路徑 String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); // 以文件的方式掃描整個包下的文件 並添加到集合中 findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes); } else if ("jar".equals(protocol)) { // 如果是jar包文件 // 定義一個JarFile System.err.println("jar類型的掃描"); JarFile jar; try { // 獲取jar jar = ((JarURLConnection) url.openConnection()) .getJarFile(); // 從此jar包 得到一個枚舉類 Enumeration<JarEntry> entries = jar.entries(); // 同樣的進行循環迭代 while (entries.hasMoreElements()) { // 獲取jar里的一個實體 可以是目錄 和一些jar包里的其他文件 如META-INF等文件 JarEntry entry = entries.nextElement(); String name = entry.getName(); // 如果是以/開頭的 if (name.charAt(0) == '/') { // 獲取后面的字符串 name = name.substring(1); } // 如果前半部分和定義的包名相同 if (name.startsWith(packageDirName)) { int idx = name.lastIndexOf('/'); // 如果以"/"結尾 是一個包 if (idx != -1) { // 獲取包名 把"/"替換成"." packageName = name.substring(0, idx) .replace('/', '.'); } // 如果可以迭代下去 並且是一個包 if ((idx != -1) || recursive) { // 如果是一個.class文件 而且不是目錄 if (name.endsWith(".class") && !entry.isDirectory()) { // 去掉后面的".class" 獲取真正的類名 String className = name.substring( packageName.length() + 1, name .length() - 6); try { // 添加到classes classes.add(Class .forName(packageName + '.' + className)); } catch (ClassNotFoundException e) { // log // .error("添加用戶自定義視圖類錯誤 找不到此類的.class文件"); e.printStackTrace(); } } } } } } catch (IOException e) { // log.error("在掃描用戶定義視圖時從jar包獲取文件出錯"); e.printStackTrace(); } } } } catch (IOException e) { e.printStackTrace(); } return classes; } /** * 以文件的形式來獲取包下的所有Class * * @param packageName * @param packagePath * @param recursive * @param classes */ public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) { // 獲取此包的目錄 建立一個File File dir = new File(packagePath); // 如果不存在或者 也不是目錄就直接返回 if (!dir.exists() || !dir.isDirectory()) { // log.warn("用戶定義包名 " + packageName + " 下沒有任何文件"); return; } // 如果存在 就獲取包下的所有文件 包括目錄 File[] dirfiles = dir.listFiles(new FileFilter() { // 自定義過濾規則 如果可以循環(包含子目錄) 或則是以.class結尾的文件(編譯好的java類文件) public boolean accept(File file) { return (recursive && file.isDirectory()) || (file.getName().endsWith(".class")); } }); // 循環所有文件 for (File file : dirfiles) { // 如果是目錄 則繼續掃描 if (file.isDirectory()) { findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes); } else { // 如果是java類文件 去掉后面的.class 只留下類名 String className = file.getName().substring(0, file.getName().length() - 6); try { // 添加到集合中去 //classes.add(Class.forName(packageName + '.' + className)); //經過回復同學的提醒,這里用forName有一些不好,會觸發static方法,沒有使用classLoader的load干凈 classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className)); } catch (ClassNotFoundException e) { // log.error("添加用戶自定義視圖類錯誤 找不到此類的.class文件"); e.printStackTrace(); } } } } }
經過以上兩步,我們的自定義注解和針對注解的處理器都開發好了。
2.3、WebServlet注解簡單測試
編寫一個用於跳轉到Login.jsp頁面的LoginUIServlet,LoginUIServlet就是一個普通的java類,不是一個真正的Servlet,然后使用WebServlet注解標注LoginUIServlet類,代碼如下:
package com.web.UI; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.annotation.WebServlet; @WebServlet("/servlet/LoginUI") public class LoginUIServlet { 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); } }
在瀏覽器中輸入訪問地址:http://gacl-pc:8080/AnnotationConfigServlet/servlet/Login.do,根據web.xml文件的配置,所有后綴名為 .do請求,都會經過AnnotationHandleFilter過濾器的doFilter方法,在doFilter方法的實現代碼中,從HttpServletRequest請求對象中得到請求的方式類型(GET/POST)和請求的URI 。如有請求http://gacl-pc:8080/AnnotationConfigServlet/servlet/LoginUI.do,此時請求方法類型為GET, URI 值為/AnnotationConfigServlet/servlet/LoginUI.do。從ServletConext對象中獲取到在過濾器中保存的Map結構,根據 URI 獲得一個 Key=”/servlet/LoginUI” ,從 Map 結構中根據此Key得到Value ,此時Value就是要請求調用的那個Servlet類,根據Servlet類創建對象實例,再根據前面得到的請求方法類型,能決定調用此Servlet對象實例的 doGet 或 doPost 方法。最終客戶端發生的后綴為.do請求,經由AnnotationHandleFilter對請求對象(HttpServletRequest)的分析,從而調用相應某Servlet的doGet或doPost方法,完成了一次客戶端請求到服務器響應的過程。
使用注解后程序流程如下所示:
運行結果如下:
從運行結果中可以看到,我們的注解處理器成功調用了目標Servlet處理用戶的請求,通過@WebServlet注解, Servlet不用再在web.xml 文件中進行繁冗的注冊,這就是使用@WebServlet注解的方便之處。
2.3、WebServlet注解復雜測試
編寫Login.jsp頁面,代碼如下:
<%@ page language="java" pageEncoding="UTF-8"%> <!DOCTYPE HTML> <html> <head> <title>登錄頁面</title> </head> <body> <fieldset> <legend>用戶登錄</legend> <form action="${pageContext.request.contextPath}/servlet/LoginServlet!loginHandle.do" method="post"> 用戶名:<input type="text" value="${param.usename}" name="usename"> <br/> 密碼:<input type="text" value="${param.pwd}" name="pwd"> <br/> <input type="submit" value="登錄"/> </form> </fieldset> <hr/> <label style="color: red;">${msg}</label> </body> </html>
form表單中的action屬性的URL="${pageContext.request.contextPath}/servlet/LoginServlet!loginHandle.do",/servlet/LoginServlet表示要訪問的是LoginServlet,!后面的loginHandle表示要調用LoginServlet中的loginHandle方法處理此次的請求,也就是說,我們在訪問Servlet時,可以在URL中指明要訪問Servlet的哪個方法,AnnotationHandleFilter過濾器的doFilter方法在攔截到用戶的請求之后,首先獲取用戶要訪問的URI,根據URI判斷用戶要訪問的Servlet,然后再判斷URI中是否包含了"!",如果有,那么就說明用戶顯示指明了要訪問Servlet的哪個方法,遍歷Servlet類中定義的所有方法,如果找到了URI中的那個方法,那么就調用對應的方法處理用戶請求!
LoginServlet的代碼如下:
package com.web.controller; import java.io.IOException; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.annotation.WebInitParam; import com.annotation.WebServlet; /** * * @ClassName: LoginServlet * @Description:處理用戶登錄的Servlet, * LoginServlet現在就是一個普通的java類,不是一個真正的Servlet * @author: hdb * @date: 2017-11-8 上午12:07:58 * */ //將開發好的WebServlet注解標注到LoginServlet類上 @WebServlet( //Servlet的訪問URL value="/servlet/LoginServlet", //Servlet的訪問URL,可以使用數組的方式配置多個訪問路徑 urlPatterns={"/gacl/LoginServlet","/xdp/LoginServlet"}, //Servlet的初始化參數 initParams={ @WebInitParam(paramName="gacl",paramValue="hdb"), @WebInitParam(paramName="bhsh",paramValue="白虎神皇") }, name="LoginServlet", description="處理用戶登錄的Servlet" ) public class LoginServlet { public void loginHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ String username = request.getParameter("usename"); String pwd = request.getParameter("pwd"); if (username.equals("gacl") && pwd.equals("xdp")) { request.getSession().setAttribute("usename", username); request.setAttribute("msg", "歡迎您!"+username); request.getRequestDispatcher("/index.jsp").forward(request, response); }else { request.setAttribute("msg", "登錄失敗,請檢查用戶名和密碼是否正確!"); request.getRequestDispatcher("/Login.jsp").forward(request, response); } } /** * @Method: init * @Description: Servlet初始化 * @Anthor:hdb * * @param config */ public void init(Map<String, String> initParamMap){ System.out.println("--LoginServlet初始化--"); System.out.println(initParamMap.get("gacl")); System.out.println(initParamMap.get("bhsh")); } }
運行結果如下:
可以看到,我們使用注解方式配置的Servlet已經成功調用了,loginHandle方法處理用戶登錄請求的完整處理過程如下圖所示:
Servlet3.0是支持采用基於注解的方式配置Servlet的,在此我使用過濾器作為注解處理器模擬模擬出了類似Servlet3.0的注解處理方式,簡化了Servlet的配置。這種使用自定義注解+注解處理器的方式山寨出來的Servlet3.0大家了解一下即可,了解一下這種處理思路,在實際應用中還是不要這么做了,要真想使用注解的方式配置Servlet還是直接用Servlet3.0吧。