上一回讲到如何在一堆工具类的帮助下,在IocHelper类中实现依赖注入。然后书中编写了一个ClassHelper类,该类主要的作用是获取所有Controller类,然后通过反射获取该类中所有带Action的方法,进而获取请求方法和请求路径,把所有的“请求-处理对象”对封装成一个HashMap,便于调用获取。
于是又定义了Request对象和Handler对象。前者封装请求方法和请求路径(请求信息),后者封装对应的Action方法和Controller类。代码较为简单,这里略过。
然后又定义了一个HelpLoader类,里面只有一个静态代码块,用来加载前面所有的Helper类。(其实不这样写也没事,作者的意思是这样写较为集中和规范)
下一步就是编写MVC框架核心的DispatcherServlet类,用它来实现接收请求,把请求分发给合适的Controller调用合适的方法来处理请求,再把结果返回给前端。之前所有的一切都是为了这一步做的准备。
在这一步中,因为要获取前端传来的请求的参数,处理完成后还要给前端返回结果,所以把参数封装成Param类,而结果分为视图(JSP页面)和数据(json格式),分别封装为View类和Data类。这里需要注意的是视图中也会有一些自带的数据,于是View中还需要有一个HashMap用来存放这些数据。
Param类:
public class Param {
/**
* 这里这个Param对象主要是封装Servlet拿到的请求参数
*/
private Map<String, Object> paramMap;
public Param(Map<String, Object> paramMap){
this.paramMap = paramMap;
}
/**
* 根据参数名获取long型参数值
*/
public long getLong(String name){
return CastUtil.castLong(paramMap.get(name));
}
/**
* 获取所有字段信息
*/
public Map<String, Object> getMap(){
return paramMap;
}
}
View类:
public class View {
/**
* 视图路径
*/
private String path;
/**
* 模型数据
*/
private Map<String, Object> model;
public View(String path){
this.path = path;
model = new HashMap<String, Object>();
}
public View addModel(String key, Object value){
model.put(key, value);
return this;
}
public String getPath(){
return path;
}
public Map<String, Object> getModel(){
return model;
}
}
Data类:
public class Data {
/**
* 直接封装一个Object类型的模型数据,框架会将该对象写入HttpServletResponse对象中便于返回
*/
private Object model;
public Data(Object model){
this.model = model;
}
public Object getModel(){
return model;
}
}
这些准备工作都做好之后,就是编写最重要的DispatcherServlet类了。它是一个普通的Servlet。
//注意这里的注解,urlPatterns写成这样是匹配所有的请求,loadOnStartup用来指定Servlet是否自动加载,为1则是,为0则否
@WebServlet(urlPatterns = "/*", loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException{
//初始化相关Helper类
HelperLoader.init();
//获取ServletContext对象(用于注册Servlet)
ServletContext servletContext = servletConfig.getServletContext();
//注册处理JSP的Servlet
ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
jspServlet.addMapping(ConfigHelper.getAppJspPath() + "*");
//注册处理静态资源的默认Servlet
ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");
}
@Override
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取请求方法与请求路径
String requestMethod = request.getMethod().toLowerCase();
String requestPath = request.getPathInfo();
//获取Action处理器
Handler handler = ControllerHelper.getHandler(requestMethod, requestPath);
if(handler != null){
//获取Controller类及其Bean实例
Class<?> controllerClass = handler.getControllerClass();
Object controllerBean = BeanHelper.getBean(controllerClass);
//创建请求参数对象
Map<String, Object> paramMap = new HashMap<String, Object>();
Enumeration<String> paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()){
String paramName = paramNames.nextElement();
String paramValue = request.getParameter(paramName);
paramMap.put(paramName, paramValue);
}
//获取请求体
String body = CodecUtil.decodeURL(StreamUtil.getString(request.getInputStream()));
if(StringUtil.isNotEmpty(body)){
//解析请求体参数
String[] params = StringUtil.splitString(body, "&");
if(ArrayUtil.isNotEmpty(params)){
for(String param : params){
//每一对参数的名+值
String[] array = StringUtil.splitString(param, "=");
if(ArrayUtil.isNotEmpty(array) && array.length == 2){
String paramName = array[0];
String paramValue = array[1];
paramMap.put(paramName, paramValue);
}
}
}
}
Param param = new Param(paramMap);
//调用Action方法
Method actionMethod = handler.getActionMethod();
Object result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param);
//处理Action方法返回值
if(result instanceof View){
//返回JSP页面(或许也可以做成静态页面html?)
View view = (View) result;
String path = view.getPath();
if(StringUtil.isNotEmpty(path)){
if(path.startsWith("/")){
response.sendRedirect(request.getContextPath() + path);
}
else{
Map<String, Object> model = view.getModel();
for(Map.Entry<String, Object> entry : model.entrySet()){
request.setAttribute(entry.getKey(), entry.getValue());
}
request.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(request, response);
}
}
}else if(result instanceof Data){
//返回JSON数据
Data data = (Data) result;
Object model = data.getModel();
if(model != null){
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
String json = JsonUtil.toJson(model);
writer.write(json);
writer.flush();
writer.close();
}
}
}
}
}
注释写得较为清楚,需要注意的:这里的请求方法指的是get/post/put/delete这些,请求路径是/xxx/xxx,后面的invokeMethod这一步才是调用对应的后台方法处理请求。