1. 首页
  2. JavaWeb学习总结

JavaWeb学习总结(四十八)——模拟Servlet3.0使用注解的方式配置Servlet

一、Servlet的传统配置方式

  在JavaWeb开发中, 每次编写一个Servlet都需要在web.xml文件中进行配置,如下所示:

  1 <servlet>
    2     <servlet-name>ActionServlet</servlet-name>
    3     <servlet-class>me.gacl.web.controller.ActionServlet</servlet-class>
    4 </servlet>
    5 
    6 <servlet-mapping>
    7     <servlet-name>ActionServlet</servlet-name>
    8     <url-pattern>/servlet/ActionServlet</url-pattern>
    9 </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上,再针对我们自定义的注解写一个注解处理器,具体的做法如下:

2.1、开发用于配置Servlet的相关注解

  1、开发WebServlet注解,用于标注处理请求的Servlet类

   1 package me.gacl.annotation;
     2 
     3 import java.lang.annotation.ElementType;
     4 import java.lang.annotation.Retention;
     5 import java.lang.annotation.RetentionPolicy;
     6 import java.lang.annotation.Target;
     7 
     8 /**
     9  * 自定义WebServlet注解,模拟Servlet3.0的WebServlet注解
    10  * @Target 注解的属性值表明了 @WebServlet注解只能用于类或接口定义声明的前面, 
    11  * @WebServlet注解有一个必填的属性 value 。
    12  * 调用方式为: @WebServlet(value="/xxxx") ,
    13  * 因语法规定如果属性名为 value 且只填 value属性值时,可以省略 value属性名,即也可以写作:@WebServlet("/xxxx") 
    14  */
    15 @Retention(RetentionPolicy.RUNTIME)
    16 @Target(ElementType.TYPE)
    17 public @interface WebServlet {
    18     //Servlet的访问URL
    19     String value();
    20     //Servlet的访问URL
    21     String[] urlPatterns() default {""};
    22     //Servlet的描述
    23     String description() default "";
    24     //Servlet的显示名称
    25     String displayName() default "";
    26     //Servlet的名称
    27     String name() default "";
    28     //Servlet的init参数
    29     WebInitParam[] initParams() default {};
    30 }

  将Servlet在web.xml中的配置信息使用WebServlet注解来表示,使用注解后,只需要在相应Servlet 类的前面使用类似@WebServlet("/servlet/LoginServlet") 注解就可以达到和上述 web.xml 文件中配置信息一样的目的。注解@WebServlet中的属性值"/servlet/LoginServlet"表示了web.xml 配置文件中 <servlet-mapping> 元素的子元素 <url-pattern> 里的值。通过这样的注解能简化在 XML 文件中配置 Servlet 信息,整个配置文件将会非常简洁干净,开发人员的工作也将大大减少。

  2、开发WebInitParam注解,用于配置Servlet初始化时使用的参数

   1 package me.gacl.annotation;
     2 
     3 import java.lang.annotation.ElementType;
     4 import java.lang.annotation.Retention;
     5 import java.lang.annotation.RetentionPolicy;
     6 import java.lang.annotation.Target;
     7 
     8 /**
     9 * @ClassName: WebInitParam
    10 * @Description: 定义Servlet的初始化参数注解
    11 * @author: 孤傲苍狼
    12 * @date: 2014-10-1 下午3:25:53
    13 *
    14 */
    15 @Retention(RetentionPolicy.RUNTIME)
    16 @Target(ElementType.TYPE)
    17 public @interface WebInitParam {
    18     //参数名
    19     String paramName() default "";
    20     //参数的值
    21     String paramValue() default "";
    22 }

2.2、编写处理注解的处理器

  上面简要地介绍了注解的定义声明与使用方式,注解在后台需要一个处理器才能起作用,所以还得针对上面的注解编写处理器,在这里我们使用Filter作为注解的处理器,编写一个AnnotationHandleFilter,代码如下:

    1 package me.gacl.web.filter;
      2 
      3 import java.io.IOException;
      4 import java.lang.reflect.InvocationTargetException;
      5 import java.lang.reflect.Method;
      6 import java.lang.reflect.Modifier;
      7 import java.util.HashMap;
      8 import java.util.Map;
      9 import java.util.Set;
     10 import javax.servlet.Filter;
     11 import javax.servlet.FilterChain;
     12 import javax.servlet.FilterConfig;
     13 import javax.servlet.ServletContext;
     14 import javax.servlet.ServletException;
     15 import javax.servlet.ServletRequest;
     16 import javax.servlet.ServletResponse;
     17 import javax.servlet.http.HttpServletRequest;
     18 import javax.servlet.http.HttpServletResponse;
     19 import me.gacl.annotation.WebInitParam;
     20 import me.gacl.annotation.WebServlet;
     21 import me.gacl.util.ScanClassUtil;
     22 
     23 /**
     24 * @ClassName: AnnotationHandleFilter
     25 * @Description: 使用Filter作为注解的处理器
     26 * @author: 孤傲苍狼
     27 * @date: 2014-11-12 下午10:15:19
     28 *
     29 */ 
     30 public class AnnotationHandleFilter implements Filter {
     31 
     32     private ServletContext servletContext = null;
     33     
     34     /* 过滤器初始化时扫描指定的包下面使用了WebServlet注解的那些类
     35      * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     36      */
     37     public void init(FilterConfig filterConfig) throws ServletException {
     38         System.out.println("---AnnotationHandleFilter过滤器初始化开始---");
     39         servletContext = filterConfig.getServletContext();
     40         Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
     41         //获取web.xml中配置的要扫描的包
     42         String basePackage = filterConfig.getInitParameter("basePackage");
     43         //如果配置了多个包,例如:<param-value>me.gacl.web.controller,me.gacl.web.UI</param-value>
     44         if (basePackage.indexOf(",")>0) {
     45             //按逗号进行分隔
     46             String[] packageNameArr = basePackage.split(",");
     47             for (String packageName : packageNameArr) {
     48                 addServletClassToServletContext(packageName,classMap);
     49             }
     50         }else {
     51             addServletClassToServletContext(basePackage,classMap);
     52         }
     53         System.out.println("----AnnotationHandleFilter过滤器初始化结束---");
     54     }
     55     
     56     /**
     57     * @Method: addServletClassToServletContext
     58     * @Description:添加ServletClass到ServletContext中
     59     * @Anthor:孤傲苍狼
     60     *
     61     * @param packageName
     62     * @param classMap
     63     */ 
     64     private void addServletClassToServletContext(String packageName,Map<String, Class<?>> classMap){
     65         Set<Class<?>> setClasses =  ScanClassUtil.getClasses(packageName);
     66         for (Class<?> clazz :setClasses) {
     67             if (clazz.isAnnotationPresent(WebServlet.class)) {
     68                 //获取WebServlet这个Annotation的实例
     69                 WebServlet annotationInstance = clazz.getAnnotation(WebServlet.class);
     70                 //获取Annotation的实例的value属性的值
     71                 String annotationAttrValue = annotationInstance.value();
     72                 if (!annotationAttrValue.equals("")) {
     73                     classMap.put(annotationAttrValue, clazz);
     74                 }
     75                 //获取Annotation的实例的urlPatterns属性的值
     76                 String[] urlPatterns = annotationInstance.urlPatterns();
     77                 for (String urlPattern : urlPatterns) {
     78                     classMap.put(urlPattern, clazz);
     79                 }
     80                 servletContext.setAttribute("servletClassMap", classMap);
     81                 System.out.println("annotationAttrValue:"+annotationAttrValue);
     82                 String targetClassName = annotationAttrValue.substring(annotationAttrValue.lastIndexOf("/")+1);
     83                 System.out.println("targetClassName:"+targetClassName);
     84                 System.out.println(clazz);
     85             }
     86         }
     87     }
     88 
     89     public void doFilter(ServletRequest request, ServletResponse response,
     90             FilterChain chain) throws IOException, ServletException {
     91         System.out.println("---进入注解处理过滤器---");
     92         //将ServletRequest强制转换成HttpServletRequest
     93         HttpServletRequest req = (HttpServletRequest)request;
     94         HttpServletResponse res = (HttpServletResponse)response;
     95         Map<String, Class<?>> classMap = (Map<String, Class<?>>) servletContext.getAttribute("servletClassMap");
     96         //获取contextPath
     97         String contextPath = req.getContextPath();
     98         //获取用户请求的URI资源
     99         String uri = req.getRequestURI();
    100         //如果没有指明要调用Servlet类中的哪个方法
    101         if (uri.indexOf("!")==-1) {
    102             //获取用户使用的请求方式
    103             String reqMethod = req.getMethod();
    104             //获取要请求的servlet路径
    105             String requestServletName = uri.substring(contextPath.length(),uri.lastIndexOf("."));
    106             //获取要使用的类
    107             Class<?> clazz = classMap.get(requestServletName);
    108             //创建类的实例
    109             Object obj = null;
    110             try {
    111                 obj = clazz.newInstance();
    112             } catch (InstantiationException e1) {
    113                 e1.printStackTrace();
    114             } catch (IllegalAccessException e1) {
    115                 e1.printStackTrace();
    116             }
    117             Method targetMethod = null;
    118             if (reqMethod.equalsIgnoreCase("get")) {
    119                 try {
    120                      targetMethod = clazz.getDeclaredMethod("doGet",HttpServletRequest.class,HttpServletResponse.class);
    121                 } catch (SecurityException e) {
    122                     e.printStackTrace();
    123                 } catch (NoSuchMethodException e) {
    124                     e.printStackTrace();
    125                 }
    126             }else {
    127                 try {
    128                     targetMethod = clazz.getDeclaredMethod("doPost",HttpServletRequest.class,HttpServletResponse.class);
    129                 } catch (SecurityException e) {
    130                     e.printStackTrace();
    131                 } catch (NoSuchMethodException e) {
    132                     e.printStackTrace();
    133                 }
    134             }
    135             
    136             try {
    137                 //调用对象的方法进行处理
    138                 targetMethod.invoke(obj,req,res);
    139             } catch (IllegalArgumentException e) {
    140                 e.printStackTrace();
    141             } catch (IllegalAccessException e) {
    142                 e.printStackTrace();
    143             } catch (InvocationTargetException e) {
    144                 e.printStackTrace();
    145             }
    146         }else {
    147             //获取要请求的servlet路径
    148             String requestServletName = uri.substring(contextPath.length(),uri.lastIndexOf("!"));
    149             //获取要调用的servlet的方法
    150             String invokeMethodName = uri.substring(uri.lastIndexOf("!")+1,uri.lastIndexOf("."));
    151         
    152             //获取要使用的类
    153             Class<?> clazz = classMap.get(requestServletName);
    154             //创建类的实例
    155             Object obj = null;
    156             try {
    157                 obj = clazz.newInstance();
    158             } catch (InstantiationException e1) {
    159                 e1.printStackTrace();
    160             } catch (IllegalAccessException e1) {
    161                 e1.printStackTrace();
    162             }
    163             //获得clazz类定义的所有方法
    164             Method[] methods = clazz.getDeclaredMethods();
    165             //获取WebServlet这个Annotation的实例
    166             WebServlet annotationInstance = clazz.getAnnotation(WebServlet.class);
    167             //获取注解上配置的初始化参数数组
    168             WebInitParam[] initParamArr = annotationInstance.initParams();
    169             Map<String, String> initParamMap = new HashMap<String, String>();
    170             for (WebInitParam initParam : initParamArr) {
    171                 initParamMap.put(initParam.paramName(), initParam.paramValue());
    172             }
    173             //遍历clazz类中的方法
    174             for (Method method : methods) {
    175                 //该方法的返回类型
    176                 Class<?> retType = method.getReturnType();
    177                 //获得方法名
    178                 String methodName = method.getName();
    179                 //打印方法修饰符
    180                 System.out.print(Modifier.toString(method.getModifiers()));
    181                 System.out.print(" "+retType.getName() + " " + methodName +"(");
    182                 //获得一个方法参数数组(getparameterTypes用于返回一个描述参数类型的Class对象数组)
    183                 Class<?>[] paramTypes = method.getParameterTypes();
    184                 for(int j = 0 ; j < paramTypes.length ; j++){
    185                      //如果有多个参数,中间则用逗号隔开,否则直接打印参数
    186                     if (j > 0){
    187                         System.out.print(",");
    188                     }  
    189                     System.out.print(paramTypes[j].getName());
    190                 }
    191                 System.out.println(");");
    192                 if (method.getName().equalsIgnoreCase("init")) {
    193                     try {
    194                         //调用Servlet的初始化方法
    195                         method.invoke(obj, initParamMap);
    196                     } catch (IllegalArgumentException e) {
    197                         e.printStackTrace();
    198                     } catch (IllegalAccessException e) {
    199                         e.printStackTrace();
    200                     } catch (InvocationTargetException e) {
    201                         e.printStackTrace();
    202                     }
    203                 }
    204             }
    205             //获取WebServlet这个Annotation的实例
    206             System.out.println("invokeMethodName:"+invokeMethodName);
    207             try {
    208                 try {
    209                     //利用反射获取方法实例,方法的签名必须符合:
    210                     //public void 方法名(HttpServletRequest request, HttpServletResponse response)
    211                     //例如:public void loginHandle(HttpServletRequest request, HttpServletResponse response)
    212                     Method targetMethod = clazz.getDeclaredMethod(invokeMethodName,HttpServletRequest.class,HttpServletResponse.class);
    213                     //调用对象的方法进行处理
    214                     targetMethod.invoke(obj,req,res);
    215                 } catch (SecurityException e) {
    216                     e.printStackTrace();
    217                 } catch (NoSuchMethodException e) {
    218                     e.printStackTrace();
    219                 } catch (IllegalArgumentException e) {
    220                     e.printStackTrace();
    221                 } catch (InvocationTargetException e) {
    222                     e.printStackTrace();
    223                 } 
    224             } catch (IllegalAccessException e) {
    225                 e.printStackTrace();
    226             }
    227         }
    228     }
    229 
    230     public void destroy() {
    231 
    232     }
    233 }

  AnnotationHandleFilter过滤器初始化时扫描指定的包下面使用了WebServlet注解的那些类,然后将类存储到一个Map集合中,再将Map集合存储到servletContext对象中。

  jiedefangshipeizhiservlet_1.png

  在web.xml文件中配置AnnotationHandleFilter过滤器和需要扫描的包

   1     <filter>
     2         <description>注解处理过滤器</description>
     3         <filter-name>AnnotationHandleFilter</filter-name>
     4         <filter-class>me.gacl.web.filter.AnnotationHandleFilter</filter-class>
     5         <init-param>
     6             <description>配置要扫描包及其子包, 如果有多个包,以逗号分隔</description>
     7             <param-name>basePackage</param-name>
     8             <param-value>me.gacl.web.controller,me.gacl.web.UI</param-value>
     9             <!-- <param-value>me.gacl.web.controller</param-value> -->
    10         </init-param>
    11     </filter>
    12     
    13     <filter-mapping>
    14         <filter-name>AnnotationHandleFilter</filter-name>
    15         <!-- 拦截后缀是.do的请求 -->
    16         <url-pattern>*.do</url-pattern>
    17     </filter-mapping>

  AnnotationHandleFilter过滤器初始化方法init(FilterConfig filterConfig)使用到了一个用于扫描某个包下面的类的工具类ScanClassUtil,ScanClassUtil的代码如下:

    1 package me.gacl.util;
      2 
      3 import java.io.File;
      4 import java.io.FileFilter;
      5 import java.io.IOException;
      6 import java.net.JarURLConnection;
      7 import java.net.URL;
      8 import java.net.URLDecoder;
      9 import java.util.Enumeration;
     10 import java.util.LinkedHashSet;
     11 import java.util.Set;
     12 import java.util.jar.JarEntry;
     13 import java.util.jar.JarFile;
     14 
     15 public class ScanClassUtil {
     16 
     17     /**
     18      * 从包package中获取所有的Class
     19      * 
     20      * @param pack
     21      * @return
     22      */
     23     public static Set<Class<?>> getClasses(String pack) {
     24 
     25         // 第一个class类的集合
     26         Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
     27         // 是否循环迭代
     28         boolean recursive = true;
     29         // 获取包的名字 并进行替换
     30         String packageName = pack;
     31         String packageDirName = packageName.replace('.', '/');
     32         // 定义一个枚举的集合 并进行循环来处理这个目录下的things
     33         Enumeration<URL> dirs;
     34         try {
     35             dirs = Thread.currentThread().getContextClassLoader().getResources(
     36                     packageDirName);
     37             // 循环迭代下去
     38             while (dirs.hasMoreElements()) {
     39                 // 获取下一个元素
     40                 URL url = dirs.nextElement();
     41                 // 得到协议的名称
     42                 String protocol = url.getProtocol();
     43                 // 如果是以文件的形式保存在服务器上
     44                 if ("file".equals(protocol)) {
     45                     System.err.println("file类型的扫描");
     46                     // 获取包的物理路径
     47                     String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
     48                     // 以文件的方式扫描整个包下的文件 并添加到集合中
     49                     findAndAddClassesInPackageByFile(packageName, filePath,
     50                             recursive, classes);
     51                 } else if ("jar".equals(protocol)) {
     52                     // 如果是jar包文件
     53                     // 定义一个JarFile
     54                     System.err.println("jar类型的扫描");
     55                     JarFile jar;
     56                     try {
     57                         // 获取jar
     58                         jar = ((JarURLConnection) url.openConnection())
     59                                 .getJarFile();
     60                         // 从此jar包 得到一个枚举类
     61                         Enumeration<JarEntry> entries = jar.entries();
     62                         // 同样的进行循环迭代
     63                         while (entries.hasMoreElements()) {
     64                             // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
     65                             JarEntry entry = entries.nextElement();
     66                             String name = entry.getName();
     67                             // 如果是以/开头的
     68                             if (name.charAt(0) == '/') {
     69                                 // 获取后面的字符串
     70                                 name = name.substring(1);
     71                             }
     72                             // 如果前半部分和定义的包名相同
     73                             if (name.startsWith(packageDirName)) {
     74                                 int idx = name.lastIndexOf('/');
     75                                 // 如果以"/"结尾 是一个包
     76                                 if (idx != -1) {
     77                                     // 获取包名 把"/"替换成"."
     78                                     packageName = name.substring(0, idx)
     79                                             .replace('/', '.');
     80                                 }
     81                                 // 如果可以迭代下去 并且是一个包
     82                                 if ((idx != -1) || recursive) {
     83                                     // 如果是一个.class文件 而且不是目录
     84                                     if (name.endsWith(".class")
     85                                             && !entry.isDirectory()) {
     86                                         // 去掉后面的".class" 获取真正的类名
     87                                         String className = name.substring(
     88                                                 packageName.length() + 1, name
     89                                                         .length() - 6);
     90                                         try {
     91                                             // 添加到classes
     92                                             classes.add(Class
     93                                                     .forName(packageName + '.'
     94                                                             + className));
     95                                         } catch (ClassNotFoundException e) {
     96                                             // log
     97                                             // .error("添加用户自定义视图类错误 找不到此类的.class文件");
     98                                             e.printStackTrace();
     99                                         }
    100                                     }
    101                                 }
    102                             }
    103                         }
    104                     } catch (IOException e) {
    105                         // log.error("在扫描用户定义视图时从jar包获取文件出错");
    106                         e.printStackTrace();
    107                     }
    108                 }
    109             }
    110         } catch (IOException e) {
    111             e.printStackTrace();
    112         }
    113 
    114         return classes;
    115     }
    116     
    117     /**
    118      * 以文件的形式来获取包下的所有Class
    119      * 
    120      * @param packageName
    121      * @param packagePath
    122      * @param recursive
    123      * @param classes
    124      */
    125     public static void findAndAddClassesInPackageByFile(String packageName,
    126             String packagePath, final boolean recursive, Set<Class<?>> classes) {
    127         // 获取此包的目录 建立一个File
    128         File dir = new File(packagePath);
    129         // 如果不存在或者 也不是目录就直接返回
    130         if (!dir.exists() || !dir.isDirectory()) {
    131             // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
    132             return;
    133         }
    134         // 如果存在 就获取包下的所有文件 包括目录
    135         File[] dirfiles = dir.listFiles(new FileFilter() {
    136             // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
    137             public boolean accept(File file) {
    138                 return (recursive && file.isDirectory())
    139                         || (file.getName().endsWith(".class"));
    140             }
    141         });
    142         // 循环所有文件
    143         for (File file : dirfiles) {
    144             // 如果是目录 则继续扫描
    145             if (file.isDirectory()) {
    146                 findAndAddClassesInPackageByFile(packageName + "."
    147                         + file.getName(), file.getAbsolutePath(), recursive,
    148                         classes);
    149             } else {
    150                 // 如果是java类文件 去掉后面的.class 只留下类名
    151                 String className = file.getName().substring(0,
    152                         file.getName().length() - 6);
    153                 try {
    154                     // 添加到集合中去
    155                     //classes.add(Class.forName(packageName + '.' + className));
    156                      //经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
    157                     classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));  
    158                     } catch (ClassNotFoundException e) {
    159                     // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
    160                     e.printStackTrace();
    161                 }
    162             }
    163         }
    164     }
    165 }

  经过以上两步,我们的自定义注解和针对注解的处理器都开发好了。

2.3、WebServlet注解简单测试

  编写一个用于跳转到Login.jsp页面的LoginUIServlet,LoginUIServlet就是一个普通的java类,不是一个真正的Servlet,然后使用WebServlet注解标注LoginUIServlet类,代码如下:

   1 package me.gacl.web.UI;
     2 
     3 import java.io.IOException;
     4 import javax.servlet.ServletException;
     5 import javax.servlet.http.HttpServletRequest;
     6 import javax.servlet.http.HttpServletResponse;
     7 import me.gacl.annotation.WebServlet;
     8 
     9 @WebServlet("/servlet/LoginUI")
    10 public class LoginUIServlet {
    11 
    12     public void doGet(HttpServletRequest request, HttpServletResponse response)
    13             throws ServletException, IOException{
    14         request.getRequestDispatcher("/Login.jsp").forward(request, response);
    15     }
    16     
    17     public void doPost(HttpServletRequest request, HttpServletResponse response)
    18             throws ServletException, IOException {
    19         doGet(request, response);
    20     }
    21 }

  在浏览器中输入访问地址: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方法,完成了一次客户端请求到服务器响应的过程。

  使用注解后程序流程如下所示:

  jiedefangshipeizhiservlet_2.png

  运行结果如下:

  jiedefangshipeizhiservlet_3.png

  从运行结果中可以看到,我们的注解处理器成功调用了目标Servlet处理用户的请求,通过@WebServlet注解, Servlet不用再在web.xml 文件中进行繁冗的注册,这就是使用@WebServlet注解的方便之处。

2.3、WebServlet注解复杂测试

  编写Login.jsp页面,代码如下:

   1 <%@ page language="java" pageEncoding="UTF-8"%>
     2 <!DOCTYPE HTML>
     3 <html>
     4   <head>
     5     <title>登录页面</title>
     6   </head>
     7   
     8   <body>
     9     <fieldset>
    10         <legend>用户登录</legend>
    11         <form action="${pageContext.request.contextPath}/servlet/LoginServlet!loginHandle.do" method="post">
    12             用户名:<input type="text" value="${param.usename}" name="usename">
    13             <br/>
    14             密码:<input type="text" value="${param.pwd}" name="pwd">
    15             <br/>
    16             <input type="submit" value="登录"/>
    17         </form>
    18     </fieldset>
    19     <hr/>
    20     <label style="color: red;">${msg}</label>
    21   </body>
    22 </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的代码如下:

   1 package me.gacl.web.controller;
     2 
     3 import java.io.IOException;
     4 import java.util.Map;
     5 import javax.servlet.ServletException;
     6 import javax.servlet.http.HttpServletRequest;
     7 import javax.servlet.http.HttpServletResponse;
     8 import me.gacl.annotation.WebInitParam;
     9 import me.gacl.annotation.WebServlet;
    10 
    11 /**
    12 * 
    13 * @ClassName: LoginServlet
    14 * @Description:处理用户登录的Servlet,
    15 * LoginServlet现在就是一个普通的java类,不是一个真正的Servlet
    16 * @author: 孤傲苍狼
    17 * @date: 2014-10-8 上午12:07:58
    18 *
    19 */
    20 //将开发好的WebServlet注解标注到LoginServlet类上
    21 @WebServlet(
    22             //Servlet的访问URL
    23             value="/servlet/LoginServlet",
    24             //Servlet的访问URL,可以使用数组的方式配置多个访问路径
    25             urlPatterns={"/gacl/LoginServlet","/xdp/LoginServlet"},
    26             //Servlet的初始化参数
    27             initParams={
    28                     @WebInitParam(paramName="gacl",paramValue="孤傲苍狼"),
    29                     @WebInitParam(paramName="bhsh",paramValue="白虎神皇")
    30             },
    31             name="LoginServlet",
    32             description="处理用户登录的Servlet"
    33         )
    34 public class LoginServlet {
    35 
    36     public void loginHandle(HttpServletRequest request, HttpServletResponse response) 
    37             throws ServletException, IOException{
    38         String username = request.getParameter("usename");
    39         String pwd = request.getParameter("pwd");
    40         if (username.equals("gacl") && pwd.equals("xdp")) {
    41             request.getSession().setAttribute("usename", username);
    42             request.setAttribute("msg", "欢迎您!"+username);
    43             request.getRequestDispatcher("/index.jsp").forward(request, response);
    44         }else {
    45             request.setAttribute("msg", "登录失败,请检查用户名和密码是否正确!");
    46             request.getRequestDispatcher("/Login.jsp").forward(request, response);
    47         }
    48     }
    49     
    50     
    51     /**
    52     * @Method: init
    53     * @Description: Servlet初始化
    54     * @Anthor:孤傲苍狼
    55     *
    56     * @param config
    57     */ 
    58     public void init(Map<String, String> initParamMap){
    59         System.out.println("--LoginServlet初始化--");
    60         System.out.println(initParamMap.get("gacl"));
    61         System.out.println(initParamMap.get("bhsh"));
    62     }
    63 }

  运行结果如下:

  jiedefangshipeizhiservlet_4.gif

  可以看到,我们使用注解方式配置的Servlet已经成功调用了,loginHandle方法处理用户登录请求的完整处理过程如下图所示:

  jiedefangshipeizhiservlet_5.gif

  Servlet3.0是支持采用基于注解的方式配置Servlet的,在此我使用过滤器作为注解处理器模拟模拟出了类似Servlet3.0的注解处理方式,简化了Servlet的配置。这种使用自定义注解+注解处理器的方式山寨出来的Servlet3.0大家了解一下即可,了解一下这种处理思路,在实际应用中还是不要这么做了,要真想使用注解的方式配置Servlet还是直接用Servlet3.0吧。

作者:孤傲苍狼

来源:https://www.cnblogs.com/xdp-gacl/p/4010328.html


看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「方志朋」,公众号后台回复「666」 免费领取我精心整理的进阶资源教程
  4. JS中文网,Javascriptc中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,是给开发者用的 Hacker News,技术文章由为你筛选出最优质的干货,其中包括:Android、iOS、前端、后端等方面的内容。目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。

    本文著作权归作者所有,如若转载,请注明出处

    转载请注明:文章转载自「 Java极客技术学习 」https://www.javajike.com

    标题:JavaWeb学习总结(四十八)——模拟Servlet3.0使用注解的方式配置Servlet

    链接:https://www.javajike.com/article/1146.html

« JavaWeb学习总结(十二)——Session
javaweb学习总结(三十一)——国际化(i18n)»

相关推荐

QR code