您的位置:首页 > 编程语言 > C语言/C++

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++,只是前一篇文章的++。

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.java

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());
//检查是否是静态文件,例如: 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.java

private 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.java

private 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.properties

web.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.java

public 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: