前言
这篇文章标题的确够屌,那么接下来阿海就以这个标题为入口,那么,就请诸位看官们且听我如何吹牛!
在阿海开始吹之前呢,也就是三天前吧,在某群看到过一篇博客,那界面,真心爽!而且阿海最近也在抽时间看关于前段的一些资料。虽然在前段上没什么造诣,但是写个HelloWorld什么的还是可以的,最起码我觉得我的审美观还是比较正常的。
你问我为什么扯到前端上了?哦,是这个样子,前面说到我的审美观比较不错,所以当我看到一个博客的清新大气简约时尚的页面时,又想想自己那个傻不溜秋的博客(blog.321aiyi.com),就萌生了一个完全属于自己的博客的想法!
于是就搭建了一个基于Spring的工程,当然数据库用的还是之前我曾经说过的基于Mybatis封装的神速开发框架。但是吧,尽管Spring很轻量级,但是我觉得写个博客程序什么的有点儿大材小用,直接用Servlet吧,又觉得有点儿麻烦。
于是乎,我决定封装一套用于MVC层的一个小框架,指不定以后什么时候突然兴趣来临写个小东西什么的就能派上用场了呢!
效果演示
哦了,上面的部分纯属乱吹,可以直接无视,在介绍这个MVC之前,我喜欢先让大家们看一下他的运行效果图:
先创建一个Controller:
@MVCController("controller")
public class MyController{
//创建完毕~待会儿下面演示的Java代码都是写在这里的
}
测试对象响应体
代码:
/**
* 测试对象返回
* @return
*/
@MVCURL("test")
@MVCJson
public Object test(){
Map<String, Object> result = new HashMap<>();
result.put("testString", "Hello World!");
result.put("testInteger", 110);
result.put("testBoolean", true);
String[] liStrings = {"哈哈哈", "嘿嘿嘿", "呵呵呵"};
result.put("testList", liStrings);
return result;
}
启动并访问localhost:8080/Blog/controller/test.e
测试JSP模板
JAVA代码
/**
* 测试JSP模板
* @param request
* @param response
* @return
*/
@MVCURL("test2")
public String toJsp(HttpServletRequest request, HttpServletResponse response){
request.setAttribute("va", "如果你看到这句话,说明JSP模板成功了!");
return "test";
}
JSP代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${va }
</body>
</html>
运行效果:
测试自定义参数接收
JAVA代码:
/**
* 测试自定义参数接收
* @param name
* @param sex
* @return
*/
@MVCURL("test4")
@MVCJson
public Map<String, Object> test4(String name, String sex, int age){
Map<String, Object> result = new HashMap<>();
result.put("姓名", name);
result.put("性别", sex);
result.put("年龄", age);
return result;
}
运行效果
测试Model的封装
Java代码:
/**
* 测试Model的封装
* @param name
* @param model
* @return
*/
@MVCURL("test5")
public String test5(String name, Model model){
model.set("name", name);
return "testJsp";
}
JSP代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
你传入的姓名是:${name }
</body>
</html>
结果截图:
到此,演示完毕,毕竟这个框架才写了三天,并且都是业余时间写的,所以也就只有这些比较常用的功能了,好在可以直接用HttpServletRequest和HttpServletResponse来处理所以一般情况下的小程序用这个做MVC感觉已经差不多了。
不是哥们儿我吹啊,想当年,哥们儿我小学的时候就就会黑别人QQ了(手动滑稽)~
简介
接下来我把这个封装的东西暂且叫他MVC框架,或者mvc框架。如果你觉得不够亲切,可以叫他《大屌牌儿MVC框架》
说说它的整体结构吧,他的整体结构其实就是一个单利的基于Hash表的ControllerBean全局管理控制容器和一个RequestMethod全局管理控制容器。(说白了就是俩静态Map)
下面是这个容器类的代码,如果被我之前说的一大堆没用的给整蒙了甚至找不到北的话,你看了下面这个代码或许会想“TMD这么简单的东西说那么复杂干毛!”:
package com.aiyi.mvc.reflection;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import com.aiyi.mvc.annotations.RequesMethod;
/**
* 对象容器
* @author 郭胜凯
* @time 2016年11月8日 下午2:47:18
* @addr blog.321aiyi.com
* @email [email protected]
*/
public class BeanContainer {
/**
* 对象容器集合
*/
public static Map<String, Object> ObjectContainer = new HashMap<>();
/**
* 方法容器集合
*/
public static Map<String, Method> MethodContainer = new HashMap<>();
/**
* 向容器中添加一个Bean
* @param string
* @param filterUrl
* @param class1
*/
public static void setBean(String type, String filterUrl, Object bean) {
// TODO Auto-generated method stub
ObjectContainer.put("<t>" + type + "</t><u>" + filterUrl + "</u>", bean);
}
/**
* 向容器中添加一个Url映射
* @param requesMethod
* @param url
* @param method
*/
public static void setMethod(RequesMethod requesMethod, String url, Method method) {
// TODO Auto-generated method stub
MethodContainer.put("<m>" + requesMethod.name().toUpperCase() + "</m><u>" + url + "</u>", method);
}
public static Method getMethod(String method, String uri) {
// TODO Auto-generated method stub
return MethodContainer.get("<m>" + method.toUpperCase() + "</m><u>" + uri + "</u>");
}
public static Object getBean(String type, String uri) {
// TODO Auto-generated method stub
return ObjectContainer.get("<t>" + type + "</t><u>" + type + "</u>");
}
}
哈哈,就是这么简单,就是俩Map而已!然后就是定义了一些通过客户端请求URI获取Controller和Method的一些方法。仅此而已!别问我我为什么要卖这么个关子,现在很多程序员都把很简单的东西说的那么复杂,还满嘴的名词儿一个一个的往外蹦,其实说的都是HelloWorld级别的东西,还自我认为很良好的样子。不只是程序员,连一些其他的职位也是这个样子,感觉会几个词儿技术就会变厉害似的。
下面看一个有趣的对话:
甲:你会MQ嘛?
我:消息队列就消息队列呗,还M什么Q呀 ,别问我我不会!
甲:…教教我
其实我当时真不会消息队列,但是我认为这类的问题,我可以花一个小时的时间阅读一下资料就能解决的问题。要知道一个开源的Java,他的东西那么那么多,你没有学完的时候,所以对于Java工程师而言,我觉得做的最多的还是现学现用!活学活用!随用而学!
要知道没有什么问题是看一遍文档解决不了的,如果有,那就看两遍!
好了一不小心又给跑题了,接下来回到正题,说一下这个MVC框架的核心:
另外,他的核心呢,就是一个Servlet!(这点儿和Spring MVC挺像)
当然,他也有属于自己的Filter,而且我已经定义好了Filter注解了,但是我还没有给他加上任何功能。
所以我想到这里大家应该知道怎么配这个框架了吧!
先不说怎么配,把这个核心的Servlet代码给大家亮一下:
package com.aiyi.mvc;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.aiyi.mvc.annotations.MVCController;
import com.aiyi.mvc.annotations.MVCFilter;
import com.aiyi.mvc.annotations.MVCJson;
import com.aiyi.mvc.annotations.MVCURL;
import com.aiyi.mvc.reflection.BeanContainer;
import com.aiyi.mvc.reflection.BeanUtil;
import com.aiyi.mvc.reflection.Model;
import com.aiyi.mvc.util.PropertiesUtil;
import com.aiyi.mvc.util.StringUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* MVC核心控制器
* @author 郭胜凯
* @time 2016年11月9日 上午11:23:38
* @addr blog.321aiyi.com
* @email [email protected]
*/
public class CoreServlet extends HttpServlet{
/**
*
*/
private static final long serialVersionUID = 5441344237169464974L;
private boolean isChinaseUincode = false;
private String contentCharset = "utf-8";
private Set<Class<?>> classes;
private String templateFrist = null;
private String templateSuffix = ".jsp";
private String contentPath = "";
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
Properties properites = new PropertiesUtil().getProperites("mvc/config.properties");
/**
* 中文Unicode编码
*/
isChinaseUincode = properites.getProperty("ChinaseUnicode", "false").equalsIgnoreCase("true");
/**
* 字符集
*/
contentCharset = properites.getProperty("ContentCharset", "UTF-8");
/**
* JSP模板前缀
*/
templateFrist = properites.getProperty("TemplateFrist", "/");
/**
* JSP模板后缀
*/
templateSuffix = properites.getProperty("TemplateSuffix", ".jsp");
classes = BeanUtil.getClasses(properites.getProperty("ClassPacketName"));
for (Class<?> c : classes) {
//实例化过滤器
MVCFilter filterAnm = c.getAnnotation(MVCFilter.class);
if (null != filterAnm) {
try {
String value = filterAnm.value();
if (null == value || "".equals(value)) {
value = "/*";
}
Object bean = c.newInstance();
BeanContainer.setBean("filter", value, bean);
} catch (InstantiationException | IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
super.init();
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, IllegalArgumentException {
// TODO Auto-generated method stub
//获取客户端请求路径和请求Method
contentPath = request.getContextPath() + "/";
String requestURI = request.getRequestURI().replace(contentPath, "");
String requestMehod = request.getMethod();
//根据请求路径,从Bean容器中获取对应处理方法
Method method = BeanContainer.getMethod(requestMehod, requestURI);
if (null == method) {
//容器中没有对应方法的实例,扫描注解并加载实例到容器中
for (Class<?> c : classes) {
MVCController controllerAnm = c.getAnnotation(MVCController.class);
if (null != controllerAnm) {
String controllerUri = controllerAnm.value();
if (requestURI.indexOf(controllerUri) != -1) {
Method[] methods = c.getMethods();
for (Method meth : methods) {
MVCURL urlAnm = meth.getAnnotation(MVCURL.class);
if (null != urlAnm) {
String reqUri = urlAnm.value();
if (controllerUri.charAt(0) == '/') {
controllerUri = controllerUri.substring(1);
}
//整理完整请求URI
if (controllerUri.charAt(controllerUri.length() - 1) == '/') {
if (reqUri.charAt(0) == '/') {
reqUri = controllerUri + reqUri.substring(1);
}else{
reqUri = controllerUri + reqUri;
}
}else{
if (requestURI.charAt(0) == '/') {
reqUri = controllerUri + reqUri;
}else{
reqUri = controllerUri + "/" + reqUri;
}
}
if (requestURI.charAt(requestURI.length() - 1) != '/' && requestURI.indexOf(".") != -1) {
requestURI = requestURI.substring(0, requestURI.lastIndexOf("."));
}
if (requestURI.equals(reqUri) && requestMehod.equalsIgnoreCase(urlAnm.method().name())) {
//找到对应方法,实例化到Bean容器中
BeanContainer.setMethod(urlAnm.method(), reqUri, meth);
Log.info("实例化方法 -" + urlAnm.method() + ":" + reqUri + "[" + meth + "]");
//如果该方法的类没有被实例化则实例化类到Bean容器中
Object bean = BeanContainer.getBean("controller", controllerUri);
if (null == bean) {
try {
bean = c.newInstance();
BeanContainer.setBean("controller", controllerUri, bean);
Log.info("实例化控制器 " + ":" + controllerUri + "[" + c.getName() + "]");
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
goToMethod(request, response, meth, bean);
return;
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
}
}else{
for (Class<?> c : classes) {
MVCController controllerAnm = c.getAnnotation(MVCController.class);
if (null != controllerAnm) {
String controllerUri = controllerAnm.value();
Object bean = BeanContainer.getBean("controller", controllerUri);
if (null == bean) {
try {
bean = c.newInstance();
BeanContainer.setBean("controller", controllerUri, bean);
Log.info("实例化控制器 " + ":" + controllerUri + "[" + c.getName() + "]");
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
goToMethod(request, response, method, bean);
return;
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
response.sendError(404);
// super.service(request, response);
}
/**
* 反射执行控制器的URL处理方法
* @param request
* @param response
* @param method
* @param controller
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws IOException
* @throws ServletException
*/
private void goToMethod(HttpServletRequest request, HttpServletResponse response, Method method, Object controller)
throws IllegalArgumentException, InvocationTargetException, IOException, ServletException, IllegalAccessException{
response.setCharacterEncoding(contentCharset);
Class<?>[] parameterTypes = method.getParameterTypes();
//当该方法的参数列表是空的时候,直接调用
if (parameterTypes.length == 0) {
Object result = method.invoke(controller);
if (null != result) {
MVCJson jsonAnm = method.getAnnotation(MVCJson.class);
//标有Json注解并具有返回值的方法将返回值序列成Json并输出
if (null != jsonAnm) {
response.setContentType("Content-Type: application/json;charset" + contentCharset);
String jsonResult = null;
if (isChinaseUincode) {
jsonResult = StringUtil.chinaToUnicode(new ObjectMapper().writeValueAsString(result));
}else{
jsonResult = new ObjectMapper().writeValueAsString(result);
}
response.getWriter().print(jsonResult);
return;
}else if (result instanceof String) {
//否则返回String文本,该文本对应一个Jsp模板路径
String templatePath = String.valueOf(result);
if (templatePath.charAt(0) == '/') {
if (!templateFrist.equals("/")) {
if (templateFrist.charAt(templateFrist.length() - 1) == '/') {
templatePath = templateFrist + templatePath.substring(1);
}else{
templatePath = templateFrist + templatePath;
}
}
}else{
if (templateFrist.charAt(templateFrist.length() - 1) == '/') {
templatePath = templateFrist + templatePath;
}else{
templatePath = templateFrist + "/" + templatePath;
}
}
templatePath += templateSuffix;
if (templatePath.charAt(0) != '/') {
templatePath = "/" + templatePath;
}
request.getRequestDispatcher(templatePath).forward(request, response);
return;
}else{
//以上两种都不是,直接404
response.sendError(404);
return;
}
}
}else{
//Controller中带有参数
Object[] params = new Object[parameterTypes.length];
String[] methodParamNames = MethodUtil.getMethodParameterNamesByAsm4(controller.getClass(), method);
Map<String, String[]> parameterMap = request.getParameterMap();
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i].equals(HttpServletRequest.class)) {
params[i] = request;
methodParamNames[i] = null;
continue;
}
if (parameterTypes[i].equals(HttpServletResponse.class)) {
params[i] = response;
methodParamNames[i] = null;
continue;
}
if (parameterTypes[i].equals(HttpSession.class)) {
params[i] = request.getSession();
methodParamNames[i] = null;
continue;
}
if (parameterTypes[i].equals(Model.class)) {
params[i] = new Model(request);
methodParamNames[i] = null;
continue;
}
//除了标准的一些Http处理参数外,还有一些额外的参数,有戏了,匹配用户请求参数名
if (null != methodParamNames[i]) {
String[] strings = parameterMap.get(methodParamNames[i]);
if (null != strings) {
if (strings.length == 1) {
if (parameterTypes[i].equals(Integer.class) || parameterTypes[i].equals(Integer.TYPE)) {
try {
params[i] = Integer.valueOf(strings[0]);
} catch (Exception e) {
// TODO: handle exception
throw new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");
}
continue;
}
if (parameterTypes[i].equals(Long.class) || parameterTypes[i].equals(Long.TYPE)) {
try {
params[i] = Long.valueOf(strings[0]);
} catch (Exception e) {
// TODO: handle exception
throw new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");
}
continue;
}
if (parameterTypes[i].equals(Double.class) || parameterTypes[i].equals(Double.TYPE)) {
try {
params[i] = Double.valueOf(strings[0]);
} catch (Exception e) {
// TODO: handle exception
throw new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");
}
continue;
}
if (parameterTypes[i].equals(Float.class) || parameterTypes[i].equals(Float.TYPE)) {
try {
params[i] = Float.valueOf(strings[0]);
} catch (Exception e) {
// TODO: handle exception
throw new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");
}
continue;
}
if (parameterTypes[i].equals(Short.class) || parameterTypes[i].equals(Short.TYPE)) {
try {
params[i] = Short.valueOf(strings[0]);
} catch (Exception e) {
// TODO: handle exception
throw new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");
}
continue;
}
if (parameterTypes[i].equals(Boolean.class) || parameterTypes[i].equals(Boolean.TYPE)) {
try {
params[i] = Boolean.valueOf(strings[0]);
} catch (Exception e) {
// TODO: handle exception
throw new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");
}
continue;
}
//上面的都没戏了,直接贴数据
params[i] = strings[0];
continue;
}
//有数组!例如复选框之类的表单!
//!!!待完善
else{
}
}
}
}
try {
Object result = method.invoke(controller, params);
if (null != result) {
MVCJson jsonAnm = method.getAnnotation(MVCJson.class);
//标有Json注解并具有返回值的方法将返回值序列成Json并输出
if (null != jsonAnm) {
response.setContentType("Content-Type: application/json;charset" + contentCharset);
String jsonResult = null;
if (isChinaseUincode) {
jsonResult = StringUtil.chinaToUnicode(new ObjectMapper().writeValueAsString(result));
}else{
jsonResult = new ObjectMapper().writeValueAsString(result);
}
response.getWriter().print(jsonResult);
return;
}else if (result instanceof String) {
//否则返回String文本,该文本对应一个Jsp模板路径
String templatePath = String.valueOf(result);
if (templatePath.charAt(0) == '/') {
if (!templateFrist.equals("/")) {
if (templateFrist.charAt(templateFrist.length() - 1) == '/') {
templatePath = templateFrist + templatePath.substring(1);
}else{
templatePath = templateFrist + templatePath;
}
}
}else{
if (templateFrist.charAt(templateFrist.length() - 1) == '/') {
templatePath = templateFrist + templatePath;
}else{
templatePath = templateFrist + "/" + templatePath;
}
}
templatePath += templateSuffix;
if (templatePath.charAt(0) != '/') {
templatePath = "/" + templatePath;
}
request.getRequestDispatcher(templatePath).forward(request, response);
return;
}else{
//以上两种都不是,直接404
response.sendError(404);
return;
}
}
} catch (Exception e) {
// TODO: handle exception
throw new IllegalArgumentException("请求参数与服务器接收参数不匹配");
}
}
}
}
写道这里,时间又不早了,那么就先到这儿,我就不吊大家胃口了,如果对这个MVC感兴趣,的话我分享到GItHub了,有感兴趣的朋友可以直接在这里down到源码:https://git.coding.net/shengkai/MVC.git
如果有兴趣的朋友可以期待我的下一篇博客《玩儿转MVC之大屌牌儿MVC框架(二)》