对 Servlet 的改进
通过上一篇博客:Servlet 的详解 http://www.cnblogs.com/ysocean/p/6912191.html,我们大致知道了 Servlet 的基本用法。但是稍微分析一下 Servlet 的用法,我们还是发现其存在很多缺点:
①、一个请求对应一个 Servlet,即每一个请求我们都需要在 web.xml 文件中配置映射。如果项目大,请求很多,那么会造成 web.xml 很大,很难维护。
②、即便在好几个请求对应一个 Servlet,即在 service() 方法中,通过 if--else 语句来判断执行的代码块。那这样就会造成 service() 方法很拥挤。
③、一个项目只存在一个 web.xml 文件,如果一个项目是多人开发,那么整合代码开发过程中会有很多问题。不适合团队开发。
④、Servlet中doGet方法和doPost方法中的两个参数reqeust,response拥有严重的容器依赖性。
⑤、如果页面上表单中的元素比较复杂,则在Servlet的方法中获取表单元素的数据比较繁琐。
⑥、Servlet是单线程的,只要在Servlet中的声明一个实例变量,那么该变量在多线程访问时就会有线程安全问题。
⑦、在Servlet中处理异常,如果Servlet中有N个方法,则这N个方法必须都要try--catch。因为子类抛的异常不能大于父类。
那么接下来我们用一个例子来解决上面的问题。
1、新建一个 Web 工程,名为 ServletIncreased。并在 web.xml 中配置一个过滤器 ServletFilter,这个过滤器会过滤所有以 .do 结尾的 URL 链接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?xml version= "1.0" encoding= "UTF-8" ?> <web-app xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns= "http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http: //java.sun.com/xml/ns/javaee http: //java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id= "WebApp_ID" version= "3.0" > <filter> <filter-name>ServletFilter</filter-name> <filter- class >com.ys.filter.ServletFilter</filter- class > </filter> <filter-mapping> <filter-name>ServletFilter</filter-name> <url-pattern>*. do </url-pattern> </filter-mapping> </web-app> |
2、创建一个 UserServlet,里面有两个方法,insert()和update()方法,调用 insert() 方法会跳转到 insert.jsp 页面,调用 update() 方法会调转到 update.jsp 页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.ys.servlet; 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 的类名和全类名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.ys.config; import java.util.HashMap; import java.util.Map; public class ServletNameConfig { //定义一个 Servlet 配置文件,Map<key,value> //key:表示 Servlet 的类名 //value:表示 Servlet 类名的全称 public static Map<String, String> servletMap = new HashMap<>(); static { servletMap.put( "UserServlet" , "com.ys.servlet.UserServlet" ); } } |
4、回头看我们配置的过滤器,ServletFilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
package com.ys.filter; 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://www.cnblogs.com/ysocean/p/6893446.html
然后我们在浏览器输入如下链接: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 详解