【JavaWeb】封装一个MVC框架

 

 

框架参考自:

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>

 

posted @ 2021-10-01 20:33  emdzz  阅读(100)  评论(0编辑  收藏  举报