企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # 步骤1:一个路径对应一个Servlet的弊端 通过观察 ``` http://192.168.10.165:8080/tmall_ssm/admin_category_list ``` 可以发现,分类管理需要:增加,删除,编辑,修改,查询5个服务端功能。 那么按照传统的Servlet方式(`web.xml`配置或者使用注解`@WebServlet`),一个路径对应一个Servlet的思路,就需要设计5个Servlet类。 比如: ``` AddCategoryServlet DeleteCategoryServlet EditCategoryServlet UpdateCategoryServlet ListCategoryServlet ``` 而后台需要做分类,产品,属性,产品图片,用户,订单这么6种管理,那么就一共需要30个Servlet,也就是`一个请求对应一个Servlet`,控制器就显得比较臃肿,写起来也比较麻烦。 # 步骤2: 对设计进行改进 但是观察已经实现了分类管理的`可运行的项目`里的代码,却发现Servlet只有一个即CategoryServlet。 如图是对于最后完工了的项目的servlet包里类的截图,可以发现,每种实体类,对应了一个Servlet,而不是对应了5个,这样首先从Servlet数量上来讲,就大大的减少了。 ![](https://box.kancloud.cn/e636147285dcd26471fcf9686c3b5af1_216x141.png) # 步骤3: 原理流程图 那么是如何做到一个CategoryServlet类,就能完成本来需要5个Servlet类才能完成的功能的呢? 让我们借助流程图来分析,为什么访问`admin_category_list`的时候,CategoryServlet的list()方法会被调用。 1. 假设访问路径是 `http://127.0.0.1:8080/tmall_j2ee/admin_category_list` 2. 过滤器BackServletFilter进行拦截,判断访问的地址是否以`admin_`开头 3. 如果是,那么做如下操作 3.1 取出两个下划线之间的值 category 3.2 取出最后一个下划线之后的值 list 3.3 然后根据这个值,服务端跳转到categoryServlet,并且把list这个值传递过去 4. categoryServlet 继承了BaseBackServlet,其service方法会被调用。 在service中,借助反射技术,根据传递过来的值 list,调用对应categoryServlet 中的方法list() 。 5. 这样就实现了当访问的路径是 `admin_category_list`的时候,就会调用`categoryServlet.list()`方法这样一个效果。 > 换句话说: 如果访问的路径是admin\_category\_add,就会调用categoryServlet.add()方法 如果访问的路径是admin\_category\_delete,就会调用categoryServlet.delete()方法 如果访问的路径是admin\_category\_edit,就会调用categoryServlet.edit()方法 如果访问的路径是admin\_category\_update,就会调用categoryServlet.update()方法 如此这般,一个categoryServlet类,就完成了本来需要5个Servlet类才能完成的功能。 ![](https://box.kancloud.cn/791385aa832c22ed4d1aaceffd117ad6_761x639.png) # 步骤4:代码讲解 - BackServletFilter 那么BackServletFilter类到底是如何工作的呢? 接下来我们此类进行代码讲解。 1. 首先在web.xml配置文件中,让所有的请求都会经过BackServletFilter ``` <filter-mapping> <filter-name>BackServletFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ``` 2\. 还是假设访问的路径是: http://127.0.0.1:8080/tmall_j2ee/admin\_category\_list 3\. 在BackServletFilter 中通过request.getRequestURI()取出访问的uri: /tmall_j2ee/admin\_category\_list 4\. 然后截掉/tmall_j2ee,得到路径/admin\_category\_list 5\. 判断其是否以/admin开头 6\. 如果是,那么就取出两个\_之间的字符串,category,并且拼接成/CategoryServlet,通过服务端跳转到/CategoryServlet 7\. 在跳转之前,还取出了list字符串,然后通过request.setAttribute的方式,借助服务端跳转,传递到categoryServlet里去。 ``` package com.dodoke.filter; import java.io.IOException; 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 org.apache.commons.lang.StringUtils; /** * Servlet Filter implementation class BackServletFilter */ public class BackServletFilter implements Filter { /** * Default constructor. */ public BackServletFilter() { // TODO Auto-generated constructor stub } /** * @see Filter#destroy() */ public void destroy() { // TODO Auto-generated method stub } /** * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) */ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub // place your code here HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String contextPath = request.getContextPath(); String uri = request.getRequestURI(); uri = StringUtils.remove(uri, contextPath); if (uri.startsWith("/admin_")) { // 从admin_category_list截取出category String temp = StringUtils.substringBetween(uri, "_", "_"); // category首字母大写与Servlet拼接 String servletPath = StringUtils.capitalize(temp) + "Servlet"; String method = StringUtils.substringAfterLast(uri, "_"); request.setAttribute("method", method); req.getRequestDispatcher("/" + servletPath).forward(request, response); return; } // pass the request along the filter chain chain.doFilter(request, response); } /** * @see Filter#init(FilterConfig) */ public void init(FilterConfig fConfig) throws ServletException { // TODO Auto-generated method stub } } ``` # 步骤5:代码讲解 - CategoryServlet和BaseBackServlet 接着流程就到了categoryServlet这里。 服务端跳转/categoryServlet就到了CategoryServlet这个类里 1. 首先CategoryServlet继承了BaseBackServlet,而BaseBackServlet又继承了HttpServlet。 2. 服务端跳转过来之后,会访问CategoryServlet的doGet()或者doPost()方法 3. 在访问doGet()或者doPost()之前,会访问service()方法 4. BaseBackServlet中重写了service() 方法,所以流程就进入到了service()中 5. 在service()方法 根据**反射**访问对应的方法,然后根据对应方法的返回值,进行服务端跳转、客户端跳转、或者直接输出字符串。 5.1 取到从BackServletFilter中request.setAttribute()传递过来的值 list 5.2 根据这个值list,借助**反射机制**调用CategoryServlet类中的list()方法 这样就达到了CategoryServlet.list()方法被调用的效果 BaseBackServlet ``` package com.dodoke.controller; import java.lang.reflect.Method; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.dodoke.dao.impl.CategoryDaoImpl; import com.dodoke.dao.inter.CategoryDao; import com.dodoke.util.Page; /** * Servlet implementation class BaseBackServlet */ @WebServlet("/BaseBackServlet") public abstract class BaseBackServlet extends HttpServlet { private static final long serialVersionUID = 1L; public abstract String add(HttpServletRequest request, HttpServletResponse response); public abstract String delete(HttpServletRequest request, HttpServletResponse response); public abstract String edit(HttpServletRequest request, HttpServletResponse response); public abstract String update(HttpServletRequest request, HttpServletResponse response); public abstract String list(HttpServletRequest request, HttpServletResponse response); protected CategoryDao categoryDao = new CategoryDaoImpl(); public void service(HttpServletRequest request, HttpServletResponse response) { try { /* 借助反射,调用对应的方法 */ String method = (String) request.getAttribute("method"); Method m = this.getClass().getMethod(method, javax.servlet.http.HttpServletRequest.class, javax.servlet.http.HttpServletResponse.class, Page.class); String redirect = m.invoke(this, request, response).toString(); /* 根据方法的返回值,进行相应的客户端跳转,服务端跳转,或者仅仅是输出字符串 */ System.out.println(redirect); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } } ``` CategoryServlet ``` package com.dodoke.controller; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class CategoryServlet */ @WebServlet("/CategoryServlet") public class CategoryServlet extends BaseBackServlet { @Override public String add(HttpServletRequest request, HttpServletResponse response) { // TODO Auto-generated method stub return "add"; } @Override public String delete(HttpServletRequest request, HttpServletResponse response) { // TODO Auto-generated method stub return "delete"; } @Override public String edit(HttpServletRequest request, HttpServletResponse response) { // TODO Auto-generated method stub return "edit"; } @Override public String update(HttpServletRequest request, HttpServletResponse response) { // TODO Auto-generated method stub return "update"; } @Override public String list(HttpServletRequest request, HttpServletResponse response) { // TODO Auto-generated method stub return "list"; } } ``` # 步骤6: 一个Servlet类就能满足CRUD一系列业务要求 通过这样一种模式,一个Servlet类就能满足CRUD一系列业务要求 如果访问的路径是admin\_category\_list,就会调用categoryServlet.list()方法 如果访问的路径是admin\_category\_add,就会调用categoryServlet.add()方法 如果访问的路径是admin\_category\_delete,就会调用categoryServlet.delete()方法 如果访问的路径是admin\_category\_edit,就会调用categoryServlet.edit()方法 如果访问的路径是admin\_category\_update,就会调用categoryServlet.update()方法 ![](https://box.kancloud.cn/56edd50d3577fdcb98af642836a9cdda_434x147.png)