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 [email protected](“/servlet/LoginServlet”) 注解就可以达到和上述 web.xml [email protected]性值”/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对象中。

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

  在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方法,完成了一次客户端请求到服务器响应的过程。

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

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

  运行结果如下:

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

  从运行结果中可以看到,我们的注解处理器成功调用了目标Servlet处理用户的请求,[email protected], Servlet不用再在web.xml 文件中进行繁冗的注册,[email protected]

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 }

  运行结果如下:

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

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

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

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

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/11365.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论