servlet优化
Web 层的 Servlet 个数太多了,不利于管理和编写
我们发现每一个功能都需要定义一个 servlet
,一个模块需要实现增删改查功能,就需要4个 servlet
,模块一多就会造成servlet
泛滥。此时我们就想 servlet
能不能像 service
一样,一个模块只定义一个 servlet
,而每一个功能只需要在该 servlet
中定义对应的方法。例如下面代码:
@WebServlet("/brand/*")
public class BrandServlet {
//查询所有
public void selectAll(...) {}
//添加数据
public void add(...) {}
//修改数据
public void update(...) {}
//删除删除
public void delete(...) {}
}
而我们知道发送请求 servlet
,tomcat
会自动的调用 service()
方法,之前我们在自定义的 servlet
中重写 doGet()
方法和 doPost()
方法,当我们访问该 servlet
时会根据请求方式将请求分发给 doGet()
或者 doPost()
方法,如下图
那么我们也可以仿照这样请求分发的思想,在 service()
方法中根据具体的操作调用对应的方法,如:查询所有就调用 selectAll()
方法,添加企业信息就调用 add()
方法。
为了做到通用,我们定义一个通用的 servlet
类,在定义其他的 servlet
是不需要继承 HttpServlet
,而继承我们定义的 BaseServlet
,在 BaseServlet
中调用具体 servlet
(如BrandServlet
)中的对应方法。
public class BaseServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//进行请求的分发
}
}
BrandServlet
定义就需要修改为如下:
@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {
//用户实现分页查询
public void selectAll(...) {}
//添加企业信息
public void add(...) {}
//修改企业信息
public void update(...) {}
//删除企业信息
public void delete(...) {}
}
那么如何在 BaseServlet
中调用对应的方法呢?比如查询所有就调用 selectAll()
方法。
可以规定在发送请求时,请求资源的二级路径(/brandServlet/selectAll)和需要调用的方法名相同,如:
查询所有数据的路径以后就需要写成: http://localhost:8080/brand-case/brandServlet/selectAll
添加数据的路径以后就需要写成: http://localhost:8080/brand-case/brandServlet/add
修改数据的路径以后就需要写成: http://localhost:8080/brand-case/brandServlet/update
删除数据的路径以后就需要写成: http://localhost:8080/brand-case/brandServlet/delete
这样的话,在 BaseServlet
中就需要获取到资源的二级路径作为方法名,然后调用该方法
public class BaseServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取请求路径
String uri = req.getRequestURI(); // 例如路径为:/brand-case/brand/selectAll
//2. 获取最后一段路径,方法名
int index = uri.lastIndexOf('/');
String methodName = uri.substring(index + 1); // 获取到资源的二级路径 selectAll
//2. 执行方法
//2.1 获取BrandServlet /UserServlet 字节码对象 Class
//System.out.println(this);
Class<? extends BaseServlet> cls = this.getClass();
//2.2 获取方法 Method对象
try {
Method method = cls.getMethod(methodName,???);
//4,调用该方法
method.invoke(this,???);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
通过上面代码发现根据方法名获取对应方法的 Method
对象时需要指定方法参数的字节码对象。解决这个问题,可以将方法的参数类型规定死,而方法中可能需要用到 request
对象和 response
对象,所以指定方法的参数为 HttpServletRequest
和 HttpServletResponse
,那么 BrandServlet
代码就可以改进为:
@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {
//用户实现分页查询
public void selectAll(HttpServletRequest req, HttpServletResponse resp) {}
//添加企业信息
public void add(HttpServletRequest req, HttpServletResponse resp) {}
//修改企业信息
public void update(HttpServletRequest req, HttpServletResponse resp) {}
//删除企业信息
public void delete(HttpServletRequest req, HttpServletResponse resp) {}
}
BaseServlet代码可以改进为:
public class BaseServlet extends HttpServlet {
//根据请求的最后一段路径来进行方法分发
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取请求路径
String uri = req.getRequestURI(); // 例如路径为:/brand-case/brand/selectAll
//2. 获取最后一段路径,方法名
int index = uri.lastIndexOf('/');
String methodName = uri.substring(index + 1); // 获取到资源的二级路径 selectAll
//2. 执行方法
//2.1 获取BrandServlet /UserServlet 字节码对象 Class
//System.out.println(this);
Class<? extends BaseServlet> cls = this.getClass();
//2.2 获取方法 Method对象
try {
Method method = cls.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
//2.3 执行方法
method.invoke(this,req,resp);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
定义了 BaseServlet
后,针对品牌模块我们定义一个 BrandServlet
的 Servlet,并使其继承 BaseServlet
。在BrandServlet
中定义 以下功能的方法:
查询所有
功能:方法名声明为selectAll
,并将之前的SelectAllServlet
中的逻辑代码拷贝到该方法中添加数据
功能:方法名声明为add
,并将之前的AddServlet
中的逻辑代码拷贝到该方法中
具体代码如下:
@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet{
private BrandService brandService = new BrandServiceImpl();
public void selectAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 调用service查询
List<Brand> brands = brandService.selectAll();
//2. 转为JSON
String jsonString = JSON.toJSONString(brands);
//3. 写数据
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(jsonString);
}
public void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 接收品牌数据
BufferedReader br = request.getReader();
String params = br.readLine();//json字符串
//转为Brand对象
Brand brand = JSON.parseObject(params, Brand.class);
//2. 调用service添加
brandService.add(brand);
//3. 响应成功的标识
response.getWriter().write("success");
}
}