作者:田超凡
时间:2019-08-23
Maven依赖新增javax.servlet依赖,用于编写的MVC核心控制器继承自HttpServlet并重写对应方法,调用对应ServletAPI方法实现
Maven依赖新增fastjson依赖处理json序列化和反序列化。
pom.xml配置如下:
主要实现原理简述:
(1).首先,定义模型封装类
Request封装请求方式和请求路径(注意,此处存放的请求路径是请求URL相对项目根目录的路径,调用request.getServletPath()获取)
Handle定义处理每次请求的控制器和方法,Request和Handle是一对一的关联
Param封装请求参数
View封装控制器处理方法执行完毕之后的视图解析结果,包括视图路径和绑定的模型参数
Data封装控制器处理方法执行完毕之后直接返回的响应数据,最后通过json序列化成json字符串然后写入输出流返回给前台。
(2).其次,定义ControllerHelper控制器助手类用于加载所有视图层的控制器并建立Request和Handle的关联映射
(3).最后,编写核心控制器并在web.xml中注册servlet(或者直接通过@WebServlet注解注册)
KidDispatcherServlet是整个MVC框架最核心的管理和控制器,作用等效于Spring MVC的DispatcherServlet核心控制器
对于静态资源拦截和过滤,可以通过配置默认的servlet并定义URL拦截规则实现。可在web.xml中直接配置default servlet,也可以在KidDispatcherServlet初始化方法中定义ServletRegistration并addMapping,两种方式均可用来过滤静态资源请求。
Request.java 封装请求方式和请求路径
package com.tcf.kid.smart.framework.mvc;
/***
* TODO TCF 请求信息类
* TODO TCF 封装请求方式和请求路径
* @author 71485
*
*/
public class Request {
//TODO TCF 请求方式
private String requestMethod;
//TODO TCF 请求路径
private String requestPath;
public String getRequestMethod() {
return requestMethod;
}
public String getRequestPath() {
return requestPath;
}
//TODO TCF 构造注入请求方式和请求路径
public Request(String requestMethod,String requestPath)
{
this.requestMethod=requestMethod;
this.requestPath=requestPath;
}
//TODO TCF 默认无参构造
public Request()
{
}
}
Handle.java 封装处理请求的控制器和处理方法
package com.tcf.kid.smart.framework.mvc;
import java.lang.reflect.Method;
/**
* TODO TCF 请求处理类
* TODO TCF 封装处理请求的控制器和处理方法
* @author 71485
*
*/
public class Handle {
//TODO TCF 处理请求控制器
private Class> handleController;
//TODO TCF 处理请求方法
private Method handleMethod;
public Class> getHandleController() {
return handleController;
}
public Method getHandleMethod() {
return handleMethod;
}
//TODO TCF 构造注入处理请求的控制器和处理方法
public Handle(Class> handleController,Method handleMethod)
{
this.handleController=handleController;
this.handleMethod=handleMethod;
}
//TODO TCF 默认无参构造
public Handle()
{
}
}
Param.java 封装请求参数
package com.tcf.kid.smart.framework.mvc;
import java.util.Map;
/**
* TODO TCF 封装请求参数
* @author 71485
*
*/
public class Param {
//TODO TCF 请求参数 ParameterName:ParameterValue
private Map
public Map
return paramMap;
}
//TODO TCF 构造注入请求参数
public Param(Map
{
this.paramMap=paramMap;
}
//TODO TCF 默认无参构造
public Param()
{
}
}
View.java 封装视图解析器
package com.tcf.kid.smart.framework.mvc;
import java.util.Map;
/***
* TODO TCF 视图解析器类
* TODO TCF 定义视图名称和绑定的模型参数
* @author 71485
*
*/
public class View {
//TODO TCF 视图名称
private String viewName;
//TODO TCF 绑定的模型参数
private Map
public String getViewName() {
return viewName;
}
public void setViewName(String viewName) {
this.viewName = viewName;
}
public Map
return modelParams;
}
public void setModelParams(Map
this.modelParams = modelParams;
}
}
Data.java 封装响应结果
package com.tcf.kid.smart.framework.mvc;
/***
* TODO TCF 输出响应数据类
* TODO TCF 控制器处理方法直接返回的相应数据,适用于ajax或直接写入输出流的情况
* @author 71485
*
*/
public class Data {
//TODO TCF 输出数据
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
//TODO TCF 构造注入输出数据
public Data(Object data)
{
this.data=data;
}
//TODO TCF 默认无参构造
public Data()
{
}
}
@KidController 标注和注册控制器
package com.tcf.kid.smart.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* TODO TCF 注册控制器组件
* @author 71485
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface KidController {
}
@KidAction 标注和匹配映射请求的控制器处理方法
package com.tcf.kid.smart.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/***
* TODO TCF 标注控制器处理方法
* @author 71485
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface KidAction {
String mapping();
String method();
}
package com.tcf.kid.smart.framework.helper;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.tcf.kid.smart.framework.annotation.KidAction;
import com.tcf.kid.smart.framework.annotation.KidController;
import com.tcf.kid.smart.framework.mvc.Handle;
import com.tcf.kid.smart.framework.mvc.Request;
/***
* TODO TCF 控制器助手类
* TODO TCF 扫描所有视图层控制器和处理方法,建立请求和处理的映射关系
* TODO TCF 初始化加载请求和处理的映射Map
* @author 71485
*
*/
public class ControllerHelper {
//TODO TCF 请求和处理的映射Map
public static Map
static
{
//TODO TCF 建立请求和处理之间的映射关系
initHandleMapping();
}
//TODO TCF 扫描控制器和处理方法并建立和请求的映射关系,初始化请求和处理之间的映射Map
public static void initHandleMapping()
{
try
{
//TODO TCF 加载控制器注解标注的控制器
Set
if(controllerClassList!=null && controllerClassList.size()>0)
{
for(Class> controllerClass:controllerClassList)
{
//TODO TCF 控制器中定义的所有方法
Method[] methods=controllerClass.getDeclaredMethods();
if(methods!=null && methods.length>0)
{
for(Method method:methods)
{
//TODO TCF 默认只有使用KidAction注解标注的类才是控制器处理方法
if(method.isAnnotationPresent(KidAction.class))
{
KidAction action=method.getAnnotation(KidAction.class);
if(action!=null)
{
String actionUrl=action.mapping();
String actionMethod=action.method();
if(StringUtils.isNotEmpty(actionUrl) && StringUtils.isNotEmpty(actionMethod))
{
//TODO TCF 请求路径
String requestPath=actionUrl;
//TODO TCF 请求方式
String requestMethod=actionMethod.toLowerCase();
Request request=new Request(requestMethod,requestPath);
Handle handle=new Handle(controllerClass,method);
MAPPING_MAP.put(request,handle);
}
}
}
}
}
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
//TODO TCF 根据请求方式和请求路径获取处理请求信息
public static Handle getHandleByRequest(String requestMethod,String requestPath)
{
Request request=new Request(requestMethod,requestPath);
Handle handle=new Handle();
for(Map.Entry
{
Request entryRequest=mappingEntry.getKey();
if(entryRequest!=null)
{
if(requestMethod.equals(entryRequest.getRequestMethod())
&& requestPath.equals(entryRequest.getRequestPath()))
{
handle=mappingEntry.getValue();
break;
}
}
}
return handle;
}
}
package com.tcf.kid.smart.framework.core;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import com.tcf.kid.smart.framework.common.Const;
import com.tcf.kid.smart.framework.helper.BeanHelper;
import com.tcf.kid.smart.framework.helper.ClassHelper;
import com.tcf.kid.smart.framework.helper.ControllerHelper;
import com.tcf.kid.smart.framework.mvc.Data;
import com.tcf.kid.smart.framework.mvc.Handle;
import com.tcf.kid.smart.framework.mvc.Param;
import com.tcf.kid.smart.framework.mvc.View;
import com.tcf.kid.smart.framework.util.CodecUtil;
import com.tcf.kid.smart.framework.util.JsonUtil;
import com.tcf.kid.smart.framework.util.PropertiesUtil;
import com.tcf.kid.smart.framework.util.ReflectUtil;
import com.tcf.kid.smart.framework.util.StreamUtil;
/***
* TODO TCF MVC核心处理请求控制器
* @author 71485
*
*/
@WebServlet
public class KidDispatcherServlet extends HttpServlet{
//TODO TCF 初始化
@Override
public void init(ServletConfig servletConfig) throws ServletException
{
//TODO TCF 启动IOC/AOP容器,扫包
ClassHelper.loadCoreClass();
//TODO TCF 注册Servlet资源服务(各类视图/其他静态资源)
ServletContext servletContext=servletConfig.getServletContext();
//TODO TCF HTML视图资源
ServletRegistration viewRegistration=servletContext.getServletRegistration(Const.STATIC_RESOURCE_TYPE.JSP);
viewRegistration.addMapping(PropertiesUtil.loadPropertiesFile(Const.PROPERTIES_FILES.BASE_PROPERTIES).getProperty(Const.BASE_PROPERTIES_KEYS.VIEW_RESOURCE));
//TODO TCF ...其他视图资源
//TODO TCF 其他静态资源
ServletRegistration staticRegistration=servletContext.getServletRegistration(Const.STATIC_RESOURCE_TYPE.DEFAULT);
staticRegistration.addMapping(PropertiesUtil.loadPropertiesFile(Const.PROPERTIES_FILES.BASE_PROPERTIES).getProperty(Const.BASE_PROPERTIES_KEYS.STATIC_RESOURCE));
staticRegistration.addMapping("*.js");
}
//TODO TCF 服务
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
try
{
//TODO TCF 请求方式
String requestMethod=request.getMethod().toLowerCase();
//TODO TCF 请求路径
//TODO TCF request.getServletPath()获取的是相对项目的纯请求路径,如/show.do
String requestPath=request.getServletPath();
if(StringUtils.isNotEmpty(requestMethod) && StringUtils.isNotEmpty(requestPath))
{
//TODO TCF 根据请求信息获取处理信息
Handle handle=ControllerHelper.getHandleByRequest(requestMethod,requestPath);
if(handle!=null)
{
//TODO TCF 处理请求的控制器
Class> handleController=handle.getHandleController();
//TODO TCF 处理请求的方法
Method handleMethod=handle.getHandleMethod();
//TODO TCF 处理请求的控制器实例
Object handleControllerInstance=BeanHelper.getBeanInstance(handleController);
//TODO TCF 请求参数名
Enumeration
if(parameterNames!=null)
{
//TODO TCF 请求参数Map
Map
while(parameterNames.hasMoreElements())
{
//TODO TCF 请求参数名
String parameterName=parameterNames.nextElement();
//TODO TCF 根据请求参数名获取请求参数
String parameterValue=request.getParameter(parameterName);
paramMap.put(parameterName,parameterValue);
}
//TODO TCF 封装请求参数
Param param=new Param(paramMap);
//TODO TCF 请求属性
String requestUrl=CodecUtil.encodeStr(StreamUtil.readInputStream(request.getInputStream()));
if(StringUtils.isNotEmpty(requestUrl))
{
String[] parameters=requestUrl.split("&");
if(parameters!=null && parameters.length>0)
{
for(String parameter:parameters)
{
//TODO TCF 解析参数名和参数值
String[] params=parameter.split("=");
if(params!=null && params.length==2)
{
//TODO TCF 参数名
String paramName=params[0];
//TODO TCF 参数值
String paramValue=params[1];
//TODO TCF 存入请求作用域
request.setAttribute(paramName,paramValue);
}
}
}
}
if(handleController!=null && handleMethod!=null)
{
//TODO TCF 调用对应的处理请求控制器的处理方法
Object invokeResult=ReflectUtil.invokeMethod(handleControllerInstance,handleMethod,param);
//TODO TCF 根据控制器处理方法执行之后的返回结果进行下一步处理
if(invokeResult!=null)
{
if(invokeResult instanceof View)
{
//TODO TCF 返回视图
View view=(View)invokeResult;
if(view!=null)
{
//TODO TCF 返回视图名
String viewName=view.getViewName();
if(StringUtils.isNotEmpty(viewName))
{
if(viewName.startsWith("/"))
{
//TODO TCF 响应方式:重定向
response.sendRedirect(PropertiesUtil.loadPropertiesFile(Const.PROPERTIES_FILES.BASE_PROPERTIES).getProperty(Const.BASE_PROPERTIES_KEYS.VIEW_RESOURCE)+viewName);
}
else
{
//TODO TCF 响应方式:转发,解析模型参数
//TODO TCF 返回视图并传递的渲染视图的模型参数
Map
if(modelParams!=null)
{
for(Map.Entry
{
//TODO TCF 需要绑定的模型参数名
String modelParamName=paramEntry.getKey();
//TODO TCF 需要绑定的模型参数值
Object modelParamValue=paramEntry.getValue();
//TODO TCF 存入request请求作用域
request.setAttribute(modelParamName,modelParamValue);
}
}
//TODO TCF 转发
request.getRequestDispatcher(
PropertiesUtil.loadPropertiesFile(Const.PROPERTIES_FILES.BASE_PROPERTIES)
.getProperty(Const.BASE_PROPERTIES_KEYS.VIEW_RESOURCE)
+viewName)
.forward(request,response);
}
}
}
}
else if (invokeResult instanceof Data)
{
//TODO TCF 直接返回响应数据(如ajax等直接将响应数据写入输出流)
Data data=(Data)invokeResult;
if(data!=null)
{
//TODO TCF JSON序列化
String jsonString=JsonUtil.pojoToJson(data.getData());
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
PrintWriter writer=response.getWriter();
writer.write(jsonString);
//TODO TCF 清空缓冲区,输出响应结果
writer.flush();
writer.close();
}
}
}
}
}
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
package com.tcf.kid.smart.framework.demo;
import java.util.HashMap;
import java.util.Map;
import com.tcf.kid.smart.framework.annotation.KidAction;
import com.tcf.kid.smart.framework.annotation.KidController;
import com.tcf.kid.smart.framework.mvc.Data;
import com.tcf.kid.smart.framework.mvc.Param;
import com.tcf.kid.smart.framework.mvc.View;
/***
* TODO TCF 处理请求的控制器
* @author 71485
*
*/
@KidController
public class TestController {
//TODO TCF EXAMPLE 1 直接返回视图解析器解析模型参数
@KidAction(mapping="/toPage.do",method="GET")
public View toPage(Param param)
{
View view=new View();
view.setViewName("example.jsp");
return view;
}
//TODO TCF EXAMPLE 2 直接返回响应结果
@KidAction(mapping="/show.do",method="GET")
public Data showProxyResult(Param param)
{
System.out.println("====代理的目标方法执行====");
System.out.println(param.getParamMap().get("message")!=null?param.getParamMap().get("message").toString():"");
Map
paramMap.put("name","zhangsan");
return new Data(paramMap);
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
运行结果图:
输入toPage.do
后台日志: