提要 在原生 Servlet 开发中,如果采用一个 url 对应一个 servlet-mapping,那么 web.xml 将会十分冗长难以维护。其实,我们其实可以通过 Filter + 反射 来 使 一个 servlet 处理 多个 url ,并且根据不同 url 调用到 servlet 中的不同方法 (类似 SpringMVC)
根据下面的代码实现之后,可以实现 用户访问 /test 的时候,就会自动调用 FrontServlet 下面 的 test() 方法,并且根据 该 方法的返回值 来 返回 jsp 文件 或者是 跳转,用户访问 /test2 的时候,就会调用 test2() 方法,以此类推。
本文是 小小商城-Servlet 版的 细节详解系列 之一,项目 github:https://github.com/xenv/S-mall-servlet/ 本文代码大部分在 github 中 可以找到
具体实现 完整的实现代码见:https://github.com/xenv/S-mall-servlet/blob/master/src/filter/BackServletFilter.java
创建 Filter 文件,implements Filter ,override doFilter 方法
2) 在 doFilter 方法中获取 uri
1 2 3 4 5 6 String contextPath = request.getServletContext().getContextPath(); String uri = request.getRequestURI(); uri = StringUtils.remove(uri,contextPath);
根据不同的 uri ,获取到对应的 函数 名,插入到 request 中,比如,我们简单在这里直接把根目录下的 uri 直接 转成 函数 名
1 2 3 4 5 6 7 8 9 if (!(uri.contains("." ) || (uri.lastIndexOf('/' )>0 ))){ String method = uri.substring(1 ); request.setAttribute("method" ,method); String servletPath = "front.servlet" ; request.getRequestDispatcher(servletPath).forward(request,response); ... } filterChain.doFilter(request,response);
根据不同的 uri,调用 Servlet,比如,我们简单在这里直接 调用 一个固定的 servlet
1 2 String servletPath = "front.servlet" ; request.getRequestDispatcher(servletPath).forward(request,response);
在 Servlet 中,重写 service 方法,我们读取 刚刚传进来的 method ,反射调用相关方法,并把 request 和 responce 传进去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws xception { String method = (String)req.getAttribute("method" ); req.setCharacterEncoding("utf-8" ); Method m = this .getClass().getMethod(method,HttpServletRequest.class ,HttpServletResponse .class ) ; String redirect = m.invoke(this ,req,resp).toString(); if (redirect.startsWith("@" )){ resp.sendRedirect(redirect.substring(1 )); }else if (redirect.startsWith("%" )) resp.getWriter().print(redirect.substring(1 )); else req.getRequestDispatcher(redirect).forward(req, resp); }
在这里,我们的 method 会返回一个 String,如果这个 String 是 @ 开头,那么是跳转,相当于 Springmvc 的 (redirect:) ,如果是 %开头,则是返回纯文本,其他开头则 调用那个 JSP (相当于 Springmvc 的视图定位)
对于非法访问的情况,可能会出错,我们必须要进行错误处理,这里只是简单一个简单演示
在具体 method 中,我们需要接受 request 和 responce,返回 String,例如
1 2 3 4 5 public String delete (HttpServletRequest request , HttpServletResponse response) { int id = Integer.parseInt(request.getParameter("id" )); service.delete(id); return "@/admin/category_list" ; }
在 web.xml 配置 filter 和 servlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <filter > <filter-name > BackServletFilter</filter-name > <filter-class > filter.BackServletFilter</filter-class > </filter > <filter-mapping > <filter-name > BackServletFilter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping > <servlet > <servlet-name > FrontServlet</servlet-name > <servlet-class > servlet.FrontServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > FrontServlet</servlet-name > <url-pattern > /front.servlet</url-pattern > </servlet-mapping >
这样,用户访问 /test 的时候,就会自动调用 Frontservlet 下面 的 test() 方法,并且根据 该 方法的返回值 来 返回 jsp 文件 或者是 跳转
总结 用户访问 url ,先到 Filter ,filter 根据 url ,获取相应的 method 和 servlet ,然后 filter 把控制权交给 servlet, serlvet 再根据 method 调用 具体的方法,返回要加载 jsp 文件 或者 跳转,这样我们就实现了和 SpringMVC 大致一样的效果。