SpringMVC study notes-09 SpringMVC core

1. Forwarding and redirection


1. Number of requests

Redirection means that the browser sends a request to the server and sends a request to a new address again after receiving the response. Forwarding means that the server jumps to a new address in order to complete the response after receiving the request; redirect the request at least twice and forward the request once;

2. The address bar is different

The redirect address bar will change, and the forwarding address bar will not change;

3. Whether to share data

Redirect twice to request not to share data, and forward once to request to share data (information sharing is used at the request level, and redirection is bound to go wrong);

4. Jump restrictions

Redirection can jump to any URL (except protected resources), and forwarding can only jump to resources of this site;

5. Behave differently

Redirection is a client-side behavior, and forwarding is a server-side behavior; therefore, forwarding can access the protected resources in /WEB-INF, but redirection is not.

6. Realization of forwarding and redirection

In Servlet, we forward and redirect as follows:

forward:转发   ---- request.getRequestDispatcher("xx.jsp").forward()
redirect:重定向---- response.sendRedirect("xx.jsp")

The implementation of forwarding and redirection is encapsulated more concisely in SpringMVC:

A. forward

package com.zzt.Vo;

public class Student {
    private String name;
    private Integer age;

    public Student() {
    }

    public Student(String name,Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
@RequestMapping("/doForward")
    public ModelAndView doForwad(Student student){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","forward");
        modelAndView.addObject("your_name",student.getName());
        modelAndView.addObject("your_age",student.getAge());
        modelAndView.setViewName("forward:/WEB-INF/view/show.jsp");
        return modelAndView;
    }

/WEB-INF/view/show.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>请求过来的方式为:${requestScope.get("msg")}</p>
    <p>name:${requestScope.get("your_name")}</p>
    <p>age:${requestScope.get("your_age")}</p>
</body>
</html>

We have also said before that, in fact, the Spring MVC framework also executes forward by default for the view, so what is the point of our explicit forwarding? In fact, many times we configure the view resolver so that we only need the logical view name when returning the view name, but this also brings a disadvantage, that is, the framework will splice all view names according to the logical view name without thinking. After forwarding, sometimes the view we need to forward cannot be obtained through the view resolver + logical view name. At this time, we need to forward it manually. Yes, after the view is forward: marked, it will not be spliced ​​by the view parser during parsing.

B. redirect

    @RequestMapping("/doRedirect")
    public ModelAndView doRedirect(Student student){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","redirect");
        modelAndView.addObject("your_name",student.getName());
        modelAndView.addObject("your_age",student.getAge());
        modelAndView.setViewName("redirect:/static/jsp/show.jsp");
        return modelAndView;
    }
    <form action="test/doRedirect" method="post">
        name:<input type="text" name="name"><br>
        age:<input type="text" name="age"><br>
        <input type="submit" value="提交redirect">
    </form>

/show.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>请求过来的方式为:${requestScope.get("msg")}</p>
    <p>name:${requestScope.get("your_name")}</p>
    <p>age:${requestScope.get("your_age")}</p>
</body>
</html>

1. Obviously, the redirect process initiated two requests.

2. The parameter is added in the address bar. This is because we added the parameter to the request--r1 of the first request in the processor method of the first request mapping:

When we redirect, we use a new request--r2, the parameters will not be shared, and SpringMVC (smart) helps us convert simple types of data in the parameters into strings, and use the get request method Carry these parameters for the second request.

3. What the framework does is to carry the parameters through the URL, and we need to use the parameter collection object param to take the value ${param. parameter}.

4. request.getParameter() can obtain the parameters carried in the request; and request.setAttribute() adds data to the web container [request scope], and the data existing in the scope can be obtained through EL expressions on the JSP page. The former is used for requests initiated from the client to the server, and the latter is used for data transfer on the server (limited to the same request). Therefore, although there is no data in the request scope of the new request, because SpringMVC carries parameters through the get request, we can still get the parameters through the request object or the parameter object param. Some readers may ask, it won’t be long before all the data is carried in the form of parameters. This method is too complicated. The GET method will result in a very long URL. The length of the URL that GET carries parameters is limited. The method only supports the String type or the simple type that can be converted to the String type. The complex type cannot, and the parameters will be exposed, and the security is low [It is not clear how to add parameters to the post request in the Controller]. The scope can support any type and is more universal.

    <p>请求过来的方式为:<%=request.getParameter("msg")%></p>
    <p>name:<%=request.getParameter("your_name")%></p>
    <p>age:<%=request.getParameter("your_age")%></p>
    <p>请求过来的方式为:${param.msg}</p>
    <p>name:${param.your_name}</p>
    <p>age:${param.your_age}</p>

5. redirect: also invalidates the view resolver.

    @RequestMapping("/doRedirect")
    public ModelAndView doRedirect(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","使用doRedirect");
        modelAndView.setViewName("redirect:/WEB-INF/view/show.jsp");
        return modelAndView;
    }
    <form action="test/doRedirect" method="post">
        name:<input type="text" name="name"><br>
        age:<input type="text" name="age"><br>
        <input type="submit" value="提交redirect">
    </form>

Since the redirection is equivalent to the client re-initiating a request, but because the address we are accessing is a resource in the /WEB-INF directory, it is protected and cannot be accessed directly from outside the server, so this will happen .

[Note]: Don’t forget that when we return String directly, the framework will treat it as the view name. Therefore, forward: and redirect: can also be used to return String.

 

2. Exception handling

The way we usually handle exceptions in Java is: try--catch--finally (optional). For every place where an exception may occur, it needs to be processed, which will have a lot of redundant code and will make the call of the business method appear unclean. In Spring we learned the AOP, in SpringMVC, AOP-based thinking, provides us with a unified, global exception handler: using AOP, the Controller of all exception handling into one place, the business logic and exception handling Code is decoupled and separated. Two annotations @ExceptionHandler and @ControllerAdvice will be used.

0. View page

index.jsp

    <p>处理异常</p>
    <form action="test/some.do" method="post">
        name:<input type="text" name="name"><br>
        age:<input type="text" name="age"><br>
        <input type="submit" value="提交">
    </form>

/WEB-INF/view/show.jsp

    <p>name:${your_name}</p>
    <p>age:${your_age}</p>

1. Create a new exception class for demonstration of exception handling

StudentException

package com.zzt.exception;

public class StudentException extends Exception{
    public StudentException() {
        super();
    }

    public StudentException(String message) {
        super(message);
    }
}

NameException

package com.zzt.exception;

public class NameException extends StudentException {
    public NameException() {
        super();
    }

    public NameException(String message) {
        super(message);
    }
}

AgeException

package com.zzt.exception;

public class AgeException extends StudentException {
    public AgeException() {
        super();
    }

    public AgeException(String message) {
        super(message);
    }
}

2. Throw an exception in the Controller

package com.zzt.Controller;

import com.zzt.Vo.Student;
import com.zzt.exception.AgeException;
import com.zzt.exception.NameException;
import com.zzt.exception.StudentException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping(value = "/test")
public class MyController {

    @RequestMapping("/some.do")
    public ModelAndView doForwad(Student student) throws StudentException {
        System.out.println(student.getName() + "=" + student.getAge());
        //把异常同一抛出给框架 让框架选择对应的方式处理
        if(!"zzt".equals(student.getName())){
            throw new NameException("姓名不对");
        }

        if(student.getAge() == null || student.getAge() > 150){
            throw new AgeException("年龄不对");
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","forward");
        modelAndView.addObject("your_name",student.getName());
        modelAndView.addObject("your_age",student.getAge());
        modelAndView.setViewName("show");
        return modelAndView;
    }

}

3. Create a new common class for use as a global exception handling class

    1) Add @ControllerAdvice to the class

    2) Define the method of exception handling in the class, add @ExceptionHandler to the method

package com.zzt.handler;

import com.zzt.exception.AgeException;
import com.zzt.exception.NameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice //用于表示该类是用来给控制器增加功能的,Advice就是AOP中的通知
public class GlobalExceptionHandler {

    /*
        异常处理的方法定义与处理器方法一致。
        @ExceptionHandler 用于指定抛出的异常类型。
    */
    @ExceptionHandler(value = NameException.class)
    public ModelAndView doNameException(Exception exception){
        /*
            异常处理流程:记录异常发生的方法、异常原因 到数据库、日志文件等,并发送通知。
        */
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","姓名设置错误");
        modelAndView.addObject("ex",exception);
        modelAndView.setViewName("nameError");
        return modelAndView;
    }

    @ExceptionHandler(value = AgeException.class)
    public ModelAndView doAgeException(Exception exception){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","年龄设置错误");
        modelAndView.addObject("ex",exception);
        modelAndView.setViewName("ageError");
        return modelAndView;
    }

    @ExceptionHandler //不加value 表示接受那些未被其他方法接收的异常
    public ModelAndView doOtherException(Exception exception){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","未知异常");
        modelAndView.addObject("ex",exception);
        modelAndView.setViewName("otherError");
        return modelAndView;
    }

}

4. Create a view of exception handling

/WEB-INF/view/nameError.jsp

<body>
    nameError:${msg}<br>
    异常消息:${ex.message}
</body>

/WEB-INF/view/ageError.jsp

<body>
    ageError:${msg}<br>
    异常消息:${ex.message}
</body>

/WEB-INF/view/otherError.jsp

<body>
    nameError:${msg}<br>
    异常消息:${ex.message}
</body>

5. Create a configuration file

    1) Component scanner, scan Controller

    2) Component scanner, scan exception handling class

    3) Declare annotation driven

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.1.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

    <!--声明组件扫描器-->
    <context:component-scan base-package="com.zzt.Controller"/>

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--配置注解驱动-->
    <mvc:annotation-driven/>

    <!--异常处理类包的扫描-->
    <context:component-scan base-package="com.zzt.handler" />

</beans>

This achieves the separation of business logic and exception handling.

 

3. Interceptor

Interceptors are mainly used to intercept requests, and HandlerInterceptor needs to be implemented in SpringMVC to use interceptors.

1. The difference between interceptor and filter:

1. The interceptor is used to intercept user requests, the request may be because the interceptor cannot reach the processor method; the filter is responsible for processing the request parameters, such as filtering sensitive words, setting the character set, etc., and releasing it after processing.

2. The interceptor is global and can intercept multiple Controllers. There can be no or multiple interceptors in a project, and they can intercept user requests together. Usually intercept is used in a user login, permission checks, logging aspects and so on.

3. The filter is an object in the Servlet specification, created by the Tomcat server; the interceptor is an object in the SpringMVC framework, created by the SpringMVC container.

4. The filter implements the Filter interface, and the interceptor implements HandleHandlerInterceptor.

5. The filter is executed first among the interceptors.

6. The filter has only one execution time, and the interceptor has three execution times.

7. The filter can handle jsp, js, html, etc. The interceptor mainly intercepts the request to the Controller. If the request itself cannot be intercepted by the DispatcherServlet, it cannot be intercepted by the interceptor.

2. The use of interceptors:

1. Customize the interceptor and implement the HandlerInterceptor interface.

package com.zzt.handler;

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

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

//自定义拦截器类
public class MyIntercepter implements HandlerInterceptor {
    /*
        preHandle--预处理方法  在Controller方法之前执行
        参数 : Object handler : 被拦截的Controller对象
        这个方法一般用于验证请求符合要求、验证用户是否登录、验证用户是否有权限访问
        验证成功 返回 true  拦截器放行请求(如果后面还有拦截器,会被继续拦截)
        验证失败 返回 false 拦截器会阻断请求继续传递,该请求不会被响应
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器的preHandle");
        if(!"zzt".equals(request.getParameter("name"))){
            request.getRequestDispatcher("/WEB-INF/view/tips.jsp").forward(request,response);
            return false;
        }
        return true;
    }

    /*
        postHandle--后处理方法  在Controller方法之后
        参数 : Object handler : 被拦截的Controller对象
               ModelAndView modelAndView : Controller方法返回的modelAndView
        这个方法可以获取处理器方法返回的modelAndView,可以修改数据和视图,对原结果做二次修正
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器的postHandle");
    }

    /*
        afterCompletion--请求完成后方法  在框架对视图执行了forward,就认为请求完成了
        参数 : Object handler : 被拦截的Controller对象
               Exception ex : 程序中的异常
        这个方法用于在最后对请求处理过程中申请的资源进行回收
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("拦截器的afterCompletion");
    }
}

2. Declare the interceptor in the SpringMVC configuration file.

    <mvc:interceptors>
        <mvc:interceptor>
            <!--拦截的uri地址
                path: uri地址  支持通配符** ,表示任意字符、多级目录
                /test/**: 以/test/开头的所有请求
            -->
            <mvc:mapping path="/test/**"/>
            <!--拦截器对象-->
            <bean class="com.zzt.handler.MyIntercepter"/>
        </mvc:interceptor>
    </mvc:interceptors>

controller

    @RequestMapping("/some.do")
    public ModelAndView doForwad(Student student){
        System.out.println("到处理器方法");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","forward");
        modelAndView.addObject("your_name",student.getName());
        modelAndView.addObject("your_age",student.getAge());
        modelAndView.setViewName("show");
        return modelAndView;
    }

/WEB-INF/view/tips.jsp

<body>
    请求遭遇拦截!
</body>

Let's first take the input that can be intercepted by the preprocessing method:

Let's take the input that cannot be intercepted by the preprocessing method:

It should be noted that when we forward the request manually, we need to give the full path, which will not be parsed by the view resolver.

3. Execution of the interceptor

1. Before the request is processed (that is, before the method in the Controller is called)

If the request is intercepted by the interceptor and cannot pass the preHandle method, the request will be blocked by the interceptor and will not be transmitted later.

2. After the processor method

3. After the request is completely finished (the framework forwards the request)

    @RequestMapping("/some.do")
    public ModelAndView doSome(Student student){
        System.out.println("到some.do方法");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","forward");
        modelAndView.addObject("your_name",student.getName());
        modelAndView.addObject("your_age",student.getAge());
        modelAndView.setViewName("forward:show.do");
        return modelAndView;
    }

    @RequestMapping("/show.do")
    public ModelAndView doShow(ModelAndView modelAndView){
        System.out.println("到show.do方法");
        ModelAndView mv = modelAndView;
        mv.setViewName("show");
        return  mv;
    }

analysis:

1) The client initiates some.do request

2) The interceptor's preHandle intercepted somed.do request and let it go

3) Reach the doSome method, set the forwarding request show.do, and the method ends, and it is intercepted by the interceptor’s postHandle

4) show.do arrives at the interceptor, forwards the shared request object, the parameters in the request have not changed, so through the interceptor's preHandle method

5) Reach the doShow method, set the logical view, and complete the request

6) At this time doSome and doShow are regarded as the end of the request, and they are intercepted by the interceptor's afterCompletion

4. Multiple interceptors

For multiple interceptors, the framework organizes their instantiation objects into a list according to the sequence in the configuration file, and intercepts them before and after the list when they are called.

MyIntercepter.java

package com.zzt.handler;

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

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

//自定义拦截器类
public class MyIntercepter implements HandlerInterceptor {
    /*
        preHandle--预处理方法  在Controller方法之前执行
        参数 : Object handler : 被拦截的Controller对象
        这个方法一般用于验证请求符合要求、验证用户是否登录、验证用户是否有权限访问
        验证成功 返回 true  拦截器放行请求(如果后面还有拦截器,会被继续拦截)
        验证失败 返回 false 拦截器会阻断请求继续传递,该请求不会被响应
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("1111----拦截器的preHandle");
        return true;
    }

    /*
        postHandle--后处理方法  在Controller方法之后
        参数 : Object handler : 被拦截的Controller对象
               ModelAndView modelAndView : Controller方法返回的modelAndView
        这个方法可以获取处理器方法返回的modelAndView,可以修改数据和视图,对原结果做二次修正
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("1111----拦截器的postHandle");
    }

    /*
        afterCompletion--请求完成后方法  在框架对视图执行了forward,就认为请求完成了
        参数 : Object handler : 被拦截的Controller对象
               Exception ex : 程序中的异常
        这个方法用于在最后对请求处理过程中申请的资源进行回收
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("1111----拦截器的afterCompletion");
    }
}

MyIntercepter2.java

package com.zzt.handler;

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

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

public class MyIntercepter2 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("2222----拦截器的preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("2222----拦截器的postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("2222----拦截器的afterCompletion");
    }
}

springmvc.xml

    <mvc:interceptors>
        <mvc:interceptor>
            <!--拦截的uri地址
                path: uri地址  支持通配符** ,表示任意字符、多级目录
                /test/**: 以/test/开头的所有请求
            -->
            <mvc:mapping path="/test/**"/>
            <!--拦截器对象-->
            <bean class="com.zzt.handler.MyIntercepter"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/test/**"/>
            <bean class="com.zzt.handler.MyIntercepter2"/>
        </mvc:interceptor>
    </mvc:interceptors>

​​​​​​

What will happen if the request is blocked in Interceptor 2?

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("2222----拦截器的preHandle");
        return false;
    }

Analysis: First, interceptor 1 will let it go, so interceptor 1 will think that the request can be processed; interceptor 2 will block, it thinks the request cannot be processed, so interceptor 2’s postHandle and aferCompletion methods cannot be executed; return to interceptor 1. The processor method is not executed, naturally the postHandle of interceptor 1 will not be executed, but because interceptor 1 allows the request, it will think that the request can be processed, so it will still execute the afterCompletion method.

[Note]: If we intercept at interceptor 1, then interceptor 2 behind it cannot intercept the request.

 

 

 

 

Guess you like

Origin blog.csdn.net/qq_39304630/article/details/112999483