最近在复习servlet,发现用原生servlet处理请求时,一个servlet只能处理一个请求,有多个业务时只能写很多个servlet,虽然springmvc等框架都可以解决这个问题,而且实际开发也不会有人用原生servlet处理请求,但是还是想尝试自己写一个简单的工具解决这一麻烦。先回顾下原生servlet处理一个请求:
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("loginServlet");
}
}
然而,一个Controller会有很多业务,如userController里就有login、register、loginByWX(微信扫码登陆)、checkEmail(查看邮箱是否可用)、checkUsername(查看用户名是否可用)等,加上其他controller的话servlet就要写上百个,显然是不可行的,所以我想能不能通过规范请求的url,让我后端知道该请求应该去到哪里,比如说/user/login.do就是要去userController这个类中去调用login()方法,于是我写了一个类,就叫DispatcherServlet(分发器),让他在service方法中截取url获得两个东西:我要调用哪个方法和这个方法在哪个类里:
@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//为所有
@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {
private static Map requestMapping = new HashMap<>();
//以下静态代码块仅运行一次,在此类被加载时(第一次收到*.do请求时),根据applicationContext.xml的配置初始化uri和controller的映射关系
static {
try {
InputStream is = DispatcherServlet.class.getClassLoader().getResourceAsStream("applicationContext.xml");
//解析xml文件
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document dom = documentBuilder.parse(is);
// 或者 中的内容拿出来,封装成com.sun.org.apache.xerces.internal.dom.DeepNodeListImpl对象
NodeList bean = dom.getElementsByTagName("bean");
for (int i = 0; i < bean.getLength(); i++) {
Node node = bean.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE){
Element element = (Element) node;
String id = element.getAttribute("id");
String className = element.getAttribute("class");
Object o = Class.forName(className).getDeclaredConstructor().newInstance();
requestMapping.put(id,o);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//为所有/*.do的请求设置编码集
req.setCharacterEncoding("utf8");
String uri = req.getRequestURI();
//uri统一格式为:/controller/operation.do,拿到要执行的方法名
int startIndex = uri.lastIndexOf("/")+1;
int endIndex = uri.indexOf(".");
//拿到要调用的方法名
String operation = uri.substring(startIndex,endIndex);
//拿到controller的名字
String beanName = uri.substring(uri.indexOf("/")+1,uri.lastIndexOf("/"));
//在加载此类时已根据在applicationContext.xml中配置的信息将所有controller创建实例,现在根据uri,找到容器中对应的controller实例
Object controllerBean = requestMapping.get(beanName);
//拿到实例后,根据uri找到需要调用的方法名,通过反射找到此方法并调用
try {
Method methodToInvoke = controllerBean.getClass().getDeclaredMethod(operation, HttpServletRequest.class, HttpServletResponse.class);
methodToInvoke.setAccessible(true);
methodToInvoke.invoke(controllerBean,req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
算是一个乞丐版SpringMVC吧



