框架参考自:

https://www.bilibili.com/video/BV1gV411r7ct

在老师的基础上添加了

1、POST参数处理

2、Tomcat8版本下中文乱码处理

3、可声明请求方式

框架需要的全部依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>cn.jack2048</groupId>
<artifactId>MVC-Framework</artifactId>
<version>1.0-SNAPSHOT</version> <properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties> <packaging>jar</packaging> <dependencies> <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency> <dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.7.0</version>
</dependency> <dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.26.0-GA</version> <!-- <version>3.12.1-GA</version> -->
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency> <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency> <dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency> <dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency> <!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version> <!-- <version>0.9.12</version> 此版本 Reflections已经不适用了字符串构造 -->
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency> </dependencies> </project>

注解一栏:

@Controller仅用于标记注册类

package cn.jack2048.framework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 只能注解在类上面
* 用于标识为Controller,提供给Servlet进行调用
*
*/

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}

@RequestMapping标记

package cn.jack2048.framework.annotation;

import cn.jack2048.framework.constant.MethodType;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 支持在类上和方法上配置映射路径 和设置请求方式
*/

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value() default "";
MethodType methodType() default MethodType.GET;
}

请求方式枚举类:

package cn.jack2048.framework.constant;

/**
* 定义请求方式
* MethodType
*
*/

public enum MethodType {
GET("doGet"), POST("doPost"), DELETE("doDelete"), PUT("doPut") , HEAD("doHEAD") , TRACE("doTrace"); private final String type; MethodType(String type) {
this.type = type;
} public String getType() {
return type;
}
}

中文乱码过滤器:

过滤器参考自乐字节

https://www.bilibili.com/video/BV1SP4y147wJ?p=69

对于Tomcat版本的确认进行了改进

package cn.jack2048.framework.filter;

import cn.jack2048.framework.constant.ServletConstants;
import cn.jack2048.framework.util.RequestWrapper;
import cn.jack2048.framework.util.ServletUtil; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException; /**
* @WebFilter(value = {"/*", "/**"})
* 过滤器注解不能决定过滤链的顺序,不推荐使用注解过滤器,所以还是要手动配置
*/
public class CharacterEncodingFilter implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException { } /**
* 乱码处理
*
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest; String method = req.getMethod(); String tomcatMajorVersion = ServletUtil.getTomcatMajorVersion(req);
Integer tmv = Integer.valueOf(tomcatMajorVersion); // 如果Tomcat版本小于8 (8以下的版本) 且为 GET请求
if (8 > tmv && ServletConstants.REQUEST_METHOD_GET.equalsIgnoreCase(method)) {
RequestWrapper requestWrapper = new RequestWrapper(req); filterChain.doFilter(requestWrapper, servletResponse);
return;
} /**
* Tomcat所有版本都不会处理非GET请求的乱码,默认设置即可
*
*/
if (! ServletConstants.REQUEST_METHOD_GET.equals(method)) {
req.setCharacterEncoding(ServletConstants.CHARSET_TYPE_UTF8);
}
filterChain.doFilter(req, servletResponse);
} @Override
public void destroy() { }
}

主版本获取方法:

    /**
* 获取Tomcat主版本号
* @param request
* @return
*/
public static String getTomcatMajorVersion(HttpServletRequest request) {
ServletContext servletContext = request.getServletContext();
String serverInfo = servletContext.getServerInfo(); // Apache Tomcat/8.5.70 获取完整版本信息
int i = serverInfo.indexOf("/");
serverInfo = serverInfo.substring(i + 1); // 8.5.70 获取版本号
int i2 = serverInfo.indexOf(".");
String majorVersion = serverInfo.substring(0, i2); // 8 获取主版本号 return majorVersion;
}

Wrrapper包装类:

package cn.jack2048.framework.util;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.nio.charset.StandardCharsets; public class RequestWrapper extends HttpServletRequestWrapper { private HttpServletRequest httpServletRequest; public RequestWrapper(HttpServletRequest request) {
super(request);
this.httpServletRequest = request;
} /**
* GET 请求重写Parameter方法处理
* @param name
* @return
*/
@Override
public String getParameter(String name) {
String value = this.httpServletRequest.getParameter(name); if(null != value && !"".equals(value)) {
try {
value = new String(value.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
} catch (Exception exception) {
exception.printStackTrace();
}
} return value;
}
}

注解的作用在于被反射机制获取到被注解标注的类,以方便保存反射需要使用的信息

package cn.jack2048.framework.util;

import cn.jack2048.framework.annotation.Controller;
import cn.jack2048.framework.annotation.RequestMapping;
import cn.jack2048.framework.constant.MethodType;
import cn.jack2048.framework.model.Mapping;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtMethod;
import javassist.bytecode.AttributeInfo;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.reflections.Reflections; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*; public class ClassUtil { public static Set<Class<?>> generalReferType; // 基本常用类型
public static Set<String> allBasicType; // 基本数据类型
public static Set<Class<?>> dateType; // 日期类型
private static Map<String, Mapping> allMapping; // 创建集中容器 static {
allMapping = getAllRequestMapping("cn.jack2048.controller"); generalReferType = new HashSet<>();
generalReferType.add(String.class);
generalReferType.add(Byte.class);
generalReferType.add(Character.class);
generalReferType.add(Short.class);
generalReferType.add(Integer.class);
generalReferType.add(Long.class);
generalReferType.add(BigDecimal.class);
generalReferType.add(Float.class);
generalReferType.add(Double.class);
generalReferType.add(Boolean.class); allBasicType = new HashSet<>();
allBasicType.add("boolean");
allBasicType.add("byte");
allBasicType.add("char");
allBasicType.add("short");
allBasicType.add("int");
allBasicType.add("long");
allBasicType.add("float");
allBasicType.add("double"); dateType = new HashSet<>();
dateType.add(Date.class);
dateType.add(LocalDate.class);
dateType.add(LocalDateTime.class);
} public static Map<String, Mapping> getAllRequestMapping(String packagePath) {
try {
Reflections reflections = new Reflections(packagePath); Map<String, Mapping> mappingMap = new HashMap<>(); ClassPool classPool = ClassPool.getDefault(); // 获取指定包名下面所有的带有@Controller注解的类对象
Set<Class<?>> controllerClassSet = reflections.getTypesAnnotatedWith(Controller.class); // 对每一个@Controller注解的类处理
for (Class<?> controllerClass : controllerClassSet) {
// System.out.println(controllerClass.getName()); Object newInstance = controllerClass.newInstance(); String baseUri = ""; // 检查这个类是否还有@RequestMapping注解
RequestMapping requestMappingForClass = controllerClass.getDeclaredAnnotation(RequestMapping.class); // 如果类中有此注解
if (null != requestMappingForClass) {
baseUri = requestMappingForClass.value();
} // 获取所有私有方法
Method[] declaredMethods = controllerClass.getDeclaredMethods(); // 寻找带有@RequestMapping注解的方法
for (Method declaredMethod : declaredMethods) { RequestMapping requestMappingForMethod = declaredMethod.getAnnotation(RequestMapping.class);
if (null == requestMappingForMethod) continue; // 该方法没有标注此注解,直接忽略 String methodUri = requestMappingForMethod.value(); // 有此注解,获取对应的uri
MethodType methodType = requestMappingForMethod.methodType(); // 有此注解,获取对应的请求方法 final String FULL_URI = baseUri + methodUri; // 拼接完整uri // 除了方法之外,方法的参数信息也需要进行保存
classPool.insertClassPath(new ClassClassPath(controllerClass)); CtMethod ctMethod = classPool.getMethod(controllerClass.getName(), declaredMethod.getName()); MethodInfo methodInfo = ctMethod.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
AttributeInfo attribute = codeAttribute.getAttribute(LocalVariableAttribute.tag);
LocalVariableAttribute attribute2 = (LocalVariableAttribute) attribute; Map<String, Class<?>> methodParams = new LinkedHashMap<>(); // 需要保证顺序 Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); if (null != attribute2) {
int pos = Modifier.isStatic( ctMethod.getModifiers()) ? 0 : 1; // for (int i = 0; i < declaredMethod.getParameterCount(); i++) {
for (int i = 0; i < declaredMethod.getParameterCount(); i++) {
String paraName = attribute2.variableName(i + pos);
methodParams.put(paraName, parameterTypes[i]);
}
} mappingMap.put(FULL_URI, new Mapping(newInstance, declaredMethod, methodType, methodParams));
}
}
return mappingMap;
} catch (Exception exception) {
exception.printStackTrace();
return null;
}
} public static Mapping getRequestMapping(String uri) {
return allMapping.get(uri);
} }

用于存储调用方法的实体类Bean:

package cn.jack2048.framework.model;

import cn.jack2048.framework.constant.MethodType;

import java.lang.reflect.Method;
import java.util.Map; /**
* 用于存储 url路径对应的类和方法?
*
*/
public class Mapping { private Object Object; // 需要执行的Controller实例
private Method method; // 需要执行的Controller实例方法
private MethodType methodType; // 该方法声明的请求方式 // key代表方法名称 value表示数据类型 约定要求:不允许使用重载!
private Map<String, Class<?>> methodParams; // 方法的参数列表信息 public Mapping(java.lang.Object object, Method method, MethodType methodType, Map<String, Class<?>> methodParams) {
Object = object;
this.method = method;
this.methodType = methodType;
this.methodParams = methodParams;
} public java.lang.Object getObject() {
return Object;
} public Method getMethod() {
return method;
} public MethodType getMethodType() {
return methodType;
} public Map<String, Class<?>> getMethodParams() {
return methodParams;
} }

核心中枢:派发Servlet

这里处理的东西有点多

1、根据uri匹配寻找对应封装对象

2、执行前的校验,例如路径是否符合, 请求方式是否符合

3、封装请求参数(Post参数)

4、为了做参数可扩展,对参数注入写了一大段包装参数的逻辑在其中

5、调用封装对象携带的要执行方法(开发的业务逻辑)

6、执行完成之后对返回结果要处理的信息(字符串 走 重定向 和转发, 其他类型一律转JSON返回)

老师还是非常厉害的,最后还特意对数组类型做了处理,支持了逗号分割

我自己写为了精炼表达内容,和老师视频里面的写法就不一样了

package cn.jack2048.framework.servlet;

import cn.jack2048.framework.model.Mapping;
import cn.jack2048.framework.util.ClassUtil;
import cn.jack2048.framework.util.DateConverter;
import cn.jack2048.framework.util.DateUtil;
import cn.jack2048.framework.util.ServletUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils; import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*; import static cn.jack2048.framework.constant.ServletConstants.POST_PARAM_KEY; public class DispatchServlet extends HttpServlet { // private Class<?> thisClass = this.getClass(); private static Mapping currentMapping;
private static Map<String, Mapping> allRequestMapping;
public static final String REDIRECT = "redirect:";
public static String dispatchPath = "/WEB-INF/jsp/";
public static String fileSuffix = ".jsp"; @Override
public void init(ServletConfig config) throws ServletException {
super.init(config); // 注册转换器
ConvertUtils.register(DateConverter.dateConverter, Date.class);
ConvertUtils.register(DateConverter.dateConverter, LocalDate.class);
ConvertUtils.register(DateConverter.dateConverter, LocalDateTime.class); String cp = config.getInitParameter("controllerPackage");
String dp = config.getInitParameter("dispatchPath");
String fs = config.getInitParameter("fileSuffix");
allRequestMapping = ClassUtil.getAllRequestMapping(null == cp ? "cn.jack2048.controller" : cp);
if (dp != null && !"".equals(dp)) dispatchPath = dp.trim();
if (fs != null && !"".equals(fs)) fileSuffix = fs.trim(); } private Object parseToCustomType(Class<?> parameterType, HttpServletRequest request) {
Object o = null;
try {
o = parameterType.newInstance();
BeanUtils.populate(o, request.getParameterMap());
BeanUtils.populate(o, (Map) request.getAttribute(POST_PARAM_KEY));
} catch (Exception e) {
e.printStackTrace();
}
return o;
} /**
* 对于其他类型参数专门定义一个方法进行处理
* @param parameterType
* @return
*/
private Object processingOtherType(Class<?> parameterType, String paramName, HttpServletRequest request) throws ParseException { // 请求对象解析请求参数名称,返回该参数值
String paramValue = request.getParameter(paramName);
String typeName = parameterType.getTypeName(); Map<String, Object> postParam = (Map<String, Object>)request.getAttribute(POST_PARAM_KEY); Object paramValueInPost = postParam.get(paramName); Class<?> componentType = parameterType.getComponentType(); // 获取组合类型
String[] values = request.getParameterValues(paramName); // 请求存放的一组值 boolean isBasicType = ClassUtil.allBasicType.contains(typeName);
boolean isGeneralReferType = ClassUtil.generalReferType.contains(parameterType);
boolean isDateType = ClassUtil.dateType.contains(parameterType);
boolean isComponentType = null != componentType;
boolean isEmptyValue = (null == paramValue) || "".equals(paramValue);
boolean isEmptyPostValue = (null == paramValueInPost) || "".equals(paramValueInPost); Object param = null; // 上述类型全不不匹配 且get参数和post参数也找不到此名称,则执行此自定义引用类型处理
if (isEmptyValue && isEmptyPostValue && !isBasicType && !isGeneralReferType && ! isDateType && !isComponentType) param = parseToCustomType(parameterType, request); if (isEmptyValue && !isEmptyPostValue) { // 如果get参数找不到,但是post参数有,也就是说get有,就不用post
paramValue = paramValueInPost.toString();
} paramValue = paramName.trim();
if (isBasicType) {
// 基本数据类型处理
param =
"int".equals(typeName) ? Integer.valueOf(paramValue).intValue()
: "long".equals(typeName) ? Long.valueOf(paramValue).longValue()
: "short".equals(typeName) ? Short.valueOf(paramValue).shortValue()
: "byte".equals(typeName) ? Byte.valueOf(paramValue).byteValue()
: "boolean".equals(typeName) ? Boolean.valueOf(paramValue).booleanValue()
: "float".equals(typeName) ? Float.valueOf(paramValue).floatValue()
: "double".equals(typeName) ? Double.valueOf(paramValue).doubleValue()
: "char".equals(typeName) ? Character.valueOf(paramValue.charAt(0)) : 0;
}
else if (isGeneralReferType) {
// 一般对象类型处理
param =
parameterType == String.class ? paramValue
: parameterType == BigDecimal.class ? BigDecimal.valueOf(Long.valueOf(paramValue))
: parameterType == Long.class ? Long.valueOf(paramValue)
: parameterType == Integer.class ? Integer.valueOf(paramValue)
: parameterType == Double.class ? Double.valueOf(paramValue)
: parameterType == Float.class ? Float.valueOf(paramValue)
: parameterType == Short.class ? Short.valueOf(paramValue)
: parameterType == Byte.class ? Byte.valueOf(paramValue)
: parameterType == Boolean.class ? Boolean.valueOf(paramValue)
: parameterType == Character.class ? paramValue.charAt(0) : null;
}
else if (isDateType) {
// 日期格式判断
param =
parameterType == java.util.Date.class && DateUtil.PATTEN_DATE.matcher(paramValue).matches() ? DateUtil.parseToDate(paramValue, DateUtil.FORMAT_DATE)
: parameterType == java.util.Date.class && DateUtil.PATTEN_DATETIME.matcher(paramValue).matches() ? DateUtil.parseToDate(paramValue, DateUtil.FORMAT_DATETIME)
: parameterType == java.time.LocalDate.class && DateUtil.PATTEN_DATE.matcher(paramValue).matches() ? DateUtil.parseToLocalDate(paramValue)
: parameterType == java.time.LocalDateTime.class && DateUtil.PATTEN_DATETIME.matcher(paramValue).matches()? DateUtil.parseToLocalDateTime(paramValue)
: null;
} // 数组类型,条件 1反射得到的参数是组合类型, 2请求得到的参数组不能空 3参数组个数大于0
else if (isComponentType && null != values && values.length > 0) {
int len = values.length; // if (! ClassUtil.generalReferType.contains(componentType)) return null;
if (componentType == String.class) {
List<String> stringList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) stringList.add(s);
}
param = stringList.toArray(new String[stringList.size()]);
}
else if (componentType == Integer.class) {
List<Integer> integerList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) integerList.add(Integer.valueOf(s));
}
param = integerList.toArray(new Integer[integerList.size()]);
}
else if (componentType == Long.class) {
List<Long> longList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) longList.add(Long.valueOf(s));
}
param = longList.toArray(new Long[longList.size()]);
}
else if (componentType == Character.class) {
List<Character> characterList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) characterList.add(Character.valueOf(s.charAt(0)));
}
param = characterList.toArray(new Character[characterList.size()]);
}
else if (componentType == Double.class) {
List<Double> doubleList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) doubleList.add(Double.valueOf(s));
}
param = doubleList.toArray(new Double[doubleList.size()]);
}
else if (componentType == Float.class) {
List<Float> floatList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) floatList.add(Float.valueOf(s));
}
param = floatList.toArray(new Float[floatList.size()]);
}
else if (componentType == BigDecimal.class) {
List<BigDecimal> bigDecimalList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) bigDecimalList.add(BigDecimal.valueOf(Long.valueOf(s)));
}
param = bigDecimalList.toArray(new BigDecimal[bigDecimalList.size()]);
}
else if (componentType == Short.class) {
List<Short> shortList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) shortList.add(Short.valueOf(s));
}
param = shortList.toArray(new Short[shortList.size()]);
}
else if (componentType == Byte.class) {
List<Byte> byteList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) byteList.add(Byte.valueOf(s));
}
param = byteList.toArray(new Byte[byteList.size()]);
}
else if (componentType == Date.class) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateUtil.FORMAT_DATE); List<Date> dateList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) dateList.add(simpleDateFormat.parse(s)); // 这里有异常,让方法抛出去了
}
param = dateList.toArray(new Date[dateList.size()]);
}
else if (componentType == LocalDate.class) {
List<LocalDate> localDateList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) localDateList.add(LocalDate.parse(s, DateTimeFormatter.ofPattern(DateUtil.FORMAT_DATE)));
}
param = localDateList.toArray(new LocalDate[localDateList.size()]);
}
else if (componentType == LocalDateTime.class) {
List<LocalDateTime> localDateTimeList = new ArrayList<>();
for (int i = 0; i < len; i++) {
String[] split = values[i].split(","); // 需要支持逗号分割处理
for (String s : split) localDateTimeList.add(LocalDateTime.parse(s, DateTimeFormatter.ofPattern(DateUtil.FORMAT_DATETIME))); // 这里有异常,让方法抛出去了
}
param = localDateTimeList.toArray(new LocalDateTime[localDateTimeList.size()]);
} } return param;
} @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
try {
System.out.println("doGet detected"); // 得到此映射的要执行的方法
Method currentMappingMethod = this.currentMapping.getMethod();
Map<String, Class<?>> methodParams = this.currentMapping.getMethodParams(); // 参数处理
Object result = null;
int parameterCount = currentMappingMethod.getParameterCount();
if (0 != parameterCount) { // 如果存在参数
Object[] paramList = new Object[parameterCount]; // 预备入参容器
int idx = 0;
for (String paramName : methodParams.keySet()) { Class<?> paramType = methodParams.get(paramName); paramList[idx ++] =
// request对象和response对象方法要求了,就直接丢进去
paramType == HttpServletRequest.class ? req
: paramType == HttpServletResponse.class ? resp // 其他要做特殊处理
: processingOtherType(paramType, paramName, req);
} result = currentMappingMethod.invoke(currentMapping.getObject(), paramList); } else { // 无参直接获取
result = currentMappingMethod.invoke(currentMapping.getObject());
} // 不做参数处理, 约定只有Request & Response 直接调用
// Object invoke = currentMappingMethod.invoke(currentMapping.getObject(), req, resp); // 对结果的处理
if (null == result) return;
else if (result instanceof String) {
String string = (String) result; // 先判断是不是重定向
if (string.startsWith(REDIRECT)) { String path = string.substring(REDIRECT.length());
// 再判断是不是外部链接?
if( path.startsWith("http")) {
resp.sendRedirect(path);
return;
} resp.sendRedirect(req.getContextPath() + path);
return;
} // 默认是做跳转处理 跳转不支持对html静态文件处理乱码
// req.getRequestDispatcher(string).forward(req, resp);
req.getRequestDispatcher(this.dispatchPath + string + this.fileSuffix).forward(req, resp); return;
} else { // 剩余类型全部转换成JSON字符串
resp.setContentType("application/json; charset=utf-8;");
String jsonString = JSON.toJSONString(result);
PrintWriter writer = resp.getWriter();
writer.write(jsonString); return;
} } catch (Exception e) {
e.printStackTrace();
}
} @Override
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doHead detected");
this.doGet(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost detected");
this.doGet(req, resp);
} @Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPut detected");
this.doGet(req, resp);
} @Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doDelete detected");
this.doGet(req, resp);
} @Override
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doOptions detected");
this.doGet(req, resp);
} @Override
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doTrace detected");
this.doGet(req, resp);
} @Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try { // 获取请求的URI
String requestURI = req.getRequestURI();
requestURI = requestURI.replace(req.getContextPath(), "");// 移除工程路径 // 根据此路径获取对应的映射模型
Mapping mapping = this.allRequestMapping.get(requestURI); resp.setHeader("Content-Type", "text/html; charset=utf-8;"); // 响应中文乱码处理, 请求乱码还没处理,放在过滤器中实现 // 用户不一定正确请求到Uri, 无法访问则需要走404
if (null == mapping) {
// resp.setStatus(404);
PrintWriter writer = resp.getWriter();
writer.write("请求的路径不存在:404" + requestURI);
writer.close();
return;
}
this.currentMapping = mapping; // 获取来自请求的请求方法
String methodFromRequest = req.getMethod();
// 获取映射模型描述的请求方法
String methodFromMapping = mapping.getMethodType().toString(); // 获取对应要执行的方法名称
String methodName = mapping.getMethodType().getType(); // 如果并不匹配请求方法
if(!methodFromMapping.equals(methodFromRequest)) {
Class<BackupServlet> backupServletClass = BackupServlet.class; Method declaredMethod = backupServletClass.getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
declaredMethod.invoke(backupServletClass.newInstance(), req, resp);
return;
} // 通过@MethodType注解存储的名称 来调用具体某一个请求方式的方法
Method doMethod = DispatchServlet.class.getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
// doMethod.invoke(this, req, resp); Map<String, Object> postParam = ServletUtil.getPostParam(req);
req.setAttribute(POST_PARAM_KEY, postParam); // DispatchServlet instance = DispatchServlet.class.newInstance();
// instance.currentMapping = mapping;
doMethod.invoke(this, req, resp); } catch (Exception e) {
e.printStackTrace(); // 控制台输出异常信息
resp.setStatus(500);
PrintWriter writer = resp.getWriter(); writer.println("服务器内部异常:500");
e.printStackTrace(writer); writer.close();
}
} }

这里BackupServlet是因为 设置405不匹配请求方式的需要才设置的

因为反射机制不可以调用抽象类,所以写子类来套用

package cn.jack2048.framework.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; public class BackupServlet extends HttpServlet { @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
} @Override
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doHead(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
} @Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPut(req, resp);
} @Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doDelete(req, resp);
} @Override
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doOptions(req, resp);
} @Override
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doTrace(req, resp);
}
}

关于自定义类型处理的补充

自定义类型在老师的视频中使用了BeanUtil自动装填参数

但是日期类型是不支持的,这里是我对照的一点写法

package cn.jack2048.framework.util;

import org.apache.commons.beanutils.Converter;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date; public class DateConverter implements Converter { @Override
public Object convert(Class aClass, Object o) {
try {
if (null == o || "".equals(o)) return null;
if (o instanceof String) {
String s = o.toString().trim();
if (aClass.equals(Date.class)) return new SimpleDateFormat(DateUtil.FORMAT_DATE).parse(s);
else if (aClass.equals(LocalDate.class)) return LocalDate.parse(s, DateTimeFormatter.ofPattern(DateUtil.FORMAT_DATE));
else if (aClass.equals(LocalDateTime.class)) return LocalDateTime.parse(s, DateTimeFormatter.ofPattern(DateUtil.FORMAT_DATETIME));
}
return o;
} catch (Exception e) {
e.printStackTrace();
return null;
}
} public static DateConverter dateConverter = new DateConverter(); }

日期工具类:

package cn.jack2048.framework.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern; public class DateUtil { // 格式1 yyyy-MM-dd
public static final String FORMAT_DATE = "yyyy-MM-dd";
public static final String FORMAT_REGEXP_DATE = "\\d{4}-\\d{2}-\\d{2}";
public static final Pattern PATTEN_DATE = Pattern.compile(FORMAT_REGEXP_DATE); // 格式2 yyyy-MM-dd HH:mm:ss
public static final String FORMAT_DATETIME = "yyyy-MM-dd HH:mm:ss";
public static final String FORMAT_REGEXP_DATETIME = "\\d{4}-\\d{2}-\\d{2}\\s{1}\\d{2}:\\d{2}:\\d{2}";
public static final Pattern PATTEN_DATETIME = Pattern.compile(FORMAT_REGEXP_DATETIME); public static Date parseToDate(String timeString, String dateFormat) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
try {
return simpleDateFormat.parse(timeString);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
} public static LocalDate parseToLocalDate(String timeString) {
return LocalDate.parse(timeString, DateTimeFormatter.ofPattern(FORMAT_DATE));
} public static LocalDateTime parseToLocalDateTime(String timeString) {
return LocalDateTime.parse(timeString, DateTimeFormatter.ofPattern(FORMAT_DATETIME));
} public static boolean matchDateFormat(String val) { // 编译正则表达式
Pattern pattern1 = Pattern.compile(FORMAT_REGEXP_DATE);
Pattern pattern2 = Pattern.compile(FORMAT_REGEXP_DATETIME); // 忽略大小写的写法
// Pattern pat = Pattern.compile(regEx, Pattern.CASE_INSENSITIVE); Matcher matcher1 = PATTEN_DATE.matcher(val);
Matcher matcher2 = PATTEN_DATETIME.matcher(val); return matcher1.matches() || matcher2.matches();
} }

封装Post参数摘要自某个博客,我也忘记了。。。

    /**
* 从POST请求中获取参数
* @param request
* @return
* @throws Exception
*/
public static Map<String, Object> getPostParam(HttpServletRequest request) throws Exception {
// 返回参数
Map<String, Object> params = new HashMap<>(); // 获取内容格式
String contentType = request.getContentType(); if (null == contentType || "".equals(contentType)) throw new ServletException("没有设置请求头项Content-Type!!!");
else contentType = contentType.split(";")[0]; // form表单格式 表单形式可以从 ParameterMap中获取
if (ServletConstants.CONTENT_TYPE_VALUE_URL_ENCODED2.equalsIgnoreCase(contentType)) {
// 获取参数
Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap != null) {
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
params.put(entry.getKey(), entry.getValue()[0]);
}
}
} // json格式 json格式需要从request的输入流中解析获取
if (ServletConstants.CONTENT_TYPE_VALUE_JSON2.equalsIgnoreCase(contentType)) {
// 使用 commons-io中 IOUtils 类快速获取输入流内容
String paramJson = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
Map parseObject = JSON.parseObject(paramJson, Map.class);
params.putAll(parseObject);
} return params ;
}

一些写了一点点的常量:

package cn.jack2048.framework.constant;

public interface ServletConstants {

    int HTTP_STATUS_REDIRECT = 302;

    String CONTENT_TYPE_KEY = "Content-Type";
String CONTENT_TYPE_VALUE_JSON = "application/json;charset=utf-8;";
String CONTENT_TYPE_VALUE_JSON2 = "application/json";
String CONTENT_TYPE_VALUE_URL_ENCODED = "application/x-www-form-urlencoded;charset=utf-8;";
String CONTENT_TYPE_VALUE_URL_ENCODED2 = "application/x-www-form-urlencoded";
String CONTENT_TYPE_VALUE_FORM_DATA = "multipart/form-data;charset=UTF-8;"; String REQUEST_METHOD_GET = "GET";
String REQUEST_METHOD_POST = "POST";
String REQUEST_METHOD_DELETE = "DELETE";
String REQUEST_METHOD_PUT = "PUT"; String POST_PARAM_KEY = "postParam"; String CHARSET_TYPE_UTF8 = "UTF-8"; String STATIC_RESOURCE = "svg,png,jpg,jpeg,gif,bmp,css,js";
}

测试的Controller

package cn.jack2048.controller;

import cn.jack2048.framework.annotation.Controller;
import cn.jack2048.framework.annotation.RequestMapping;
import cn.jack2048.framework.constant.MethodType;
import cn.jack2048.framework.constant.ServletConstants;
import cn.jack2048.framework.servlet.DispatchServlet;
import cn.jack2048.model.Account; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map; @Controller
@RequestMapping("/test")
public class TestController { /**
* 成功跳转测试
* @param request
* @param response
*/
// @RequestMethod(MethodType.GET)
@RequestMapping("/testMethod")
public void testMethod(HttpServletRequest request, HttpServletResponse response) {
System.out.println("testMethod");
} /**
*
* POST请求测试
* @param request
* @param response
*/
// @RequestMethod(MethodType.POST)
@RequestMapping(value = "/testMethod2", methodType = MethodType.POST)
public void testMethod2(HttpServletRequest request, HttpServletResponse response) {
System.out.println("testMethod2");
} /**
* 内部重定向测试
* http://localhost:8080/mvc-framework/test/innerRedirect
* @return
*/
@RequestMapping("/innerRedirect")
public String innerRedirect() {
return DispatchServlet.REDIRECT + "/html/index.html";
} /**
* 外部重定向测试
* http://localhost:8080/mvc-framework/test/outerRedirect
* @return
*/
@RequestMapping("/outerRedirect")
public String outerRedirect() {
return DispatchServlet.REDIRECT + "https://www.baidu.com/";
} /**
* jsp跳转测试
* http://localhost:8080/mvc-framework/test/dispatchTest
* @return
*/
@RequestMapping("/dispatchTest")
public String dispatchTest() {
return "index";
} /**
* 响应JSON数据测试
* http://localhost:8080/mvc-framework/test/jsonTest
* @return
*/
@RequestMapping("/jsonTest")
public Map<String, Object> jsonTest() { Map<String, Object> map = new HashMap<>();
map.put("status", 100);
map.put("msg", "ok");
map.put("data", "json数据响应成功!"); return map;
} /**
* 获取参数测试
* http://localhost:8080/mvc-framework/test/getParamTest
* @return
*/
@RequestMapping("/getParamTest")
public void getParamTest(String a, int b, boolean c) {
System.out.println(a + " " + b + " " + c);
} /**
* 日期数据测试
* http://localhost:8080/mvc-framework/test/getDateTest
* @return
*/
@RequestMapping("/getDateTest")
public void getDateTest(Date date) {
System.out.println(date);
} /**
* 自定义对象测试
* http://localhost:8080/mvc-framework/test/getAccount
* @return
*/
@RequestMapping("/getAccount")
public void getAccount(Account account) {
System.out.println(account);
} /**
* 数组类型
* http://localhost:8080/mvc-framework/test/getArray
* @return
*/
@RequestMapping("/getArray")
public void getArray(Integer[] ints) {
System.out.println(Arrays.toString(ints));
} /**
* POST数据处理
* http://localhost:8080/mvc-framework/test/postParamTest
* @return
*/
@RequestMapping("/postParamTest")
public void postParamTest(HttpServletRequest request) {
Map<String, Object> params = (Map<String, Object>)request.getAttribute(ServletConstants.POST_PARAM_KEY); for (String s : params.keySet()) {
System.out.println(new StringBuffer(s).append(" -> ").append( params.get(s)));
}
} }

测试的自定义类型:

package cn.jack2048.model;

import java.time.LocalDate;
import java.util.Date; public class Account { private String username;
private String password;
private Integer id; public Date getBirth() {
return birth;
} public void setBirth(Date birth) {
this.birth = birth;
} public Account(String username, String password, Integer id, Date birth) {
this.username = username;
this.password = password;
this.id = id;
this.birth = birth;
} private Date birth; public Account() {
} public Account add(String username, String password, Integer id) {
return new Account(username, password, id);
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public Account(String username, String password, Integer id) {
this.username = username;
this.password = password;
this.id = id;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} @Override
public String toString() {
return "Account{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", id=" + id +
", birth=" + birth +
'}';
}
}

Web.xml配置的信息:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"> <!-- 阻止静态资源被DispatchServlet访问到 提前放行,注意,不要改变这个排放顺序 -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.png</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.jpeg</url-pattern>
<url-pattern>*.gif</url-pattern>
</servlet-mapping> <servlet>
<servlet-name>DispatchServlet</servlet-name>
<servlet-class>cn.jack2048.framework.servlet.DispatchServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>DispatchServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> <!-- 请求乱码过滤 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>cn.jack2048.framework.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/**</url-pattern>
</filter-mapping> </web-app>

工具类的测试:

import cn.jack2048.framework.model.Mapping;
import cn.jack2048.framework.util.ClassUtil;
import cn.jack2048.framework.util.DateUtil;
import cn.jack2048.model.Account;
import javassist.*;
import javassist.bytecode.AttributeInfo;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.junit.Test; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.Map; public class ClassUtilTest { /**
* 根据包名获取指定路径下所有带某一注解的类
*
*/
@Test
public void getAllClassUnderPackagePath() throws InvocationTargetException, IllegalAccessException { Map<String, Mapping> allRequestMapping = ClassUtil.getAllRequestMapping("cn.jack2048.controller"); Mapping mapping = allRequestMapping.get("/test/testMethod2"); Object object = mapping.getObject(); Method method = mapping.getMethod(); // Object invoke = method.invoke(object); // System.out.println(mapping.getMethodType());
} @Test
public void substringTest() { final String s = "redirect:"; String a = "redirect:asd1ewd2ee3"; System.out.println(a.substring(s.length()));
} @Test
public void paramNameTest() {
Class<Account> accountClass = Account.class; Method[] declaredMethods = accountClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { Parameter[] parameters = declaredMethod.getParameters(); System.out.print(declaredMethod.getName() + " : "); for (Parameter parameter : parameters) { String name = parameter.getName(); // JDK编译会缩减标识符号名称 1编译配置不简化标识符, 2使用第三方组件实现 Class<?> type = parameter.getType(); System.out.print(type.getName() + " : " + name + ", ");
}
System.out.println(" \n"); }
} @Test
public void paramNameTest2() throws Exception { Class<Account> accountClass = Account.class; Method addMethod = accountClass.getDeclaredMethod("add", String.class, String.class, Integer.class); ClassPool classPool = ClassPool.getDefault(); ClassPath classPath = classPool.insertClassPath(new ClassClassPath(accountClass)); CtMethod ctMethod = classPool.getMethod(accountClass.getName(), addMethod.getName()); MethodInfo methodInfo = ctMethod.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
AttributeInfo attribute = codeAttribute.getAttribute(LocalVariableAttribute.tag);
LocalVariableAttribute attribute2 = (LocalVariableAttribute) attribute; if (null != attribute2) {
int pos = Modifier.isStatic( ctMethod.getModifiers()) ? 0 : 1;
for (int i = 0; i < addMethod.getParameterCount(); i++) {
String paraName = attribute2.variableName(i + pos); System.out.println(paraName);
}
}
} @Test
public void dateTest() { String time = "2021-09-31 16:24:33";
String time2 = "2021-09-31";
System.out.println( DateUtil.matchDateFormat(time));
System.out.println( DateUtil.matchDateFormat(time2)); }
}

写到这里发现还有一些问题:

1、老师视频里面是没有处理html跳转的,结果发现html跳转是乱码的

原因是因为静态资源已经交给defaultServlet来处理放行的,也就是Tomcat来处理

解决办法是给defaultServlet再配置一个字符编码参数

2、写的是一个单体服务的架构,所以,不存在跨域访问的情况,但是如果要跨域肯定是需要设置的

使用过滤器再实现跨域访问的放行

package cn.dzz.framework.web.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; public class CorsFilter implements Filter { @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 强转HttpServletResponse对象使用
HttpServletResponse resp = (HttpServletResponse) servletResponse; // resp.setContentType("text/html;charset=UTF-8"); // 一些请求是响应数据做接口调用,不应该写死 //禁用缓存,确保网页信息是最新数据
resp.setHeader("Pragma","No-cache");
resp.setHeader("Cache-Control","no-cache"); // 添加参数,允许任意domain访问
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, HEAD, DELETE, PUT");
resp.setHeader("Access-Control-Max-Age", "3600");
resp.setHeader("Access-Control-Allow-Headers",
"X-Requested-With, Content-Type, Authorization, Accept, Origin, User-Agent, Content-Range, Content-Disposition, Content-Description"); resp.setDateHeader("Expires", -10);
filterChain.doFilter(servletRequest, resp);
}
}

对web.xml的配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"> <!-- 阻止静态资源被DispatchServlet访问到 提前放行,注意,不要改变这个排放顺序 -->
<servlet>
<servlet-name>default</servlet-name> <!-- 防止静态资源乱码配置 -->
<init-param>
<param-name>fileEncoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet> <servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.png</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.jpeg</url-pattern>
<url-pattern>*.gif</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list> <servlet>
<servlet-name>DispatchServlet</servlet-name>
<servlet-class>cn.dzz.framework.web.servlet.DispatchServlet</servlet-class> <init-param>
<param-name>controllerPackage</param-name>
<param-value>cn.dzz.controller</param-value>
</init-param> <load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>DispatchServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> <filter>
<filter-name>CorsFilter</filter-name>
<filter-class>cn.dzz.framework.web.filter.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/**</url-pattern>
<url-pattern>/*</url-pattern>
<url-pattern>/</url-pattern>
</filter-mapping>
<!-- 请求乱码过滤 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>cn.dzz.framework.web.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/**</url-pattern>
<url-pattern>/*</url-pattern>
<url-pattern>/</url-pattern>
</filter-mapping> </web-app>

【JavaWeb】封装一个MVC框架的更多相关文章

  1. 老司机教你用原生JDK 撸一个 MVC 框架!!!

    其实 Spring MVC 是一个基于请求驱动的 Web 框架,并且也使用了前端控制器模式来进行设计,再根据请求映射规则分发给相应的页面控制器进行处理,具体工作原理见下图. 在这里,就不详细谈相关的原 ...

  2. 使用 Netty 实现一个 MVC 框架

    NettyMVC 上面介绍 Netty 能做是什么时我们说过,相比于 SpringMVC 等框架,Netty 没提供路由等功能,这也契合和 Netty 的设计思路,它更贴近底层.下面我们在 Netty ...

  3. 2015年3月26日 - Javascript MVC 框架DerbyJS DerbyJS 是一个 MVC 框架,帮助编写实时,交互的应用。

    2015年3月26日 -  Javascript MVC 框架DerbyJS DerbyJS 是一个 MVC 框架,帮助编写实时,交互的应用.

  4. 一个 MVC 框架以 MVVM 之「魂」复活了!

    GitHub: https://github.com/houfeng/mokit Mokit 最初编写于 2012 年,是一个面向移动应用的前端 mvc 框架,v3 版本进行了大量的重构或重写,并尽可 ...

  5. 利用jdbc简单封装一个小框架(类似DBUtils)

    利用jdbc写的一个类似DBUtils的框架 package com.jdbc.orm.dbutils; import java.io.IOException; import java.io.Inpu ...

  6. springMVC和struts2有什么不同?为什么要用springMVC或者struts2?让你实现一个MVC框架大概如何设计?

    [问题一:不同] (1)框架机制 1.Struts2采用Filter(StrutsPrepareAndExecuteFilter)实现,SpringMVC(DispatcherServlet)则采用S ...

  7. openresty 前端开发轻量级MVC框架封装一(控制器篇)

    通过前面几章,我们已经掌握了一些基本的开发知识,但是代码结构比较简单,缺乏统一的标准,模块化,也缺乏统一的异常处理,这一章我们主要来学习如何封装一个轻量级的MVC框架,规范以及简化开发,并且提供类似p ...

  8. 自定义MVC框架之工具类-分页类的封装

    以前写过一个MVC框架,封装的有点low,经过一段时间的沉淀,打算重新改造下,之前这篇文章封装过一个验证码类. 这次重新改造MVC有几个很大的收获 >全部代码都是用Ubuntu+Vim编写,以前 ...

  9. AsMVC:一个简单的MVC框架的Java实现

    当初看了<从零开始写一个Java Web框架>,也跟着写了一遍,但当时学艺不精,真正进脑子里的并不是很多,作者将依赖注入框架和MVC框架写在一起也给我造成了不小的困扰.最近刚好看了一遍sp ...

  10. JavaWeb之搭建自己的MVC框架

    https://blog.csdn.net/anita9999/article/details/83378111 自己写一个mvc框架吧(一) https://www.cnblogs.com/heba ...

随机推荐

  1. C# asp.net mvc 创建虚拟目录

    使用背景: 虚拟目录(virtual directory),计算机术语,每个 Internet服务可以从多个目录中发布.通过以通用命名约定 (UNC) 名.用户名及用于访问权限的密码指定目录,可将每个 ...

  2. Wakeup Source框架设计与实现

    Wakeup Source 为系统组件提供了投票机制,以便低功耗子系统判断当前是否可以进入休眠. Wakeup Source(后简称:WS) 模块可与内核中的其他模块或者上层服务交互,并最终体现在对睡 ...

  3. kettle从入门到精通 第二十二课 kettle carte web服务中文乱码

    在windows 上面 carte服务的canvas画布展示的中文正常,但是在linux上面中文展示乱码,如下所示: 原因:linux 机器缺少字体所致. kettle源码中使用字体: 解决方法: 安 ...

  4. Apollo启动配置排查,超时时间的配置

    Apollo启动配置排查 1.排查下来是 本地的服务 apollo 配置fake发布到线上去了.2.或者是引用的apollo jar包中指向的apollo服务器地址是否正确. 3.超时时间的配置 ## ...

  5. Gradle查看依赖及排除依赖的方法

    查看项目的编译依赖,同时写入文件aa.txt F:\sts4\order-test>gradlew :order-test-api:dependencies --configuration co ...

  6. logging.basicConfig()

    logging.basicConfig() 是 Python 标准库 logging 模块中的一个函数,用于配置日志记录器(logger)的基本选项.这个函数允许你在不创建和配置多个 logger.h ...

  7. 将静态文件打包进nuget里 Net Core

    我之前写了一个.net core 生成验证码的小工具 需要使用者先单独下载字体文件到本地在 install-package 感觉这样很捞也很不方便,但当时忙着做其他需求现在更新下. 其实很简单 vis ...

  8. Jupyter QtConsole 配置,2023 年了你还在使用 QtConsole 吗?

    目录 Jupyter QtConsole 配置,2023 年了你还在使用 QtConsole 吗? Jupyter QtConsole 的安装 设置字体 启动时自动加载需要的库包 更新:2023 年 ...

  9. injectionIII iOS代码注入工具(下)

    injectionIII iOS代码注入工具(下) 本文将解决如何使用injectionIII对主页热重载,如果对injectionIII不了解的同学请回到上篇查看 Vaccine 简单地说Vacci ...

  10. MYSQL-check管理

    mysql这个东西对于管理员并不友好,看起来还没有成品.就拿亲儿子workbeanch来说吧,功能也不是很齐全,速度也一般般,否则sqlyog之类的早没有什么活路了. 社区版的支持非常薄弱(商业版不太 ...