4.自己动手写Java Web框架-MVC++
2016-09-17 19:55
459 查看
jw的github地址是https://github.com/menyouping/jw
前一篇文章就是一个Java反射,但是却达到了最简单的MVC目的,但是与Sping相比,它的功能确实很简陋。返回值都是json,可不可以是一个jsp文件路径?可不可以返回css,js,html,png等静态文件?还有很多问题,本篇只介绍这两个问题的处理,美其名曰MVC++,只是前一篇文章的++。
Controller方法上有@ResponseBody标记,就返回json格式数据,如果返回值是String类型,就当做返回jsp页面地址(为了更通用,可以自定义文件后缀名)。
service方法中加入下面这一句,极大的方便了在jsp页面中获取本app的路径问题。
isStaticResource方法用于判断是否是静态资源,根据配置来确定
web.resources.extension中记录了哪些文件后缀名是静态文件。
新增类jw-core/src/main/java/com/jw/util/MimeUtils.java
css进行了特殊处理,因为MimetypesFileTypeMap中的返回值不准确。
修改jw-core/src/main/java/com/jw/util/FileUtils.java
找到jsp文件的路径,再利用RequestDispatcher.forward(request, response)将内容返回给前台。
修改jw-core/src/main/java/com/jw/web/servlet/DispatcherServlet.java
修改jw-web/pom.xml
新增文件夹jw-web/src/main/webapp/WEB-INF/views,放入jsp文件。
这里显示的是500.jsp。显然这个url找不到对应的controller,显示404.jsp更准确,这里就不做细致划分了。
重启Server,输入网址http://localhost:8080/jw-web/index,回车。
正确显示index.jsp内容。
修改jw-web/src/main/java/com/jay/mvc/IndexController.java
重启Server,输入网址http://localhost:8080/jw-web/index/forms,回车。
正确显示forms.jsp内容。
输入网址http://localhost:8080/jw-web/index/redirect,回车。
浏览器地址跳转到http://localhost:8080/jw-web/index/forms,正确显示forms.jsp内容。
输入网址http://localhost:8080/jw-web/index/welcome,回车。
显示
@ResponseBody正确显示。
至此MVC++完成。
本节资源地址: https://github.com/menyouping/jw-example/blob/master/4/jw-parent.zip
前一篇文章就是一个Java反射,但是却达到了最简单的MVC目的,但是与Sping相比,它的功能确实很简陋。返回值都是json,可不可以是一个jsp文件路径?可不可以返回css,js,html,png等静态文件?还有很多问题,本篇只介绍这两个问题的处理,美其名曰MVC++,只是前一篇文章的++。
MVC++
添加Annotation
前一篇文章我们全部返回json格式,现打算根据能够支持返回jsp路径地址,根据提示返回json格式。添加@ResponseBody
package com.jw.web.bind.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ResponseBody { }
Controller方法上有@ResponseBody标记,就返回json格式数据,如果返回值是String类型,就当做返回jsp页面地址(为了更通用,可以自定义文件后缀名)。
根据情况返回内容
修改jw-core/src/main/java/com/jw/web/servlet/DispatcherServlet.javapublic void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String appName = request.getSession().getServletContext().getContextPath(); request.setAttribute("root", appName);//在jsp页面中添加root变量 String path = request.getRequestURI().substring(appName.length()); //检查是否是静态文件,例如: css, js, html... if(isStaticResource(response, path)) return; //...此处代码省略,同前文 try { Class<?> controllerClaze = Class.forName(controllerClazeName); Method method = controllerClaze.getMethod(methodName, new Class<?>[] { HttpServletRequest.class, HttpServletResponse.class }); Object controller = controllerClaze.newInstance(); Object[] paras = new Object[] { request, response }; if (JwUtils.isAnnotated(method, ResponseBody.class)) { Object result = method.invoke(controller, paras); response.setHeader("Content-type", "application/json;charset=UTF-8"); response.getWriter().write(JSON.toJSONString(result)); } else if (String.class.equals(method.getReturnType())) { String returnUrl = (String) method.invoke(controller, paras); if (!StringUtils.isEmpty(returnUrl)) { if (returnUrl.split(":")[0].equals("redirect")) { response.sendRedirect(returnUrl.split(":")[1]); } else { RequestDispatcher dispatcher = request .getRequestDispatcher("/" + getPage(returnUrl + "." + PAGE_DEFAULT_EXTENSION)); dispatcher.forward(request, response); } } } else { method.invoke(controller, paras); } } catch (Exception e) { LOGGER.error("Error raised in DispatcherServlet.", e); showError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } }
service方法中加入下面这一句,极大的方便了在jsp页面中获取本app的路径问题。
request.setAttribute("root", appName);//在jsp页面中添加root变量
isStaticResource方法用于判断是否是静态资源,根据配置来确定
处理静态资源
添加配置信息
修改jw-web/src/main/resources/application.properties,添加配置#The folder that contains static resources, such as css file and js file, etc web.resources.folder=resources #The file which is regard as static resource file, others are not web.resources.extension=js;css;jpg;ico;png;jpeg;gif;bmp;swf;eot;svg;ttf;woff;woff2;less;scss;
逻辑处理
修改jw-core/src/main/java/com/jw/web/servlet/DispatcherServlet.javaprivate static final String RESOURCE_FOLDER = ConfigUtils.getProperty("web.resources.folder") + "/"; private static final String RESOURCES_EXTENSION = ConfigUtils.getProperty("web.resources.extension"); protected boolean isStaticResource(HttpServletResponse response, String path) throws IOException { int index = path.lastIndexOf("."); if (index > -1 && index < path.length() - 1) { String ext = path.substring(index + 1).toLowerCase(); if (RESOURCES_EXTENSION.contains(ext)) { response.setHeader("Content-type", MimeUtils.getMimeType(ext) + ";charset=UTF-8"); String page = this.getServletContext().getRealPath(getResource(path)); FileUtils.copy(page, response.getOutputStream()); return true; } if ("html".equals(ext)) { response.setHeader("Content-type", "text/html;charset=UTF-8"); String page = this.getServletContext().getRealPath(getPage(path)); FileUtils.copy(page, response.getOutputStream()); return true; } } return false; } public static String getResource(String fileName) { return RESOURCE_FOLDER + (fileName.startsWith("/") ? fileName.substring(1) : fileName); }
web.resources.extension中记录了哪些文件后缀名是静态文件。
指定Content-type
根据请求的资源类型,指定返回内容的Content-type,由MimeUtils.getMimeType(ext)确定。新增类jw-core/src/main/java/com/jw/util/MimeUtils.java
package com.jw.util; import javax.activation.MimetypesFileTypeMap; public class MimeUtils { private static final MimetypesFileTypeMap MIMETYPE_MAP = new MimetypesFileTypeMap(); public static String getMimeType(String extension) { if ("css".equals(extension)) return "text/css"; return MIMETYPE_MAP.getContentType("x." + extension); } }
css进行了特殊处理,因为MimetypesFileTypeMap中的返回值不准确。
写入静态资源内容
如果是静态资源,直接使用FileUtils.copy()方法将文件内容复制到response中返回给前端。修改jw-core/src/main/java/com/jw/util/FileUtils.java
public static void copy(String file, OutputStream out) throws IOException { FileInputStream in = new FileInputStream(file); int c; byte buffer[] = new byte[1024]; while ((c = in.read(buffer)) != -1) { for (int i = 0; i < c; i++) { out.write(buffer[i]); } } in.close(); out.close(); }
返回jsp内容
修改jw-core/src/main/java/com/jw/web/servlet/DispatcherServlet.javaprivate static final String PAGE_FOLDER = "WEB-INF/" + ConfigUtils.getProperty("web.page.folder") + "/"; public static String getPage(String fileName) { return PAGE_FOLDER + (fileName.startsWith("/") ? fileName.substring(1) : fileName); }
找到jsp文件的路径,再利用RequestDispatcher.forward(request, response)将内容返回给前台。
处理错误
遇到错误时,我们不能仅仅返回一个500错误,最好能根据错误状态码跳转到一个相应的错误页面。修改jw-core/src/main/java/com/jw/web/servlet/DispatcherServlet.java
protected void showError(HttpServletRequest request, HttpServletResponse response, int status) throws ServletException, IOException { response.setStatus(status); if ("jsp".equals(PAGE_DEFAULT_EXTENSION)) { String page = "/" + getPage(status + "." + PAGE_DEFAULT_EXTENSION); RequestDispatcher dispatcher = request.getRequestDispatcher(page); dispatcher.forward(request, response); } else { response.setHeader("Content-type", "text/html;charset=UTF-8"); String page = this.getServletContext().getRealPath(getPage(status + "." + PAGE_DEFAULT_EXTENSION)); FileUtils.copy(page, response.getOutputStream()); } }
添加配置
修改jw-web/src/main/resources/application.propertiesweb.page.folder=views
#The default page file extension
web.page.default.extension=jsp
#The folder that contains static resources, such as css file and js file, etc web.resources.folder=resources #The file which is regard as static resource file, others are not web.resources.extension=js;css;jpg;ico;png;jpeg;gif;bmp;swf;eot;svg;ttf;woff;woff2;less;scss;
DispatcherServlet全文
零零碎碎在DispatcherServlet修改了很多,现在把它的全文贴到这里,方便检阅。package com.jw.web.servlet;
import java.io.IOException;
import java.lang.reflect.Method;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.jw.util.ConfigUtils;
import com.jw.util.FileUtils;
import com.jw.util.JwUtils;
import com.jw.util.MimeUtils;
import com.jw.util.StringUtils;
import com.jw.web.bind.annotation.ResponseBody;
public class DispatcherServlet extends HttpServlet {
private static final Logger LOGGER = LoggerFactory.getLogger(DispatcherServlet.class);
private static final long serialVersionUID = -3874308705324703315L;
private static final String PAGE_FOLDER = "WEB-INF/" + ConfigUtils.getProperty("web.page.folder") + "/";
private static final String RESOURCE_FOLDER = ConfigUtils.getProperty("web.resources.folder") + "/";
private static final String RESOURCES_EXTENSION = ConfigUtils.getProperty("web.resources.extension");
private static final String PAGE_DEFAULT_EXTENSION = ConfigUtils.getProperty("web.page.default.extension");
public DispatcherServlet() {
}
public void init() {
}
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String appName = request.getSession().getServletContext().getContextPath();
request.setAttribute("root", appName);//在jsp页面中添加root变量
String path = request.getRequestURI().substring(appName.length());
// handle static resource, e.g. css, js, html...
if (isStaticResource(response, path))
return;
LOGGER.info("Request path is {}", path);
// /index/index/ => /index/index
if (path.length() > 1 && path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
} else if (path.startsWith("/")) {// /index/index => index/index
path = path.replaceFirst("/", "");
}
if (path.isEmpty()) {
path = "index/index";
} else if (!path.contains("/")) {
path += "/index";// index => index/index
}
String[] paths = path.split("/", 2);
String controllerClazeName = ConfigUtils.getProperty("package.scan") + "." + StringUtils.upperFirst(paths[0])
+ "Controller";
String methodName = paths[1];
try {
Class<?> controllerClaze = Class.forName(controllerClazeName);
Method method = controllerClaze.getMethod(methodName,
new Class<?>[] { HttpServletRequest.class, HttpServletResponse.class });
Object controller = controllerClaze.newInstance();
Object[] paras = new Object[] { request, response };
if (JwUtils.isAnnotated(method, ResponseBody.class)) {
Object result = method.invoke(controller, paras);
response.setHeader("Content-type", "application/json;charset=UTF-8");
response.getWriter().write(JSON.toJSONString(result));
} else if (String.class.equals(method.getReturnType())) {
String returnUrl = (String) method.invoke(controller, paras);
if (!StringUtils.isEmpty(returnUrl)) {
if (returnUrl.split(":")[0].equals("redirect")) {
response.sendRedirect(returnUrl.split(":")[1]);
} else {
RequestDispatcher dispatcher = request
.getRequestDispatcher("/" + getPage(returnUrl + "." + PAGE_DEFAULT_EXTENSION));
dispatcher.forward(request, response);
}
}
} else {
method.invoke(controller, paras);
}
} catch (Exception e) {
LOGGER.error("Error raised in DispatcherServlet.", e);
showError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
protected boolean isStaticResource(HttpServletResponse response, String path) throws IOException {
int index = path.lastIndexOf(".");
if (index > -1 && index < path.length() - 1) {
String ext = path.substring(index + 1).toLowerCase();
if (RESOURCES_EXTENSION.contains(ext)) {
response.setHeader("Content-type", MimeUtils.getMimeType(ext) + ";charset=UTF-8");
String page = this.getServletContext().getRealPath(getResource(path));
FileUtils.copy(page, response.getOutputStream());
return true;
}
if ("html".equals(ext)) {
response.setHeader("Content-type", "text/html;charset=UTF-8");
String page = this.getServletContext().getRealPath(getPage(path));
FileUtils.copy(page, response.getOutputStream());
return true;
}
}
return false;
}
public static String getPage(String fileName) {
return PAGE_FOLDER + (fileName.startsWith("/") ? fileName.substring(1) : fileName);
}
public static String getResource(String fileName) {
return RESOURCE_FOLDER + (fileName.startsWith("/") ? fileName.substring(1) : fileName);
}
protected void showError(HttpServletRequest request, HttpServletResponse response, int status) throws ServletException, IOException { response.setStatus(status); if ("jsp".equals(PAGE_DEFAULT_EXTENSION)) { String page = "/" + getPage(status + "." + PAGE_DEFAULT_EXTENSION); RequestDispatcher dispatcher = request.getRequestDispatcher(page); dispatcher.forward(request, response); } else { response.setHeader("Content-type", "text/html;charset=UTF-8"); String page = this.getServletContext().getRealPath(getPage(status + "." + PAGE_DEFAULT_EXTENSION)); FileUtils.copy(page, response.getOutputStream()); } }
}
测试
添加依赖
修改jw-parent/pom.xml<dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
修改jw-web/pom.xml
<dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> </dependency>
新增jsp文件和静态资源
新增文件夹jw-web/src/main/webapp/resources,放入css,js等资源。新增文件夹jw-web/src/main/webapp/WEB-INF/views,放入jsp文件。
测试错误URL
启动server,输入网址http://localhost:8080/jw-web/abc ,回车。这里显示的是500.jsp。显然这个url找不到对应的controller,显示404.jsp更准确,这里就不做细致划分了。
测试显示jsp
修改jw-web/src/main/java/com/jay/mvc/IndexController.javapublic String index(HttpServletRequest request, HttpServletResponse response) { return "index"; }
重启Server,输入网址http://localhost:8080/jw-web/index,回车。
正确显示index.jsp内容。
修改jw-web/src/main/java/com/jay/mvc/IndexController.java
public String redirect(HttpServletRequest request, HttpServletResponse response) { return "redirect:forms"; } public String forms(HttpServletRequest request, HttpServletResponse response) { return "forms"; }
重启Server,输入网址http://localhost:8080/jw-web/index/forms,回车。
正确显示forms.jsp内容。
输入网址http://localhost:8080/jw-web/index/redirect,回车。
浏览器地址跳转到http://localhost:8080/jw-web/index/forms,正确显示forms.jsp内容。
输入网址http://localhost:8080/jw-web/index/welcome,回车。
显示
{"message":"Welcome to jw's world!","status":200}
@ResponseBody正确显示。
至此MVC++完成。
本节资源地址: https://github.com/menyouping/jw-example/blob/master/4/jw-parent.zip
相关文章推荐
- 5.自己动手写Java Web框架-上下文
- 1.自己动手写Java Web框架-创建项目
- 自己动手设计java web框架(三)-执行请求路径所对应方法并返回
- 架构探险读书笔记——自己搭建轻量级javaWeb框架之MVC和IOC
- 3.自己动手写Java Web框架-MVC初体验
- 自己动手设计java web框架(一)-封装请求拦截器DispatchServlet
- java 自己动手做框架之MVC
- 自己动手设计java web框架(二)-自定义注解以及通过反射获取注解
- 自己动手写一个web框架(一):实现IOC与MVC
- 0.自己动手写Java Web框架-首页
- 6.自己动手写Java Web框架-Model
- 2.自己动手写Java Web框架-读取配置
- 自己动手打造基于 WKWebView 的混合开发框架(一)WKWebView 上手
- 自己动手写web框架----1
- 自己动手写web框架----2
- 自己动手做Web框架—MVC+Front Controller
- 黑马程序员--javaweb开发-自己动手写一个servlet
- PHP学习笔记,自己动手写个MVC的框架 -- base所有代码
- 《自己动手写开源框架10》:Web界面快速开发实践
- 自己动手做Web框架—MVC+Front Controller