目录
- 1. 背景
- 2. 实现
- 2.1 pom.xml
- 2.2 FruitController.java
- 2.3 DispatcherServlet.java
- 2.4 applicationContext.xml
- 3. 测试
1. 背景
前面我们做了Servlet的一个案例。但是存在很多问题,现在我们要做优化,优化的步骤如下:
- 每个Fruit请求都需要一个Servlet来处理,这样就会产生大量的Servlet。我们可以只用一个FruitServlet来处理,然后通过一个operate变量来区分请求FruitServlet的哪个方法
- 除了有Fruit的请求,可能还有很多其它类型的请求。可以用一个DispatcherServlet来处理所有的请求,然后定义一个applicationContext.xml来让一个请求和其处理的类对应
- 不在FruitController中做重定向,FruitController只返回要重定向的路径,统一由DispatcherServlet来做重定向
- 不在FruitController的方法中获取参数。由DispatcherServlet统一获取参数,然后传递给FruitController的方法
2. 实现
2.1 pom.xml
编译的时候保留方法的参数名,而不是擦除。注意删除target,不然不会生效
<!-- 编译的时候保留方法的参数名,而不是擦除。注意删除target,不然不会生效 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>11</source>
<target>11</target>
<compilerArgument>-parameters</compilerArgument>
</configuration>
</plugin>
2.2 FruitController.java
只负责具体的业务逻辑处理。重定向、参数获取全部由DispatcherServlet处理
package com.hh.javaWebTest.servlet;
import com.hh.javaWebTest.bean.Fruit;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
// 只负责具体的业务逻辑处理。重定向、参数获取全部由DispatcherServlet处理
public class FruitController {
protected String index(Integer paramId, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 不通过req.getParameter(paramName)获取参数值,而是通过在dispatcher统一获取,再传入进来
// 测试是否能获取到参数
System.out.println("paramId: " + paramId);
System.out.println(req.getRequestURI()); // /javaWebTest/fruit.do
System.out.println(req.getQueryString()); // paramId=1
List<Fruit> fruitList = new ArrayList<Fruit>();
fruitList.add(new Fruit("苹果", 5));
fruitList.add(new Fruit("香蕉", 3));
HttpSession session = req.getSession();
session.setAttribute("fruitList", fruitList);
// 由DispatcherServlet调用super.processTemplate("index", request, response);
return "index";
}
}
2.3 DispatcherServlet.java
接受所有的请求,然后进行分发
package com.hh.javaWebTest.servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
// 接受所有的请求,然后进行分发
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
private Map<String, Object> beanMap = new HashMap<>();
@Override
public void init() throws ServletException {
// 手动进行ViewBaseServlet的初始化
super.init();
// 解析applicationContext.xml,形成id:class形式的beanMap
try {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(inputStream);
NodeList beanNodeList = document.getElementsByTagName("bean");
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node beanNode = beanNodeList.item(i);
if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
Element beanElement = (Element) beanNode;
String beanId = beanElement.getAttribute("id");
String className = beanElement.getAttribute("class");
Class controllerBeanClass = Class.forName(className);
Object beanObj = controllerBeanClass.getDeclaredConstructor().newInstance();
beanMap.put(beanId, beanObj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// servletPath: /fruit.do => fruit
String servletPath = request.getServletPath();
servletPath = servletPath.substring(1);
int lastDotIndex = servletPath.lastIndexOf(".do");
servletPath = servletPath.substring(0, lastDotIndex);
// 获取fruit对应的class
Object controllerBeanObj = beanMap.get(servletPath);
// 获取operate
String operate = request.getParameter("operate");
if (StringUtils.isEmpty(operate)) {
operate = "index";
}
try {
Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
for (Method method : methods) {
// 匹配operate对应的类方法
if (operate.equals(method.getName())) {
Parameter[] parameters = method.getParameters();
// 将类方法的值,保存到parameterValues
Object[] parameterValues = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
String parameterName = parameter.getName();
if ("req".equals(parameterName)) {
parameterValues[i] = request;
} else if ("resp".equals(parameterName)) {
parameterValues[i] = response;
} else if ("session".equals(parameterName)) {
parameterValues[i] = request.getSession();
} else {
// 从请求中获取参数值
String parameterValue = request.getParameter(parameterName);
String typeName = parameter.getType().getName();
Object parameterObj = parameterValue;
if (parameterObj != null) {
if ("java.lang.Integer".equals(typeName)) {
parameterObj = Integer.parseInt(parameterValue);
}
}
parameterValues[i] = parameterObj;
}
}
// 调用方法,返回值。根据返回值判断是否需要做重定向
method.setAccessible(true);
Object returnObj = method.invoke(controllerBeanObj, parameterValues);
// 视图处理(重定向处理)
String methodReturnStr = (String) returnObj;
if (methodReturnStr.startsWith("redirect:")) { // 比如: redirect:fruit.do
String redirectStr = methodReturnStr.substring("redirect:".length());
response.sendRedirect(redirectStr);
} else if (methodReturnStr != "") { // 比如: "index"
super.processTemplate(methodReturnStr, request, response);
return;
} else {
return;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
throw new RuntimeException("operate值非法!");
}
}
2.4 applicationContext.xml
定义java bean的id,和其对应的处理类
<?xml version="1.0" encoding="utf-8"?>
<!--
XML包含三个部分:
1. XML声明, 而且声明这一行代码必须在XML文件的第一行
2. DTD: 文档类型定义
3. XML正文
-->
<!-- 该标签告知XML文档所使用的XML beans规范 -->
<!DOCTYPE beans [
<!ELEMENT beans (bean*)>
<!ELEMENT bean (property*)>
<!ELEMENT property (#PCDATA)>
<!ATTLIST bean id ID #REQUIRED>
<!ATTLIST bean class CDATA #IMPLIED>
<!ATTLIST property name CDATA #IMPLIED>
<!ATTLIST property ref IDREF #IMPLIED>
]>
<beans>
<!-- 说明这是一个java bean。id是fruit,其对应的处理类是FruitController -->
<bean id="fruit" class="com.hh.javaWebTest.servlet.FruitController"/>
</beans>
3. 测试
请求http://localhost:8080/javaWebTest/fruit.do?paramId=1,web页面显示和之前的一样
控制台输出如下:
paramId: 1