- 浏览: 1048084 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (1441)
- 软件思想&演讲 (9)
- 行业常识 (250)
- 时时疑问 (5)
- java/guava/python/php/ruby/R/scala/groovy (213)
- struct/spring/springmvc (37)
- mybatis/hibernate/JPA (10)
- mysql/oracle/sqlserver/db2/mongdb/redis/neo4j/GreenPlum/Teradata/hsqldb/Derby/sakila (268)
- js/jquery/jqueryUi/jqueryEaseyUI/extjs/angulrJs/react/es6/grunt/zepto/raphael (81)
- ZMQ/RabbitMQ/ActiveMQ/JMS/kafka (17)
- lucene/solr/nuth/elasticsearch/MG4J (167)
- html/css/ionic/nodejs/bootstrap (19)
- Linux/shell/centos (56)
- cvs/svn/git/sourceTree/gradle/ant/maven/mantis/docker/Kubernetes (26)
- sonatype nexus (1)
- tomcat/jetty/netty/jboss (9)
- 工具 (17)
- ETL/SPASS/MATLAB/RapidMiner/weka/kettle/DataX/Kylin (11)
- hadoop/spark/Hbase/Hive/pig/Zookeeper/HAWQ/cloudera/Impala/Oozie (190)
- ios/swift/android (9)
- 机器学习&算法&大数据 (18)
- Mesos是Apache下的开源分布式资源管理框架 (1)
- echarts/d3/highCharts/tableau (1)
- 行业技能图谱 (1)
- 大数据可视化 (2)
- tornado/ansible/twisted (2)
- Nagios/Cacti/Zabbix (0)
- eclipse/intellijIDEA/webstorm (5)
- cvs/svn/git/sourceTree/gradle/jira/bitbucket (4)
- jsp/jsf/flex/ZKoss (0)
- 测试技术 (2)
- splunk/flunm (2)
- 高并发/大数据量 (1)
- freemarker/vector/thymeleaf (1)
- docker/Kubernetes (2)
- dubbo/ESB/dubboX/wso2 (2)
最新评论
一、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对象中。
在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方法,完成了一次客户端请求到服务器响应的过程。
使用注解后程序流程如下所示:
运行结果如下:
从运行结果中可以看到,我们的注解处理器成功调用了目标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 }
复制代码
运行结果如下:
可以看到,我们使用注解方式配置的Servlet已经成功调用了,loginHandle方法处理用户登录请求的完整处理过程如下图所示:
Servlet3.0是支持采用基于注解的方式配置Servlet的,在此我使用过滤器作为注解处理器模拟模拟出了类似Servlet3.0的注解处理方式,简化了Servlet的配置。这种使用自定义注解+注解处理器的方式山寨出来的Servlet3.0大家了解一下即可,了解一下这种处理思路,在实际应用中还是不要这么做了,要真想使用注解的方式配置Servlet还是直接用Servlet3.0吧。
在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对象中。
在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方法,完成了一次客户端请求到服务器响应的过程。
使用注解后程序流程如下所示:
运行结果如下:
从运行结果中可以看到,我们的注解处理器成功调用了目标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 }
复制代码
运行结果如下:
可以看到,我们使用注解方式配置的Servlet已经成功调用了,loginHandle方法处理用户登录请求的完整处理过程如下图所示:
Servlet3.0是支持采用基于注解的方式配置Servlet的,在此我使用过滤器作为注解处理器模拟模拟出了类似Servlet3.0的注解处理方式,简化了Servlet的配置。这种使用自定义注解+注解处理器的方式山寨出来的Servlet3.0大家了解一下即可,了解一下这种处理思路,在实际应用中还是不要这么做了,要真想使用注解的方式配置Servlet还是直接用Servlet3.0吧。
发表评论
-
20180222积累
2018-02-22 09:34 4781. mybatis如何通过接口查找对应的mapper. ... -
20180208积累
2018-02-08 10:28 465临时表与永久表相似,但临时表存储在 tempdb 中,当不 ... -
行业应用
2018-01-30 16:30 485git clone的时候用上面那个IP地址,下面栏中的不能 ... -
SQLite 数据库
2018-01-29 22:57 755android: SQLite创建数据 ... -
java里面获取map的key和value的方法
2018-02-01 11:29 2158获取map的key和value的方法分为两种形式: ma ... -
Eclipse中Maven WEB工程tomcat项目添加调试以及项目发布细节记录
2018-02-23 21:11 725一、建立一个maven WEB项目 1、file-&g ... -
错误:HttpServlet was not found on the Java
2018-02-23 21:12 381我们在用Eclipse进行Java web ... -
使用 java8 实现List到Array的转换
2018-02-23 21:13 2988开发中需要调用第三方的库,有些 API 的入参要求是 do ... -
Java8 利用Lambda处理List集合
2018-01-11 09:58 5630Java 8新增的Lambda表达式,我们可以很方便地并行操 ... -
java中string与json互相转化
2018-01-11 09:40 1076在Java中socket传输数据时,数据类型往往比较难选择。 ... -
JSON 数据格式
2018-01-11 09:37 474JSON(JavaScript Object Notatio ... -
java怎么读取json格式的数据
2018-01-11 09:46 1059java可以使用JSONObject和JSONArray来操作 ... -
Java8-如何将List转变为逗号分隔的字符串
2018-01-10 10:13 1988Converting a List to a String ... -
eclipse maven 打war包的两种方式
2018-02-23 21:25 703第一种:利用pom.xml文件打包。 右键pom.xml ... -
Annotation(三)——Spring注解开发
2018-02-28 09:21 428Spring框架的核心功能IoC(Inversion o ... -
Spring自定义注解
2018-02-28 09:32 594java注解:附在代码中的一些元信息,用于在编译、运行时起 ... -
Java项目
2018-01-08 10:56 0这两种解决办法已经能完全解决问题,不过值得注意的一点是,我 ... -
解决Eclipse建立Maven项目后无法建立src/main/java资源文件夹的办法
2018-03-22 10:41 1130在Eclipse中建立好一个Maven项目后,如果Java ... -
Java @override报错的解决方法
2018-01-07 12:56 0有时候Java的Eclipse工程换一台电脑后编译总是@ove ... -
Java 8 配置Maven-javadoc-plugin
2018-01-07 09:07 1040在升级JDK至1.8之后,使用Maven-javadoc- ...
相关推荐
### Servlet 3.0 使用注解标注过滤器 #### 一、Servlet 3.0概述与新特性 Servlet 3.0 是 Java EE 6 的一部分,它引入了许多新的特性和改进,使得开发者能够更方便地开发 Web 应用程序。其中一个重要特性就是支持...
Servlet3.0引入了注解,允许开发者直接在类或方法上使用`@WebServlet`、`@WebFilter`和`@WebListener`注解来声明它们,使得代码更加简洁,也更易于维护。 其次,Servlet3.0引入了异步处理能力。通过实现`...
1. **注解驱动的配置**:在Servlet 3.0中,开发者可以使用注解(如`@WebServlet`, `@WebFilter`, `@WebListener`)来声明和配置Servlet、Filter和Listener,而不再需要在web.xml中进行繁琐的手动配置。这使得部署...
在Servlet3.0中,我们可以使用注解(@WebServlet、@WebFilter、@WebListener)来替代web.xml中的XML配置,使得部署更简洁。例如,`@WebServlet("/example")`可以直接在Servlet类上声明URL映射。 2. **异步处理**:...
下载的`servlet3.0jar综合`压缩包包含了实现这些特性的关键库,对于进行Java EE 3.0开发的程序员来说是必不可少的工具。在实际开发中,结合相关的框架(如Spring MVC)和其他Java EE组件,可以构建出功能强大、易...
1. **Servlet 3.0特性**: Servlet 3.0引入了注解配置,可以直接在Servlet类上使用`@WebServlet`注解声明URL映射,不再需要web.xml配置文件。 2. **异步处理**: Servlet 3.0允许开发者编写异步Servlet,通过`...
例如,使用Servlet3.0的注解功能,可以简化Servlet、过滤器和监听器的配置;借助JSTL,可以编写出更加简洁的JSP页面,减少Java代码的嵌入,提升代码的可读性。 在实际开发中,将这些库导入到项目中,开发者可以利用...
而Servlet 3.0允许使用注解(如`@WebServlet`,`@WebFilter`)直接在类上声明,简化了配置过程。 2. **异步处理**:Servlet 3.0引入了异步处理能力,允许Servlet在处理请求时启动一个后台线程,从而提高服务器性能...
这个压缩包包含了servlet3.0的jar包和对应的doc文档,非常适合开发者学习和使用。 首先,我们来看一下`javax.servlet.jar`文件,这是Servlet API的核心库,包含了Servlet和Filter等核心接口和类。在Servlet3.0中,...
Servlet 3.0是Java Servlet规范的一个重要...总的来说,“servlet3.0.zip”包含的资源为开发者提供了使用Servlet 3.0规范进行Web开发的基础,通过学习和实践,开发者可以构建高效、灵活且易于维护的Java Web应用程序。
总结起来,"servlet3.0-demo"项目展示了Servlet 3.0的关键特性,包括使用注解声明Servlet、处理文件上传以及异步处理。这些改进极大地提升了开发效率,使得Servlet API更加现代化和易于使用。通过学习这个示例,...
2. **注解驱动的配置**:相比于之前的版本,Servlet 3.0允许使用注解来配置Servlet、Filter和Listener,无需在web.xml中进行繁琐的XML配置。例如,`@WebServlet`、`@WebFilter`和`@WebListener`注解分别用于声明...
在实际开发中,我们可以使用Servlet 3.0的异步特性来处理WebSocket连接的生命周期,例如在接收到新消息时,通过异步上下文将消息分发给所有在线的WebSocket连接。同时,我们还需要一个后台服务来处理聊天室的消息...
【标题】"Jsp/Servlet3.0 ppt和代码"涉及的是Java服务器页面(JSP)和Servlet 3.0版本的关键技术。JSP是一种动态网页技术,它允许开发者将HTML代码与Java代码混合编写,从而实现服务器端的动态内容生成。而Servlet是...
最后,关于"链接.txt"、"创建MavenWeb项目Servlet3.0的模板"和"Idea安装插件"这三个文件,它们可能是教程的补充资料。"链接.txt"可能包含其他相关资源的URL;"创建MavenWeb项目Servlet3.0的模板"可能是一个详细的...
而Servlet3.0则引入了注解驱动的监听器注册,开发者可以直接在类上使用`@WebListener`注解来声明监听器,无需再依赖web.xml。这使得监听器的定义更加简洁,也更易于管理和维护。 例如,我们可以创建一个监听...
Servlet 3.0 还增加了对注解的支持,这为简化 Servlet、过滤器(Filter)和监听器(Listener)的声明提供了便利。在之前的版本中,Servlet、过滤器和监听器都需要在 web.xml 文件中进行声明式配置,而在 Servlet 3.0 中...
Servlet3.0引入了注解配置,允许开发者无需在web.xml文件中进行繁琐的配置,可以直接在Servlet类上使用`@WebServlet`注解来声明和映射Servlet。例如: ```java @WebServlet("/hello") public class ...
Apache Tomcat 7.0.61是广泛使用的开源Servlet容器,它实现了Servlet 3.0规范,提供了稳定、高性能的运行环境。 一、Servlet 3.0新特性 1. 注解支持:Servlet 3.0引入了注解(Annotation)来声明Servlet、Filter和...
首先,Servlet 3.0引入了注解配置,这大大减少了XML配置文件的使用。现在,我们可以在Servlet类上直接使用`@WebServlet`注解来声明和配置Servlet,比如: ```java @WebServlet("/example") public class ...