Servlet傳統配置方式和Servlet3.0使用注解的方式


一、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吧。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM