JavaWeb开发与代码的编写(二十)
Servlet3
Servlet的传统配置方式
在JavaWeb开发中, 每次编写一个Servlet都需要在web.xml文件中进行配置,如下所示:
ActionServlet
me.gacl.web.controller.ActionServlet
ActionServlet
/servlet/ActionServlet
每开发一个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上,再针对我们自定义的注解写一个注解处理器,具体的做法如下:
开发用于配置Servlet的相关注解
1、开发WebServlet注解,用于标注处理请求的Servlet类
package me.gacl.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义WebServlet注解,模拟Servlet3.0的WebServlet注解
* @Target 注解的属性值表明了 @WebServlet注解只能用于类或接口定义声明的前面,
* @WebServlet注解有一个必填的属性 value 。
* 调用方式为: @WebServlet(value="/xxxx") ,
* 因语法规定如果属性名为 value 且只填 value属性值时,可以省略 value属性名,即也可以写作:@WebServlet("/xxxx")
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface WebServlet {
//Servlet的访问URL
String value();
//Servlet的访问URL
String[] urlPatterns() default {""};
//Servlet的描述
String description() default "";
//Servlet的显示名称
String displayName() default "";
//Servlet的名称
String name() default "";
//Servlet的init参数
WebInitParam[] initParams() default {};
}
将Servlet在web.xml中的配置信息使用WebServlet注解来表示,使用注解后,只需要在相应Servlet 类的前面使用类似@WebServlet("/servlet/LoginServlet") 注解就可以达到和上述 web.xml 文件中配置信息一样的目的。注解@WebServlet中的属性值"/servlet/LoginServlet"表示了web.xml 配置文件中 元素的子元素 里的值。通过这样的注解能简化在 XML 文件中配置 Servlet 信息,整个配置文件将会非常简洁干净,开发人员的工作也将大大减少。
2、开发WebInitParam注解,用于配置Servlet初始化时使用的参数
package me.gacl.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName: WebInitParam
* @Description: 定义Servlet的初始化参数注解
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface WebInitParam {
//参数名
String paramName() default "";
//参数的值
String paramValue() default "";
}
编写处理注解的处理器
上面简要地介绍了注解的定义声明与使用方式,注解在后台需要一个处理器才能起作用,所以还得针对上面的注解编写处理器,在这里我们使用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作为注解的处理器
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>();
41 //获取web.xml中配置的要扫描的包
42 String basePackage = filterConfig.getInitParameter("basePackage");
43 //如果配置了多个包,例如:me.gacl.web.controller,me.gacl.web.UI
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中
60 *
61 * @param packageName
62 * @param classMap
63 */
64 private void addServletClassToServletContext(String packageName,Map 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 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 initParamMap = new HashMap();
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过滤器和需要扫描的包
注解处理过滤器
AnnotationHandleFilter
me.gacl.web.filter.AnnotationHandleFilter
配置要扫描包及其子包, 如果有多个包,以逗号分隔
basePackage
me.gacl.web.controller,me.gacl.web.UI
AnnotationHandleFilter
*.do
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> classes = new LinkedHashSet
ServletFilter
com.ys.filter.ServletFilter
ServletFilter
*.do
2、创建一个 UserServlet,里面有两个方法,insert()和update()方法,调用 insert() 方法会跳转到 insert.jsp 页面,调用 update() 方法会调转到 update.jsp 页面
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UserServlet {
//用户插入方法
public void insert(HttpServletRequest req,HttpServletResponse resp) throws Exception, IOException{
req.getRequestDispatcher("insert.jsp").forward(req, resp);
}
//用户更新方法
public void update(HttpServletRequest req,HttpServletResponse resp) throws Exception, IOException{
req.getRequestDispatcher("update.jsp").forward(req, resp);
}
}
3、创建一个配置文件类,里面存放配置文件的关系,通过一个 Map 集合,保存 Servlet 的类名和全类名
import java.util.HashMap;
import java.util.Map;
public class ServletNameConfig {
//定义一个 Servlet 配置文件,Map
//key:表示 Servlet 的类名
//value:表示 Servlet 类名的全称
public static Map servletMap = new HashMap();
static {
servletMap.put("UserServlet", "com.ys.servlet.UserServlet");
}
}
回头看我们配置的过滤器,ServletFilter
import java.io.IOException;
import java.lang.reflect.Method;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ys.config.ServletNameConfig;
public class ServletFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String reqURL = req.getRequestURI(); // /ServletIncreased/UserServlet.do
String[] strs = reqURL.split("/");
//定义 Servlet 的全类名
String servletAllName = null;
if(strs[2] != null){
//得到 请求路径的 servlet 类名
String servletName = strs[2].substring(0, strs[2].indexOf("."));
//根据获取的 Servlet 类名,由配置文件 ServletNameConfig 里面的map 得到 全类名
servletAllName = ServletNameConfig.servletMap.get(servletName);
}
//获取请求方法名
String methodName = req.getParameter("method");
System.out.println(servletAllName+"---"+methodName);
try {
//通过反射调用执行方法
Class obj = Class.forName(servletAllName);
Method method = obj.getDeclaredMethod
(methodName, HttpServletRequest.class,HttpServletResponse.class);
method.invoke(obj.newInstance(), req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
整体的项目结构如下:
然后将整个项目发布到 tomcat 服务器运行,发布的方法可以如下:
然后我们在浏览器输入如下链接:http://localhost:8080/ServletIncreased/UserServlet.do?method=insert
那么就会调用 UserServlet 的 insert 方法,进而跳转到 insert.jsp 页面
如果我们在浏览器输入如下链接:将 insert 改为 update
http://localhost:8080/ServletIncreased/UserServlet.do?method=update
那么就会调用 UserServlet 的update 方法,进而调转到 update.jsp 页面
分析:这个改进主要是配置了一个过滤器,然后通过过滤器的 doFilter() 方法,我们可以通过请求路径获得请求URL,然后通过字符串的截取方法得到 Servlet 的名称。通过配置文件保存的 Servlet类名和全类名的对应关系得到全类名;然后利用反射的原理,通过 invoke() 方法来动态调用方法。这里我们并没有解决上面所有的问题,比如严重的容器依赖性我们这里还有。
Struts2
Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts 2是Struts的下一代产品,是
struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架。其全新的Struts 2的体系结构与Struts 1的体系结构差别巨大。Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的
设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以Struts 2可以理解为WebWork的更新产品。虽然从Struts 1到Struts 2有着太大的变化,但是相对于WebWork,Struts 2的变化很小。
第一步:创建一个 web 工程,并将相应的 jar 包复制到 lib 目录下
第二步:在 WEB-INF 目录下,创建 web.xml 文件,并添加 struts2 拦截器
struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts2
/*
第三步:在 src 目录下,创建一个Java文件,HelloWorldAction
public class HelloWorldAction {
public String execute(){
System.out.println("执行Action.....");
return "success";
}
}
第四步:在 src 目录下,创建 struts.xml 文件,并添加如下代码
success.jsp
第五步:我们创建一个 index.jsp 文件,然后点击页面上的超链接,跳转到 success.jsp 页面
index.jsp 页面:
Insert title here
点击跳转到 success.jsp 页面
success.jsp页面
Insert title here
success.jsp
验证:我们将项目发布到 tomcat 服务器,然后输入链接:http://localhost:8080/HelloStruts2/index.jsp
然后鼠标点击 超链接,发现页面跳转到 如下界面:
struts2 执行流程
tomcat 服务器启动时做的工作
①、加载 web.xml 文件配置的过滤器,调用过滤器的 init()方法,初始化所有资源文件,主要包括 default.properties 文件,struts-default.xml,strut-plugin.xml,struts.xml 文件
struts2 执行流程
1、客户端初始化一个指向Servlet容器(例如Tomcat)的请求;
2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin);
3、接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action;
4、如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy;
5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类;
6、ActionProxy创建一个ActionInvocation的实例。
7、ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2框架中继承的标签。在这个过程中需要涉及到ActionMapper。
简单模拟spring MVC
在Spring MVC中,将一个普通的java类标注上Controller注解之后,再将类中的方法使用RequestMapping注解标注,那么这个普通的java类就够处理Web请求,示例代码如下:
/**
* 使用Controller注解标注LoginUI类
*/
@Controller
public class LoginUI {
//使用RequestMapping注解指明forward1方法的访问路径
@RequestMapping("LoginUI/Login2")
public View forward1(){
//执行完forward1方法之后返回的视图
return new View("/login2.jsp");
}
//使用RequestMapping注解指明forward2方法的访问路径
@RequestMapping("LoginUI/Login3")
public View forward2(){
//执行完forward2方法之后返回的视图
return new View("/login3.jsp");
}
}
spring通过java annotation就可以注释一个类为action ,在方法上添加上一个java annotation 就可以配置请求的路径了,那么这种机制是如何实现的呢,今天我们使用"自定义注解+Servlet"来简单模拟一下Spring MVC中的这种注解配置方式。
编写注解
Controller注解
开发Controller注解,这个注解只有一个value属性,默认值为空字符串,代码如下:
package me.gacl.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName: Controller
* @Description: 自定义Controller注解
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Controller {
public String value() default "";
}
RequestMapping注解
开发RequestMapping注解,用于定义请求路径,这个注解只有一个value属性,默认值为空字符串,代码如下:
package me.gacl.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 定义请求路径的java annotation
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
public String value() default "";
}
以上就是我们自定义的两个注解,注解的开发工作就算是完成了,有了注解之后,那么就必须针对注解来编写处理器,否则我们开发的注解配置到类或者方法上面是不起作用的,这里我们使用Servlet来作为注解的处理器。
编写核心的注解处理器
开发AnnotationHandleServlet
这里使用一个Servlet来作为注解处理器,编写一个AnnotationHandleServlet,代码如下:
1 package me.gacl.web.controller;
2
3 import java.io.IOException;
4 import java.lang.reflect.InvocationTargetException;
5 import java.lang.reflect.Method;
6 import java.util.Set;
7 import javax.servlet.ServletConfig;
8 import javax.servlet.ServletException;
9 import javax.servlet.http.HttpServlet;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12 import me.gacl.annotation.Controller;
13 import me.gacl.annotation.RequestMapping;
14 import me.gacl.util.BeanUtils;
15 import me.gacl.util.RequestMapingMap;
16 import me.gacl.util.ScanClassUtil;
17 import me.gacl.web.context.WebContext;
18 import me.gacl.web.view.DispatchActionConstant;
19 import me.gacl.web.view.View;
20
21 /**
22 * ClassName: AnnotationHandleServlet
23 *
Description: AnnotationHandleServlet作为自定义注解的核心处理器以及负责调用目标业务方法处理用户请求
24 * @author xudp
25 * @version 1.0 V
26 */
27 public class AnnotationHandleServlet extends HttpServlet {
28
29 private String pareRequestURI(HttpServletRequest request){
30 String path = request.getContextPath()+"/";
31 String requestUri = request.getRequestURI();
32 String midUrl = requestUri.replaceFirst(path, "");
33 String lasturl = midUrl.substring(0, midUrl.lastIndexOf("."));
34 return lasturl;
35 }
36
37 public void doGet(HttpServletRequest request, HttpServletResponse response)
38 throws ServletException, IOException {
39 this.excute(request, response);
40 }
41
42 public void doPost(HttpServletRequest request, HttpServletResponse response)
43 throws ServletException, IOException {
44 this.excute(request, response);
45 }
46
47 private void excute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
48 //将当前线程中HttpServletRequest对象存储到ThreadLocal中,以便在Controller类中使用
49 WebContext.requestHodler.set(request);
50 //将当前线程中HttpServletResponse对象存储到ThreadLocal中,以便在Controller类中使用
51 WebContext.responseHodler.set(response);
52 //解析url
53 String lasturl = pareRequestURI(request);
54 //获取要使用的类
55 Class clazz = RequestMapingMap.getRequesetMap().get(lasturl);
56 //创建类的实例
57 Object classInstance = BeanUtils.instanceClass(clazz);
58 //获取类中定义的方法
59 Method [] methods = BeanUtils.findDeclaredMethods(clazz);
60 Method method = null;
61 for(Method m:methods){//循环方法,找匹配的方法进行执行
62 if(m.isAnnotationPresent(RequestMapping.class)){
63 String anoPath = m.getAnnotation(RequestMapping.class).value();
64 if(anoPath!=null && !"".equals(anoPath.trim()) && lasturl.equals(anoPath.trim())){
65 //找到要执行的目标方法
66 method = m;
67 break;
68 }
69 }
70 }
71 try {
72 if(method!=null){
73 //执行目标方法处理用户请求
74 Object retObject = method.invoke(classInstance);
75 //如果方法有返回值,那么就表示用户需要返回视图
76 if (retObject!=null) {
77 View view = (View)retObject;
78 //判断要使用的跳转方式
79 if(view.getDispathAction().equals(DispatchActionConstant.FORWARD)){
80 //使用服务器端跳转方式
81 request.getRequestDispatcher(view.getUrl()).forward(request, response);
82 }else if(view.getDispathAction().equals(DispatchActionConstant.REDIRECT)){
83 //使用客户端跳转方式
84 response.sendRedirect(request.getContextPath()+view.getUrl());
85 }else{
86 request.getRequestDispatcher(view.getUrl()).forward(request, response);
87 }
88 }
89 }
90 } catch (IllegalArgumentException e) {
91 e.printStackTrace();
92 } catch (IllegalAccessException e) {
93 e.printStackTrace();
94 } catch (InvocationTargetException e) {
95 e.printStackTrace();
96 }
97 }
98
99 @Override
100 public void init(ServletConfig config) throws ServletException {
101 /**
102 * 重写了Servlet的init方法后一定要记得调用父类的init方法,
103 * 否则在service/doGet/doPost方法中使用getServletContext()方法获取ServletContext对象时
104 * 就会出现java.lang.NullPointerException异常
105 */
106 super.init(config);
107 System.out.println("---初始化开始---");
108 //获取web.xml中配置的要扫描的包
109 String basePackage = config.getInitParameter("basePackage");
110 //如果配置了多个包,例如:me.gacl.web.controller,me.gacl.web.UI
111 if (basePackage.indexOf(",")>0) {
112 //按逗号进行分隔
113 String[] packageNameArr = basePackage.split(",");
114 for (String packageName : packageNameArr) {
115 initRequestMapingMap(packageName);
116 }
117 }else {
118 initRequestMapingMap(basePackage);
119 }
120 System.out.println("----初始化结束---");
121 }
122
123 /**
124 * @Method: initRequestMapingMap
125 * @Description:添加使用了Controller注解的Class到RequestMapingMap中
127 * @param packageName
128 */
129 private void initRequestMapingMap(String packageName){
130 Set> requesetMap = new HashMap getClassName(String path) {
return requesetMap.get(path);
}
public static void put(String path, Class className) {
requesetMap.put(path, className);
}
public static Map> getClasses(String pack) {
31
32 // 第一个class类的集合
33 Set>();
34 // 是否循环迭代
35 boolean recursive = true;
36 // 获取包的名字 并进行替换
37 String packageName = pack;
38 String packageDirName = packageName.replace('.', '/');
39 // 定义一个枚举的集合 并进行循环来处理这个目录下的things
40 Enumeration dirs;
41 try {
42 dirs = Thread.currentThread().getContextClassLoader().getResources(
43 packageDirName);
44 // 循环迭代下去
45 while (dirs.hasMoreElements()) {
46 // 获取下一个元素
47 URL url = dirs.nextElement();
48 // 得到协议的名称
49 String protocol = url.getProtocol();
50 // 如果是以文件的形式保存在服务器上
51 if ("file".equals(protocol)) {
52 System.err.println("file类型的扫描");
53 // 获取包的物理路径
54 String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
55 // 以文件的方式扫描整个包下的文件 并添加到集合中
56 findAndAddClassesInPackageByFile(packageName, filePath,
57 recursive, classes);
58 } else if ("jar".equals(protocol)) {
59 // 如果是jar包文件
60 // 定义一个JarFile
61 System.err.println("jar类型的扫描");
62 JarFile jar;
63 try {
64 // 获取jar
65 jar = ((JarURLConnection) url.openConnection())
66 .getJarFile();
67 // 从此jar包 得到一个枚举类
68 Enumeration entries = jar.entries();
69 // 同样的进行循环迭代
70 while (entries.hasMoreElements()) {
71 // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
72 JarEntry entry = entries.nextElement();
73 String name = entry.getName();
74 // 如果是以/开头的
75 if (name.charAt(0) == '/') {
76 // 获取后面的字符串
77 name = name.substring(1);
78 }
79 // 如果前半部分和定义的包名相同
80 if (name.startsWith(packageDirName)) {
81 int idx = name.lastIndexOf('/');
82 // 如果以"/"结尾 是一个包
83 if (idx != -1) {
84 // 获取包名 把"/"替换成"."
85 packageName = name.substring(0, idx)
86 .replace('/', '.');
87 }
88 // 如果可以迭代下去 并且是一个包
89 if ((idx != -1) || recursive) {
90 // 如果是一个.class文件 而且不是目录
91 if (name.endsWith(".class")
92 && !entry.isDirectory()) {
93 // 去掉后面的".class" 获取真正的类名
94 String className = name.substring(
95 packageName.length() + 1, name
96 .length() - 6);
97 try {
98 // 添加到classes
99 classes.add(Class
100 .forName(packageName + '.'
101 + className));
102 } catch (ClassNotFoundException e) {
103 // log
104 // .error("添加用户自定义视图类错误 找不到此类的.class文件");
105 e.printStackTrace();
106 }
107 }
108 }
109 }
110 }
111 } catch (IOException e) {
112 // log.error("在扫描用户定义视图时从jar包获取文件出错");
113 e.printStackTrace();
114 }
115 }
116 }
117 } catch (IOException e) {
118 e.printStackTrace();
119 }
120
121 return classes;
122 }
123
124 /**
125 * 以文件的形式来获取包下的所有Class
126 *
127 * @param packageName
128 * @param packagePath
129 * @param recursive
130 * @param classes
131 */
132 public static void findAndAddClassesInPackageByFile(String packageName,
133 String packagePath, final boolean recursive, Set
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?