您好,欢迎来到榕意旅游网。
搜索
您的当前位置:首页【总结】自定义SpringMVC

【总结】自定义SpringMVC

来源:榕意旅游网

一、简介

1. SpringMVC的引出

  • 最开始都是用的Servlet

  • 如果一个Servlet对应一个类,比如说商品类,但是如果我们除了需要查询产品还查询了订单,就需要访问两个Servlet

  • 如果一个Servlet对应一个页面,比如说商品页,随着页面的增多,Servlet也会越来越多,同时如果一个页面有多个get请求,将无法处理

参照SpringMVC,仅通过DispatcherServlet进行请求派发这样可以让系统模块更加明确,该类的任务有

  • 拦截全部请求
  • 解析请求
  • 派发给对应的Controller里面进行处理

通过下面的注解可以拦截到全部请求

@WebServlet("/")

下面的会对jsp请求也会进行拦截,如果我们在页面中转发到jsp,就会依然被拦截到这个类里

@WebServlet("/*")

原因在tomcat的web.xml中,反斜杠是Servlet中特殊的匹配模式,优先级最低,比*.jsp优先级低,但是反斜杠星号属于路径匹配,优先级比*.jsp高

<!-- The mapping for the default servlet -->
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

 <!-- The mappings for the JSP servlet -->    
 <servlet-mapping>                                      
     <servlet-name>jsp</servlet-name>          
     <url-pattern>*.jsp</url-pattern>              
     <url-pattern>*.jspx</url-pattern>            
 </servlet-mapping>                                     

2. 自定义MVC提供的功能

  • 对所有的请求结果进行转发,利用责任链模式处理对应的请求

  • 进行编码相关的预处理

  • 支持静态资源、JSP以及Controller的请求处理,需要被转发的Controller需要在类和方法上都加入@RequestMapping注解,目前只提供Get和Post两种请求方法

  • 提供基本类型的参数转换,将请求的参数赋值在方法上,但是要求每个参数都需要加上@RequestParam注解

  • 如果方法上有@ResponseBody注解,将提供Json的视图渲染

  • 如果方法上没有提供@ResponseBody注解,将提供ModelAndView的页面渲染,返回值可以是ModelAndView,或者String类型,渲染到JSP页面

二、建立请求转发器

1. 请求的拦截

建立DispatcherServlet

  • 初始化容器
  • 初始化请求处理器责任链
  • 通过责任链模式来依次调用请求处理器对请求进行处理
  • 对处理结果进行渲染
/**
 * 1.完成框架的初始化
 * 2.对请求进行分发
 * 3.对结果进行渲染
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:05
 */
@WebServlet("/*")
public class DispatcherServlet extends HttpServlet {
    //处理器列表
    private List<RequestProcessor> Processor = new ArrayList<>();

    @Override
    public void init(){
        //1.初始化容器
        BeanContainer beanContainer = BeanContainer.getInstance();
        beanContainer.loadBeans("com.imooc");
        new AspectWeaver().doAop();
        new DependencyInjector().doIoc();
        //2.初始化请求处理器责任链
        Processor.add(new PreRequestProcessor());
        Processor.add(new StaticResourceRequestProcessor(getServletContext()));
        Processor.add(new JspRequestProcessor(getServletContext()));
        Processor.add(new ControllerRequestProcessor());
    }
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) {
        //1.创建责任链对象实例
        RequestProcessorChain requestProcessorChain = new RequestProcessorChain(Processor.iterator(), req, resp);
        //2.通过责任链模式来依次调用请求处理器对请求进行处理
        requestProcessorChain.doRequestProcessorChain();
        //3.对处理结果进行渲染
        requestProcessorChain.doRender();
    }
}

2. 责任链处理请求

/**
 * 责任链处理实例
 * 1.以责任链的模式执行注册的请求处理器
 * 2.委派给特定的Render实例对处理的结果进行渲染
 *
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:05
 */
@Data
@Slf4j
public class RequestProcessorChain {

    //请求处理器迭代器
    private final Iterator<RequestProcessor> requestProcessorIterator;
    //请求Request
    private final HttpServletRequest request;
    //请求Response
    private final HttpServletResponse response;
    //http请求方法
    private String requestMethod;
    //http请求路径
    private String requestPath;
    //请求状态码
    private int responseCode;
    //请求结果渲染器
    private ResultRender resultRender;

    public RequestProcessorChain(Iterator<RequestProcessor> requestProcessorIterator, HttpServletRequest req, HttpServletResponse resp) {
        this.requestProcessorIterator = requestProcessorIterator;
        this.request = req;
        this.response = resp;
        this.requestMethod = req.getMethod();
        this.requestPath = req.getPathInfo();
        this.responseCode = HttpServletResponse.SC_OK;
    }

    /**
     * 以责任链的模式处理请求
     */
    public void doRequestProcessorChain() {
        //1.通过迭代器遍历注册的请求处理器实现类列表
        try {
            while (requestProcessorIterator.hasNext()) {
                //2.直到某个请求处理器执行后返回为false为止
                RequestProcessor requestProcessor = requestProcessorIterator.next();
                boolean processStatue = requestProcessor.process(this);
                if (!processStatue) {
                    break;
                }
            }
        } catch (Exception e) {
            //3.期间如果出现异常,则交由内部异常渲染器处理
            this.resultRender = new InternalErrorResultRender();
            log.error("doRequestProcessorChain error:", e);
        }

    }

    /**
     * 结果渲染器
     */
    public void doRender() {
        //1.如果请求处理器实现类均未选择合适的渲染器,则使用默认的
        if (this.resultRender == null) {
            this.resultRender = new DefaultResultRender();
        }
        try {
            //2.调用渲染器的render方法对结果进行渲染
            resultRender.render(this);
        } catch (Exception e) {
            log.error("doRender error:", e);
            throw new RuntimeException(e);
        }
    }
}

三、请求处理器

1. 请求预处理器

/**
 * 请求预处理,包括进行统一的UTF-8编码以及路径处理
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:06
 */
@Slf4j
public class PreRequestProcessor implements RequestProcessor {
    @Override
    public boolean process(RequestProcessorChain requestProcessorChain) throws Exception {
        // 1.设置请求编码,将其统一设置成UTF-8
        requestProcessorChain.getRequest().setCharacterEncoding("UTF-8");
        // 2.将请求路径末尾的/剔除,为后续匹配Controller请求路径做准备
        // (一般Controller的处理路径是/aaa/bbb,所以如果传入的路径结尾是/aaa/bbb/,
        // 就需要处理成/aaa/bbb)
        String requestPath = requestProcessorChain.getRequestPath();
        //http://localhost:8080/simpleframework requestPath="/"
        if(requestPath.length() > 1 && requestPath.endsWith("/")){
            requestProcessorChain.setRequestPath(requestPath.substring(0, requestPath.length() - 1));
        }
        log.info("preprocess request {} {}", requestProcessorChain.getRequestMethod(), requestProcessorChain.getRequestPath());
        return true;
    }
}

2. 静态资源处理器

利用的tomcat默认请求派发器RequestDispatcher处理

/**
 * 静态资源请求的处理,包括但不限于图片、css、以及js文件等
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:08
 */
@Slf4j
public class StaticResourceRequestProcessor implements RequestProcessor {
    public static final String DEFAULT_TOMCAT_SERVLET = "default";
    public static final String STATIC_RESOURCE_PREFIX = "/static/";
    //tomcat默认请求派发器RequestDispatcher的名称
    RequestDispatcher defaultDispatcher;

    public StaticResourceRequestProcessor(ServletContext servletContext) {
        this.defaultDispatcher = servletContext.getNamedDispatcher(DEFAULT_TOMCAT_SERVLET);
        if(this.defaultDispatcher == null){
            throw new RuntimeException("There is no default tomcat servlet");
        }
        log.info("The default servlet for static resource is {}", DEFAULT_TOMCAT_SERVLET);
    }

    @Override
    public boolean process(RequestProcessorChain requestProcessorChain) throws Exception {
        //1.通过请求路径判断是否是请求的静态资源 webapp/static
        if(isStaticResource(requestProcessorChain.getRequestPath())){
            //2.如果是静态资源,则将请求转发给default servlet处理
            defaultDispatcher.forward(requestProcessorChain.getRequest(), requestProcessorChain.getResponse());
            return false;
        }
        return true;
    }
    //通过请求路径前缀(目录)是否为静态资源 /static/
    private boolean isStaticResource(String path){
        return path.startsWith(STATIC_RESOURCE_PREFIX);
    }
}

3. JSP处理器

利用的tomcat的jspServlet处理

/**
 * jsp资源请求处理
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:08
 */
public class JspRequestProcessor implements RequestProcessor {

    //jsp请求的RequestDispatcher的名称
    private static final String JSP_SERVLET = "jsp";
    //Jsp请求资源路径前缀
    private static final String  JSP_RESOURCE_PREFIX = "/templates/";

    /**
     * jsp的RequestDispatcher,处理jsp资源
     */
    private RequestDispatcher jspServlet;

    public JspRequestProcessor(ServletContext servletContext) {
        jspServlet = servletContext.getNamedDispatcher(JSP_SERVLET);
        if (null == jspServlet) {
            throw new RuntimeException("there is no jsp servlet");
        }
    }

    @Override
    public boolean process(RequestProcessorChain requestProcessorChain) throws Exception {
        if (isJspResource(requestProcessorChain.getRequestPath())) {
            jspServlet.forward(requestProcessorChain.getRequest(), requestProcessorChain.getResponse());
            return false;
        }
        return true;
    }

    /**
     * 是否请求的是jsp资源
     */
    private boolean isJspResource(String url) {
        return url.startsWith(JSP_RESOURCE_PREFIX);
    }

}

四、Controller处理器

1. 相关注解和配置类

2. ControllerRequestProcessor

功能

  • 针对特定请求,选择匹配的Controllerf方法进行处理
  • 解析出请求里的参数及其对应的值,并赋值给Controller方法的参数
  • 选择合适的Render,为后续请求处理结果的渲染做准备

3. 建立Controller方法与请求的映射关系

请求中包含的信息有路径和请求参数,所以需要根据这些信息找到对应的Controller方法

存储请求的信息

/**
 * 存储http请求路径和请求方法
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/5 16:45
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class RequestPathInfo {
    //http请求方法
    private String httpMethod;
    //http请求路径
    private String httpPath;

}

存储Controller以及方法的信息

/**
 * 封装待执行的Controller及其方法实例和参数的映射
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/5 16:40
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ControllerMethod {
    //Controller对应的Class对象
    private Class<?> controllerClass;
    //执行的Controller方法实例
    private Method invokeMethod;
    //方法参数名称以及对应的参数类型
    private Map<String,Class<?>> methodParameters;
}

建立的映射关系就是RequestPathInfo与ControllerMethod的,这样就可以根据请求定位到对应的方法

private Map<RequestPathInfo, ControllerMethod> pathControllerMethodMap = new ConcurrentHashMap<>();

4. 给需要执行的方法参数赋值

/**
 * 给方法赋值的工具类
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/7 16:10
 */
public class ConverterUtil {
    /**
     * 返回基本数据类型的空值
     *需要特殊处理的基本类型即int\double\short\long\byte\float\boolean
     * @param type 参数类型
     * @return 对应的空值
     */
    public static Object primitiveNull(Class<?> type) {
        if (type == int.class || type == double.class ||
                type == short.class || type == long.class ||
                type == byte.class || type == float.class) {
            return 0;
        } else if(type == boolean.class){
            return false;
        }
        return null;
    }
    /**
     * String类型转换成对应的参数类型
     *
     * @param type  参数类型
     * @param requestValue 值
     * @return 转换后的Object
     */
    public static Object convert(Class<?> type, String requestValue) {
        if(isPrimitive(type)){
            if(ValidationUtil.isEmpty(requestValue)){
                return primitiveNull(type);
            }
            if (type.equals(int.class) || type.equals(Integer.class)) {
                return Integer.parseInt(requestValue);
            } else if (type.equals(String.class)) {
                return requestValue;
            } else if (type.equals(Double.class) || type.equals(double.class)) {
                return Double.parseDouble(requestValue);
            } else if (type.equals(Float.class) || type.equals(float.class)) {
                return Float.parseFloat(requestValue);
            } else if (type.equals(Long.class) || type.equals(long.class)) {
                return Long.parseLong(requestValue);
            } else if (type.equals(Boolean.class) || type.equals(boolean.class)) {
                return Boolean.parseBoolean(requestValue);
            } else if (type.equals(Short.class) || type.equals(short.class)) {
                return Short.parseShort(requestValue);
            } else if (type.equals(Byte.class) || type.equals(byte.class)) {
                return Byte.parseByte(requestValue);
            }
            return requestValue;
        } else {
            throw new RuntimeException("count not support non primitive type conversion yet");
        }
    }

    /**
     * 判定是否基本数据类型(包括包装类以及String)
     *
     * @param type 参数类型
     * @return 是否为基本数据类型
     */
    private static boolean isPrimitive(Class<?> type) {
        return type == boolean.class
                || type == Boolean.class
                || type == double.class
                || type == Double.class
                || type == float.class
                || type == Float.class
                || type == short.class
                || type == Short.class
                || type == int.class
                || type == Integer.class
                || type == long.class
                || type == Long.class
                || type == String.class
                || type == byte.class
                || type == Byte.class
                || type == char.class
                || type == Character.class;
    }
}

5. 利用反射执行获取执行的结果

Object controller = beanContainer.getBean(controllerMethod.getControllerClass());
Method invokeMethod = controllerMethod.getInvokeMethod();
invokeMethod.setAccessible(true);
Object result;
try {
    if (methodParam.size()==0){
        result = invokeMethod.invoke(controller);
    }else {
        result = invokeMethod.invoke(controller,methodParam.toArray());
    }
} catch (IllegalAccessException e) {
    throw new RuntimeException(e);
} catch (InvocationTargetException e) {
    //如果是调用异常的话,需要通过e.getTargetException()
    // 去获取执行方法抛出的异常
    throw new RuntimeException(e.getTargetException());
}

6. 根据结果设置结果渲染器

ResultRender resultRender;
boolean isJson = controllerMethod.getInvokeMethod().isAnnotationPresent(ResponseBody.class);
if (isJson){
    resultRender = new JsonResultRender(result);
}else {
    resultRender = new ViewResultRender(result);
}
requestProcessorChain.setResultRender(resultRender);

完整代码如下

/**
 * 将请求转发给对应的Controller进行处理
 *
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:10
 */
@Slf4j
public class ControllerRequestProcessor implements RequestProcessor {
    //IOC容器
    private BeanContainer beanContainer;
    //请求和Controller的映射
    private Map<RequestPathInfo, ControllerMethod> pathControllerMethodMap = new ConcurrentHashMap<>();

    /**
     * 依靠容器,建立起请求路径、请求方法与Controller方法实例的映射
     */
    public ControllerRequestProcessor() {
        this.beanContainer = BeanContainer.getInstance();
        // 获取被@RequestMapping标记的Controller类
        Set<Class<?>> requestMappingSet = beanContainer.getClassesByAnnotation(RequestMapping.class);
        // 建立映射
        initPathControllerMethodMap(requestMappingSet);
    }

    /**
     * 建立起请求路径、请求方法与Controller方法实例的映射
     *
     * @param requestMappingSet 需要被转发的Controller
     */
    private void initPathControllerMethodMap(Set<Class<?>> requestMappingSet) {
        if (ValidationUtil.isEmpty(requestMappingSet)) {//如果没有类被@RequestMapping标记
            return;
        }
        //1.遍历所有被@RequestMapping标记的类,获取类上面该注解的属性值作为一级路径
        for (Class<?> requestMappingClass : requestMappingSet) {
            //获取@RequestMapping注解
            RequestMapping requestMapping = requestMappingClass.getAnnotation(RequestMapping.class);
            //获取注解值
            String basePath = requestMapping.value();
            if (!basePath.startsWith("/")) {//如果不是以“/”开头,为了方便处理,加上“/”
                basePath = "/" + basePath;
            }
            //2.遍历类里所有被@RequestMapping标记的方法,获取方法上面该注解的属性值,作为二级路径
            Method[] methods = requestMappingClass.getDeclaredMethods();
            if (ValidationUtil.isEmpty(methods)) {
                continue;
            }
            for (Method method : methods) {
                if (method.isAnnotationPresent(RequestMapping.class)) {//获取方法被@RequestMapping标记的
                    //获取@RequestMapping注解
                    RequestMapping methodRequest = method.getAnnotation(RequestMapping.class);
                    //获取注解值
                    String methodPath = methodRequest.value();
                    if (!methodPath.startsWith("/")) {//如果不是以“/”开头,为了方便处理,加上“/”
                        methodPath = "/" + methodPath;
                    }
                    //拼接一级和二级路径路径
                    String url = basePath + methodPath;
                    //3.解析方法里被@RequestParam标记的参数,
                    // 获取该注解的属性值,作为参数名,
                    // 获取被标记的参数的数据类型,建立参数名和参数类型的映射
                    Map<String, Class<?>> methodParams = new HashMap<>();
                    //获取方法的参数
                    Parameter[] parameters = method.getParameters();
                    if (!ValidationUtil.isEmpty(parameters)) {
                        for (Parameter parameter : parameters) {
                            //获取方法参数上的注解属性
                            RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
                            //目前暂定为Controller方法里面所有的参数都需要@RequestParam注解
                            if (requestParam == null) {//如果方法参数没有注解,则暂时报错
                                throw new RuntimeException("The parameter must have @RequestParam");
                            }
                            methodParams.put(requestParam.value(), parameter.getType());
                        }
                    }
                    //4.将获取到的信息封装成RequestPathInfo实例和ControllerMethod实例,放置到映射表里
                    String httpMethod = String.valueOf(methodRequest.method());//获取注解中所要求的请求方法
                    RequestPathInfo requestPathInfo = new RequestPathInfo(httpMethod, url);
                    //如果出现了重复的路径
                    if (this.pathControllerMethodMap.containsKey(requestPathInfo)) {
                        log.warn("duplicate url:{} registration,current class {} method{} will override the former one",
                                requestPathInfo.getHttpPath(), requestMappingClass.getName(), method.getName());
                    }
                    ControllerMethod controllerMethod = new ControllerMethod(requestMappingClass, method, methodParams);
                    this.pathControllerMethodMap.put(requestPathInfo, controllerMethod);
                }
            }
        }


    }

    /**
     * 处理请求
     *
     * @param requestProcessorChain 责任链
     * @return 返回一定成功
     * @throws Exception 处理出错会抛出异常
     */
    @Override
    public boolean process(RequestProcessorChain requestProcessorChain) throws Exception {
        //1.解析HttpServletRequest的请求方法,请求路径,获取对应的ControllerMethod实例
        String method = requestProcessorChain.getRequestMethod();//获取请求的方法类型
        String path = requestProcessorChain.getRequestPath();//获取请求路径
        ControllerMethod controllerMethod = this.pathControllerMethodMap.get(new RequestPathInfo(method, path));//查找对应的路径和方法
        if (controllerMethod == null) {//找不到请求路径
            requestProcessorChain.setResultRender(new ResourceNotFoundResultRender(method, path));
            return false;
        }
        //2.解析请求参数,并传递给获取到的ControllerMethod实例去执行
        Object result = invokeControllerMethod(controllerMethod, requestProcessorChain.getRequest());
        //3.根据处理的结果,选择对应的render进行渲染
        setResultRender(result, controllerMethod, requestProcessorChain);
        return false;
    }

    /**
     * 根据处理的结果,选择对应的render进行渲染
     *
     * @param result                处理的结果
     * @param controllerMethod      执行的Controller及其方法
     * @param requestProcessorChain 请求处理链
     */
    private void setResultRender(Object result, ControllerMethod controllerMethod, RequestProcessorChain requestProcessorChain) {
        if (result == null) {
            return;
        }
        ResultRender resultRender;
        boolean isJson = controllerMethod.getInvokeMethod().isAnnotationPresent(ResponseBody.class);
        if (isJson) {
            resultRender = new JsonResultRender(result);
        } else {
            resultRender = new ViewResultRender(result);
        }
        requestProcessorChain.setResultRender(resultRender);
    }

    /**
     * 解析请求参数,并传递给获取到的ControllerMethod实例去执行
     *
     * @param controllerMethod 需要执行的Controller配置
     * @param request          http请求
     * @return 处理的结果
     */
    private Object invokeControllerMethod(ControllerMethod controllerMethod, HttpServletRequest request) {
        //1.从请求里获取GET或者POST的参数名及其对应的值
        Map<String, String> requestParamMap = new HashMap<>();
        //GET,POST方法的请求参数获取方式
        Map<String, String[]> parameterMap = request.getParameterMap();
        for (Map.Entry<String, String[]> parameter : parameterMap.entrySet()) {
            if (ValidationUtil.isEmpty(parameter.getValue())) {
                //只支持一个参数对应一个值的形式
                requestParamMap.put(parameter.getKey(), parameter.getValue()[0]);
            }
        }
        //2.根据获取到的请求参数名及其对应的值,以及controllerMethod里面的参数和类型的映射关系,去实例化出方法对应的参数
        List<Object> methodParam = new ArrayList<>();
        Map<String, Class<?>> methodParameterMap = controllerMethod.getMethodParameters();
        for (String paramName : methodParameterMap.keySet()) {
            Class<?> type = methodParameterMap.get(paramName);
            String requestValue = requestParamMap.get(paramName);
            Object value;
            //只支持String 以及基础类型char,int,short,byte,double,long,float,boolean,及它们的包装类型
            if (requestValue == null) {
                //将请求里的参数值转成适配于参数类型的空值
                value = ConverterUtil.primitiveNull(type);
            } else {
                value = ConverterUtil.convert(type, requestValue);
            }
            methodParam.add(value);
        }
        //3.执行Controller里面对应的方法并返回结果
        Object controller = beanContainer.getBean(controllerMethod.getControllerClass());
        Method invokeMethod = controllerMethod.getInvokeMethod();
        invokeMethod.setAccessible(true);
        Object result;
        try {
            if (methodParam.size() == 0) {
                result = invokeMethod.invoke(controller);
            } else {
                result = invokeMethod.invoke(controller, methodParam.toArray());
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            //如果是调用异常的话,需要通过e.getTargetException()
            // 去获取执行方法抛出的异常
            throw new RuntimeException(e.getTargetException());
        }
        return result;
    }


}

五、处理结果渲染器

1. 默认结果渲染器

如果请求处理器实现类均未选择合适的渲染器,则使用默认的结果渲染器

if (this.resultRender == null) {
    this.resultRender = new DefaultResultRender();
}

主要将处理的结果状态码返回,默认为200

/**
 * 默认渲染器,只会返回状态码
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:28
 */
public class DefaultResultRender implements ResultRender {
    @Override
    public void render(RequestProcessorChain requestProcessorChain) throws Exception {
        //将相应状态码设置到response中
        requestProcessorChain.getResponse().setStatus(requestProcessorChain.getResponseCode());
    }
}

2. 异常结果渲染器

以责任链的模式处理请求,期间如果出现异常,则交由内部异常渲染器处理

/**
 * 以责任链的模式处理请求
 */
public void doRequestProcessorChain() {
    //1.通过迭代器遍历注册的请求处理器实现类列表
    try {
        while (requestProcessorIterator.hasNext()) {
            //2.直到某个请求处理器执行后返回为false为止
            RequestProcessor requestProcessor = requestProcessorIterator.next();
            boolean processStatue = requestProcessor.process(this);
            if (!processStatue) {
                break;
            }
        }
    } catch (Exception e) {
        //3.期间如果出现异常,则交由内部异常渲染器处理
        this.resultRender = new InternalErrorResultRender(e.getMessage());
        log.error("doRequestProcessorChain error:", e);
    }

}

设置状态码500和异常信息

/**
 * 处理在请求中出现的异常
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:31
 */
public class InternalErrorResultRender implements ResultRender {

    private String errorMsg;

    public InternalErrorResultRender(String errorMsg){
        this.errorMsg = errorMsg;
    }

    @Override
    public void render(RequestProcessorChain requestProcessorChain) throws Exception {
        HttpServletResponse response = requestProcessorChain.getResponse();
        //设置状态码500和错误信息
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,errorMsg);
    }
}

3. 找不到路径渲染器

在根据请求路径转发到Controller时,找不到对应的对象或者方法,则使用该渲染器

返回404和请求的路径及方法

/**
 * 渲染找不到资源结果的渲染器
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:32
 */
public class ResourceNotFoundResultRender implements ResultRender {

    private String httpMethod;
    private String httpPath;

    public ResourceNotFoundResultRender(String httpMethod, String httpPath) {
        this.httpMethod = httpMethod;
        this.httpPath = httpPath;
    }

    @Override
    public void render(RequestProcessorChain requestProcessorChain) throws Exception {
        requestProcessorChain.getResponse().sendError(HttpServletResponse.SC_NOT_FOUND,
                "获取不到对应的请求资源:请求路径[" + httpPath + "]" + "请求方法[" + httpMethod + "]");
    }
}

4. Json结果渲染器

当方法上面使用@ResponseBody注解时,利用Gson将结果转换成Json数据返回

/**
 * 将结果返回成Json数据
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:29
 */
public class JsonResultRender implements ResultRender {
    private Object jsonData;
    public JsonResultRender(Object jsonData) {
        this.jsonData = jsonData;
    }

    @Override
    public void render(RequestProcessorChain requestProcessorChain) throws Exception {
        //设置响应头
        requestProcessorChain.getResponse().setContentType("application/json");
        requestProcessorChain.getResponse().setCharacterEncoding("UTF-8");
        //响应流写入经过Gson格式化的处理结果
        PrintWriter writer = requestProcessorChain.getResponse().getWriter();
        Gson gson = new Gson();
        String str = gson.toJson(jsonData);
        writer.write(str);
        writer.flush();
    }
}

5. 视图解析器

当方法上面没用使用@ResponseBody注解时,将使用视图解析器

模仿Spring MVC 定义一个视图ModelAndView

/**
 * 存储请求结果,以及显示该数据的视图
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/7 18:02
 */

public class ModelAndView {
    //页面所在的路径
    @Getter
    private String view;
    //页面的数据
    @Getter
    private Map<String ,Object> model = new HashMap<>();

    public ModelAndView setView(String view) {
        this.view = view;
        return this;
    }
    //这样可以使用一连串的设置
    //modelAndView.setView("addheadline.jsp").addViewData("aaa", "bbb");
    public ModelAndView addViewData(String attributeName, Object attributeValue){
        model.put(attributeName,attributeValue);
        return this;
    }
}

视图解析器则根据返回结果的不同,而进行跳转

  • 如果是String数据,则创建一个ModelAndView对象,并将数据加入到视图地址
  • 如果是ModelAndView,则会解析其中的视图地址和数据
/**
 * 渲染页面,比如ModelAndView类型
 * @author xzzz2020
 * @version 1.0
 * @date 2020/9/4 16:29
 */
public class ViewResultRender implements ResultRender {
    public static final String VIEW_PATH = "/templates/";

    private ModelAndView modelAndView;
    /**
     * 对传入的参数进行处理,并赋值给ModelAndView成员变量
     * @param mv
     */
    public ViewResultRender(Object mv) {
        if(mv instanceof ModelAndView){
            //1.如果入参类型是ModelAndView,则直接赋值给成员变量
            this.modelAndView = (ModelAndView)mv;
        } else if(mv instanceof  String){
            //2.如果入参类型是String,则为视图,需要包装后才赋值给成员变量
            this.modelAndView = new ModelAndView().setView((String)mv);
        } else {
            //3.针对其他情况,则直接抛出异常
            throw new RuntimeException("illegal request result type");
        }
    }

    /**
     * 将请求处理结果按照视图路径转发至对应视图进行展示
     * @param requestProcessorChain
     * @throws Exception
     */
    @Override
    public void render(RequestProcessorChain requestProcessorChain) throws Exception {
        HttpServletRequest request = requestProcessorChain.getRequest();
        HttpServletResponse response = requestProcessorChain.getResponse();
        //视图路径
        String path = modelAndView.getView();
        Map<String, Object> model = modelAndView.getModel();
        for(Map.Entry<String, Object> entry : model.entrySet()){
            request.setAttribute(entry.getKey(), entry.getValue());
        }
        //JSP
        request.getRequestDispatcher(VIEW_PATH +path).forward(request, response);

    }
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- nryq.cn 版权所有

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务