北京尚学堂2020java_SpringMVC第三天

第一节 SpringMVC拦截器学习

Servlet提供了原生的过滤器,其执行时机是在servlet之前。而使用SpringMVC之后,就只有一个servlet,如果使用拦截器那么就会拦截DS前的所有请求。有的部门不想被拦截,怎么办?——使用拦截器,其执行时机位DS之后,单元方法之前。其作为对单元方法进行过滤。

DispactherServlet能拿到的所有对象都在springmvc容器里

拦截器的使用:

  1. 创建实现了指定接口的java类
  2. 在拦截器中声明对应的功能代码
  3. 配置been及其拦截范围

1.1 拦截器代码实现

1.在inteceptor类中创建一个类:

package com.bjsxt.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return false;  // false不会放行去执行单元方法,如果想放行返回true
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

1.2 拦截器方法详解

1.2.1 preHandle参数详解

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 

handler中存储了此次单元方法请求对象HanlderMethod的实例化对象,可以通过反射拿到对象并调用方法。

拦截器需要调用mycontroller的方法,就需要拿到它的实例化对象,这两个对象都在springmvc容器中,即b和a都在springmvc容器中,b又需要a,这就是依赖注入,由autowired注释来解决这个问题。

@Autowired
private MyController myController;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println(handler instanceof HandlerMethod);
    // 可以通过反射拿到单元方法
    HandlerMethod handlerMethod = (HandlerMethod) handler;
    Method method =  handlerMethod.getMethod();
    method.invoke(myController,null);
    System.out.println("MyInterceptor.preHandle");
    return true ;
}

1.2.2 postHandle方法详解

这段代码并非是返回string,而是会将请求转发到名为aa的单元方法,学了springmvc之后应该从这个角度来理解

@RequestMapping("demo")
public String testMethod() {
    // 处理请求
    System.out.println("MyController.testMethod:我是单元方法");
    return "aa";
}

postHandle执行的时机:单元方法执行之后执行,单元方法请求转发的jsp执行之前。preHandle->单元方法->postHandle->jsp
作用:可以对请求转发的数据进行修改,还可以对单元方法要响应的资源进行重新定义。

  1. 对请求转发的数据进行修改
    比如单元方法中设置了这样的数据:
// 声明单元方法
@RequestMapping("demo")
public String testMethod(Model model) {
    // 处理请求
    System.out.println("MyController.testMethod:我是单元方法");
    model.addAttribute("str", "我是中国人");
    return "aa.jsp";
}

然后在com/bjsxt/interceptor/MyInterceptor.java想要对“中国”这两字进行和谐:

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    // 对model中要流转的数据进行置换和处理

    // 重新设定要跳转的资源路径
    // modelAndView.setViewName("forward:/bb.jsp");
    // 更改流转的数据
    Map<String ,Object> model = modelAndView.getModel();
    String str = (String)model.get("str");
    if(str.contains("中国")) {
        str = str.replaceAll("中国", "**");
    }
    model.put("str", str);
    System.out.println("MyInterceptor.postHandle");
}
  1. 对要响应的资源进行重定义
    本来单元方法让跳转到aa.jsp的,但经过检查决定跳转到bb.jsp:
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    // 重新设定要跳转的资源路径
    modelAndView.setViewName("forward:/bb.jsp");
    System.out.println("MyInterceptor.postHandle");
}

1.2.3 afterCompletion方法

检测请求过程是否正常执行:

  1. 处理在请求过程中出现的异常信息
  2. 请求的日志信息
  3. 资源流的关闭
    执行时机:本次请求的最后,在jsp之后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    if(ex != null) {
        System.out.println("出现异常,使用log4j打印异常信息");
        return;
    }
    System.out.println("MyInterceptor.afterCompletion");
}

1.2 多重拦截器配置总结


如果对于同一个单元方法,有N多层拦截器,那么配置在上的为外层拦截器,配置在下的为内层拦截器,中间是单元方法。
那么执行的方法顺序为上pre->下pre->单元方法->下post->上post->下afer->上after

第二节 SpringMVC运行原理

核心组件:(SpringMVC 四大组件)

  1. DispatchServlet: Servlet分发器,整个SPringMVC框架入口.
  2. HandlerMapping:寻找URL所请求的HandlerMethod,找@RequestMapping() 负责寻找单元方法
  3. 使用实现类DefaultAnnotationHandlerMapping实际工作.
  4. HandlerAdapter:实际调用HandlerMethod的组件. 实际调用方法
  5. 使用实现类AnnotationMethodHandlerAdapter
  6. ViewResovler:视图解析器.作用解析HandlerMethod返回值.把逻辑视图转换为需要调用的物理视图.
  7. 自定义时:InternalResourceViewResolver

    当用户发起请求后,执行DiapacherServlet,如果是JSP直接调用jsp页面.如果不是JSP,DiapacherServlet调用HandlerMapping判断请求URL是否合法,如果URL不存在报错,如果URL存在使用HandlerAdapter调用具体的HandlerMethod,当HandlerMethod执行完成后会返回ModelAndView,会被ViewResovler解析,调用具体的物理视图.

第三节 SSM框架知识复习

第四节 SpringMVC对象ajax请求

问题:当浏览器发起一个ajax请求给服务器,服务器调用对应的单元方法处理ajax请求,而ajax的请求在被处理后需要立即响应。而目前我们在单元方法中响应ajax请求使用的是response对象,需要我们自己要响应的数据转换为json字符串响应比较麻烦,怎么办?而我们一直在单元方法中无论是否是ajax请求,都使用return语句来完成响应。
解决:既然我们希望使用单元方法的返回值来响应ajax请求的处理结果,而目前DispatcherServlet的底层会将单元方法的返回值按照请求转发或者重定向来处理,所以就需要我们告诉DispatcherServlet,单元方法的返回值不要按照请求转发或者重定向处理,而是按照直接响应处理,将单元方法的返回值直接响应给浏览器。
前端代码接收js请求:

<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <base href="<%=basePath %>"/>
    <title>Title</title>
    <%--引入jquery文件--%>
    <script type="text/javascript" src="js/j.js"></script>
    <%--声明js代码域--%>
    <script type="text/javascript">
        //声明页面加载事件
        $(function () {
            //给按钮增加单击事件
            $("#btn").click(function () {
                $.get("testAjax",{uname:"张三",age:18},function (data) {
                    alert(data.uname)
                })
            })

        })
    </script>
</head>
<body>
<h3>SpringMVC响应ajax请求处理结果</h3>
<hr>
<input type="button" value="点击发起ajax请求" id="btn">
</body>
</html>

后台代码非常简单:1.加上@ResponseBody注解2.把返回值类型改成某个具体的类3.导入jackjson包
在ajax的回调函数中,无需再次使用eval函数将响应数据转换为json对象,直接使用即可。

@Controller
public class MyController {
    // 处理ajax请求并响应结果
    @ResponseBody
    @RequestMapping("testAjax")
    public User testAjax(String uname, int age) {
        // 处理请求
        System.out.println("uname = " + uname + ", age = " + age);
        // 响应结果
        // 常规的方法是
        User user = new User(1, "王五", "123");
        // 直接返回要响应的数据
        return user;
    }
}
posted @ 2022-02-13 17:50  明卿册  阅读(18)  评论(0编辑  收藏  举报