SpringMVC学习指南(第二版)

package com.durian.ddp2.security.auth.configure.handle;

import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.CryptoException;
import cn.hutool.extra.servlet.ServletUtil;
import com.durian.ddp2.security.auth.common.RestResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @author 
 * 全局异常
 */
@RestControllerAdvice
public class ServerExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(ServerExceptionHandler.class);

    /**
     * 500
     */
    @ExceptionHandler(value = RuntimeException.class)
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    public RestResponse serverException(HttpServletRequest request, Exception ex) {
        log.error("500 request uri:{} from {} server exception:{}", request.getRequestURI(), ServletUtil.getClientIP(request), ex);
        if (ex instanceof CryptoException) {
            return new RestResponse().message("9995", ex.getMessage(), null);
        }
        return new RestResponse().message("badRequest", ex.getMessage(), null);
    }

    /**
     * 404
     */
    @ExceptionHandler(value = NoHandlerFoundException.class)
    @ResponseStatus(code = HttpStatus.NOT_FOUND)
    public RestResponse notFound(HttpServletRequest request, Exception ex) {
        log.error("404 request uri:{} from {} not found exception:{}", request.getRequestURI(), ServletUtil.getClientIP(request), ex.getMessage());
        return new RestResponse().message("badRequest", "资源不存在", null);
    }

    /**
     * 405
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(code = HttpStatus.METHOD_NOT_ALLOWED)
    public RestResponse methodNotFound(HttpServletRequest request, Exception ex) {
        log.error("405 request uri:{} from {} method not found exception:{}", request.getRequestURI(), ServletUtil.getClientIP(request), ex.getMessage());
        return new RestResponse().message("badRequest", "错误的请求方式", null);
    }

    /**
     * 400
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public RestResponse methodArgumentNotValid(HttpServletRequest request, MethodArgumentNotValidException ex) {
        log.error("400 request uri:{} from {} method params exception:{}", request.getRequestURI(), ServletUtil.getClientIP(request), ex.getMessage());
        BindingResult result = ex.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();
        StringBuffer msg = new StringBuffer();
        fieldErrors.stream().forEach(fieldError -> {
            msg.append("[" + fieldError.getField() + "," + fieldError.getDefaultMessage() + "]");
        });
        return new RestResponse().message("badRequest", "参数不合法:" + msg, null);
    }

    /**
     * 400
     */
    @ExceptionHandler(value = {MissingServletRequestParameterException.class})
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public RestResponse missingRequestParameter(HttpServletRequest request, MissingServletRequestParameterException ex) {
        log.error("400 request uri:{} from {} method params exception:{}", request.getRequestURI(), ServletUtil.getClientIP(request), ex.getMessage());
        return new RestResponse().message("badRequest", "参数不合法:" + ex.getMessage(), null);
    }

    /**
     * 400
     */
    @ExceptionHandler(value = {HttpMessageConversionException.class})
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public RestResponse messageConversionException(HttpServletRequest request, HttpMessageConversionException ex) {
        log.error("400 request uri:{} from {} server exception:{}", request.getRequestURI(), ServletUtil.getClientIP(request), ex.getMessage());
        List<String> expectTypes = ReUtil.findAll("`(.*?)`", ex.getMessage(), 1);
        String expectType = "";
        if (expectTypes.size() > 0) {
            String clazz = expectTypes.get(0);
            String[] names = clazz.split("\\.");
            expectType = names[names.length - 1];
        }
        if (StrUtil.isNotEmpty(expectType)) {
            return new RestResponse().message("badRequest", "参数类型错误:期望类型[" + expectType + "]", null);
        } else {
            return new RestResponse().message("badRequest", "请求失败", null);
        }
    }

}

0.记录点 

将页面放到WEB-INF目录下。WEB-INF目录下的任何文件或子目录都受保护,无法通过浏览器直接访问,但是控制可以转发请求到这些页面。
@RequestMapping(path = "/user",produces = {"application/json"})

@RequestMapping注解还设置了一个produces属性。上面案例表示,只会处理Accept头信息包含
"application/json"的请求。


@PostMapping(path = "addUser",consumes = "application/json")

@PostMapping注解存在consumes属性,consumes属性指定请求输入。上面案例表示,只会处理Content-Type
为application/json的请求。

@PatchMapping:PATCH的目的是对资源数据打补丁或局部更新。
@PutMapping:PUT真正的目的是执行大规模的替换操作,而不是更新操作。

 1.前言

SpringMVC是Spring框架中用于Web应用快速开发的一个模块。SpringMVC的MVC是Model-View-Controller的
缩写,它是一个广泛用于图形化用户交互开发中的设计模式。

2.Servlet

Java Servlet技术是Java体系中用于开发Web应用的底层技术。Servlet是运行在Servlet容器中的Java程序,
而Servlet容器或Servlet引擎相当于一个Web服务器。

一个Servlet是一个Java程序,一个Servlet应用包含了一个或多个Servlet,一个JSP页面会被翻译并编译成一
个Servlet。

一个Servlet应用运行在一个Servlet容器中,它无法独立运行。Servlet容器将来自用户的请求传递给Servlet
应用,并将Servlet应用的响应返回给用户。

 3.forward和redirece

调用request.getRequestDispatcher("/one/forward").forward(request,response)方法或者
response.sendRedirect();方法并不会停止执行剩余的代码。因此,若forward或sendRedirect方法
不是最后一行代码,则应该显示的返回。
@Slf4j
@RestController
@RequestMapping("/fr")
public class ForwardAndRedirectController {

    @GetMapping("/one")
    public void oneAction(HttpServletRequest request, HttpServletResponse response) throws Exception{
      log.info("请求One~~~~~~~");
      request.getRequestDispatcher("/fr/one/forward").forward(request,response);
      log.info("已经forward转发了 并不会停止执行剩余代码 应该显示返回");
      return;
    }

    @GetMapping("/one/forward")
    public String oneForward(HttpServletRequest request,HttpServletResponse response){
        log.info("one forward");
        return "one forward";
    }

}


输出1: 请求One~~~~~~~(oneAction)
输出2: one forward(oneForward)
输出3: 已经forward转发了 并不会停止执行剩余代码 应该显示返回(oneAction 已经转发了 但是还是输出)


-----------------------------------------------------------------------------------------
通过return 方式这样编码可以避免上面的问题

@Slf4j
@RestController
@RequestMapping("/fr")
public class ForwardAndRedirectController {

    @GetMapping("/one")
    public String oneAction(HttpServletRequest request, HttpServletResponse response) throws Exception{
      log.info("请求One~~~~~~~");
      return "forward:/one/forward";
    }

    @GetMapping("/one/forward")
    public String oneForward(HttpServletRequest request,HttpServletResponse response){
        log.info("one forward");
        return "one forward";
    }
}

@Slf4j
@RestController
@RequestMapping("/fr")
public class ForwardAndRedirectController {

    @GetMapping("/two")
    public void twoAction(HttpServletRequest request, HttpServletResponse response) throws Exception{
      log.info("请求two~~~~~~~");
      response.sendRedirect("/fr/two/redirect");
      log.info("已经redirect转发了 并不会停止执行剩余代码 应该显示返回");
      return;
    }

    @GetMapping("/two/redirect")
    public String oneForward(HttpServletRequest request,HttpServletResponse response){
        log.info("two redirect");
        return "two redirect";
    }
}


输出1:请求two~~~~~~~
输出2:已经redirect转发了 并不会停止执行剩余代码 应该显示返回(twoAction已经重定向了 还是输出了该语句)
输出3:two redirect

---------------------------------------------
注意:

1.通过重定向,可以将浏览器定向到其他应用程序,这是转发不能支持的。
2.重定向后若用户按下浏览器的重新加载/刷新按钮,则与原始请求URL相关联的代码将不会再次执行。

4.构建SpringMvc工程

1.pom依赖
  
  <properties>
    <spring.mvc.version>5.3.9</spring.mvc.version>
  </properties>

  <dependencies>
    <!-- SpringMVC依赖Apache Commons Logging组件-->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>
    <!-- Spring start-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring.mvc.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.mvc.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.mvc.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>${spring.mvc.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.mvc.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.mvc.version}</version>
    </dependency>
    <!-- end-->
  </dependencies>
2.配置web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>

   <servlet>
     <servlet-name>SpringMvcOne</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <!--
        DispatcherServlet在初始时,它会寻找在应用程序的WEB-INF目录下的一个配置文件,该配置文件
        的规则如下:servlet-name-servlet.xml。其中,servlet-name是部署描述符中的
        DispatcherServlet的名称。比如:当前应该是在WEB-INF下对应的文件是SpringMvcOne-servlet.xml
     -->
     <!--
       此外,也可以把SpringMvc的配置文件放在应用程序目录的任何地方,可以通过如下配置
       指定,告诉DispatcherServlet去哪里找到该文件。
     -->
     <init-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>/WEB-INF/config/simple-config.xml</param-value>
     </init-param>
     <!-- load-on-startup元素可选。如果它存在,则将在应用程序启动时装载servlet并调用它的init方法。
          若它不存在,则在该servlet的第一个请求时加载。
          特点:
              1.值必须是一个整数 从0开始 值越小优先加载
              2.元素不存在或者值为负数初始不会加载
              3.当值相同时,容器会自主选择加载顺序
              4.值为异常值或者浮点数 容器无法正常启动
     -->
     <load-on-startup>1</load-on-startup>
   </servlet>

  <servlet-mapping>
    <servlet-name>SpringMvcOne</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
   

</web-app>


注意:如何同时存在init-param、和load-on-startup。init-param需配置到load-on-startup前面。
3.simple-config.xml

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

    <!-- 声明HomeController控制器类,并映射到/home-index -->
    <bean name="/home-index" class="jp.reborn.controller.HomeController"></bean>

</beans>
4.HomeController

package jp.reborn.controller;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

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

/**
 * @author 小李同学
 * @pageage controller
 * @date 2021/3/21 12:24
 * @week 星期日
 * @action
 */

public class HomeController implements Controller {

    private static final Log logger = LogFactory.getLog(HomeController.class);

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        return new ModelAndView("/WEB-INF/page/home.jsp");
    }
}

5.视图解析器

SpringMvc中的视图解析器负责解析视图。可以通过在配置文件中定义一个ViewResolver来配置视图解析器。

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

    <!-- 声明HomeController控制器类,并映射到/home-index -->
    <bean name="/home-index" class="jp.reborn.controller.HomeController"></bean>
    <!-- 视图解析器-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/page/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

视图解析器有前缀和后缀两个属性,配置完成后视图解析器将会自动增加前缀和后缀。
package jp.reborn.controller;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

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

/**
 * @author 小李同学
 * @pageage controller
 * @date 2021/3/21 12:24
 * @week 星期日
 * @action
 */

public class HomeController implements Controller {

    private static final Log logger = LogFactory.getLog(HomeController.class);

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        return new ModelAndView("home");
    }
}

6.基于注解的控制器

Controller和RequestMapping注释类型是SpringMVC API最重要的两个注解类型。
1.配置XML扫包

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

    <!-- 配置扫描控制器基本包-->
    <context:component-scan base-package="jp.reborn.controller"/>
    <!-- 视图解析器-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/page/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>
package jp.reborn.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @author 小李同学
 * @pageage jp.reborn.controller
 * @date 2021/3/21 15:35
 * @week 星期日
 * @action
 */
@Controller
@RequestMapping("/annotation")
public class AnnotationController {

   @RequestMapping(method = RequestMethod.GET,value = "/action")
   public String annotationAction(){
       return "annotation";
   }
}


注意:
如果没有指定method属性值,则请求处理方法可以处理任意HTTP方法。

7.处理静态资源

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

    <!-- 配置扫描控制器基本包-->
    <context:component-scan base-package="jp.reborn.controller"/>
    <!-- 视图解析器-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/page/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    
    <mvc:annotation-driven/>
    <!-- resources元素指示某些资源不通过DispatcherServlet处理-->
    <!-- 项目结构 不截图了 
         /webapp/css/index.css
         /webapp/html/index.html
         /webapp/WEB-INF/web.xml
         /webapp/WEB-INF/config/simple-config.xml
         /webapp/WEB-INF/page/*.page   
    -->
    <mvc:resources mapping="/css/**" location="/css/"/>
    <mvc:resources mapping="/html/*.html" location="/html/"/>
</beans>

使用<mvc:resources>标签,可以标准某些资源不需要通过DispatcherServlet处理映射,但是需要注意
如果使用了<mvc:resources>标签,那么正常可以被DispatcherServlet处理的请求,将无法映射。所以
还需要使用<mvc:annotation-driven/>注解,这样既可以处理正常的DispatcherServlet请求,也可以处理不需要通过的DispatcherServlet请求。

注意:如果不需要使用<mvc:resources>则可以不配置<mvc:annotation-driven/>。

8.model对象

org.springframework.ui.Model类型。这不是一个Servlet API类型,而是一个包含Map的SpringMVC类型。
SpringMVC在每一个请求处理方法被调用时都会创建一个Model实例,(不管是否使用)用于增加需要显示
在视图中的属性。例如,调用model.addAttribute方法。


package jp.reborn.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author 小李同学
 * @pageage jp.reborn.controller
 * @date 2021/3/21 16:47
 * @week 星期日
 * @action
 */
@Controller
@RequestMapping("/model")
public class ModelController {
    
    @RequestMapping("/action")
    public String modelAction(Model model){
        model.addAttribute("name","ldd_java");
        model.addAttribute("age","72");
        return "model";
    }
}
<!DOCTYPE html>
<html>
<head>
    <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
</head>
<body>
<div>
    <h1>You are so Good</h1>
    <span>Name:${name}</span>
    <br/>
    <span>Age:${age}</span>
</div>
</body>
</html>



注意:默认情况下,Servlet2.3/jsp1.2默认是不支持el表达式的,此时有两种解决方案。
第一种:表示强制性的不忽略el表达式格式

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
第二种方式:
使用servlet的更高版本,比如我直接将web.xml改成2.5版本。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">
 
</web-app>



<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>
    <h1>You are so Good</h1>
    <span>Name:${name}</span>
    <br/>
    <span>Age:${age}</span>
</div>
</body>
</html>

9.@Autowired和@Service进行注入

1.SpringMvc中新增<context:component-scan/>一个扫描控制器类,另外一个用于扫描服务类。

<!-- 配置扫描控制器基本包-->
<context:component-scan base-package="jp.reborn.controller"/>
<!-- 配置扫描Service基本包-->
<context:component-scan base-package="jp.reborn.service"/>
2.@Autowired注入

@Controller
@RequestMapping("/model")
public class ModelController {

    @Autowired
    private ProductService productService;

    @RequestMapping("/action")
    public String modelAction(Model model){
        User user = new User();
        productService.addUser(user);
        model.addAttribute("name","ldd_java");
        model.addAttribute("age","72");
        return "model";
    }
}

 10.重定向和Flash

转发比重定向快,因为重定向经过客户端,而转发没有。若需要重定向到一个外部网站,则无法使用转发。

使用重定向可以避免用户在重新加载页面(F5刷新)时再次调用同样的动作(重复刷新重复提交问题)。

由于重定向需要经过客户端,所以model中的一切数据都在重定向时丢失。Spring3.1版本以及更高版本通过
Flash属性提供了一种供重定向传值的方法。

注意:
要使用Flash属性,必须在SpringMvc配置中配置 <mvc:annotation-driven/>元素。然后,还必须在方法
上添加一个新的参数模型org.springframework.web.servlet.mvc.support.RedirectAttributes。


@Controller
@RequestMapping("/redirect")
public class RedirectController {

    @RequestMapping("/action")
    public String redirectAction(String userName, RedirectAttributes redirectAttributes){
        redirectAttributes.addAttribute("userName",userName);
        return "redirect:/redirect/actionResult";
    }


    @RequestMapping("/actionResult")
    public String redirectResultAction(String userName,Model model){
        model.addAttribute("userName",userName);
        return "redirectResult";
    }

}

直接通过redirectAttributes.addAttribute("userName",userName);这种方式就相当于重定向之后,
在url后面拼接参数,这样在重定向之后的页面或者控制器再去获取url后面的参数就可以了,但这个方式
因为是在url后面添加参数的方式,所以暴露了参数,有风险。
2.redirectAttributes.addFlashAttributie("prama",value); 这种方法是隐藏了参数,链接地址上不直接
暴露,但是能且只能在重定向的 “页面” 获取prama参数值。其原理就是放到session中,session在跳到页面后
马上移除对象。如果是重定向一个controller中是获取不到该prama属性值的。


@Controller
@RequestMapping("/redirect")
public class RedirectController {

    @RequestMapping("/action")
    public String redirectAction(String userName, RedirectAttributes redirectAttributes){
        redirectAttributes.addFlashAttribute("userName",userName);
        return "redirect:/redirect/actionResult";
    }


    @RequestMapping("/actionResult")
    public String redirectResultAction(){
        return "redirectResult";
    }

}
请求:http://localhost:9091/SpringMvc20_war/redirect/action?userName=ldd会重定向actionResult
控制器直接跳转到redirectResult.jsp页面,在jsp页面中可以通过EL表达式取到值。

注意:
使用addFlashAttribute方法,我们在重定向后的action中并没有使用model设置userName属性,依然可以
在页面中取到值。上面使用redirectAttributes.addAttribute方法,就必须在重定向后的action中使用
model或者其他的对象存储userName的值,否则在页面中取不到值的。
3.redirectAttributes.addFlashAttributie("prama",value)值可以通过这种方法在Controller中取到值。

@Controller
@RequestMapping("/redirect")
public class RedirectController {

    private static final Log logger = LogFactory.getLog(RedirectController.class);

    @RequestMapping("/action")
    public String redirectAction(String userName, RedirectAttributes redirectAttributes){
        redirectAttributes.addFlashAttribute("userName",userName);
        return "redirect:/redirect/actionResult";
    }

    @RequestMapping("/actionResult")
    public String redirectResultAction(@ModelAttribute("userName") String userName){
        logger.info("userName="+userName);
        return "redirectResult";
    }
}

注意:
1.必须要添加@ModelAttribute标签,否侧将读不到值
2.且必须指定变量名,并不会自动做匹配

 11.请求参数和路径变量

请求参数和路径变量都可以用于发送值给服务器。
1.请求参数采用key=value形式,并用"&"分隔。

http://localhost:9091/SpringMvc20_war/redirect/action?userName=ldd&age=18

在传统的servlet编程中,可以通过httpServletRequest.getParameter("userName");获取参数,SpringMvc
提供了@RequestParam注解获取参数。

public void sendProduct(@RequestParam String userName,@RequestParam int age)

@RequestParam注解参数类型不一定是字符串。


2.路劲参数没有key部分,只是一个值。

http://localhost:9091/SpringMvc20_war/redirect/action/{userName}/{age}

@RequestMapping("/action/{userName}/{age}")
public String redirectAction(@PathVariable String userName, @PathVariable int age, Model model){
	logger.info("userName="+userName);
	logger.info("age="+age);
	model.addAttribute("userName",userName);
	model.addAttribute("age",age);
	return "redirectResult";
}

 12.@ModelAttribute

SpringMvc在每次调用请求处理方法时,都会创建Model类型的一个实例。若要使用该实例
,则可以在方法中添加一个Model类型的参数。SpringMvc还提供了一个注解@ModelAttribute用来快速
访问Model实例。

@ModelAttribute可以作用在方法参数和方法上。

@ModelAttribute注解的参数会自动存入Model对象中,相当于调用model.addAttribute方法。

@Controller
@RequestMapping("/model")
public class ModelController {

    @Autowired
    private ProductService productService;

    @RequestMapping("/action")
    public String modelAction(@ModelAttribute("name") String name){
        return "model";
    }
}

调用此方法会将name值存入model对象中,key为"name"。
@ModelAttribute修饰非请求处理的方法。被@ModelAttribute修饰的方法会在每次调用该控制器其他方法时被
调用。

@ModelAttribute修饰的方法可以返回一个对象或一个void类型。如果返回一个对象,则返回的对象会自动
添加到Model中。

@Controller
@RequestMapping("/model")
public class ModelController {

    private static final Log logger = LogFactory.getLog(ModelController.class);

    @Autowired
    private ProductService productService;

    @ModelAttribute
    public User initModel(@RequestParam String name, @RequestParam int age){

        logger.info("one调用");
        User user = new User();
        user.setUserName(name);
        user.setAge(age);
        return user;
    }

    @RequestMapping("/action")
    public String modelAction(@RequestParam String job, Model model){
        logger.info("two调用");
        model.addAttribute("job",job);
        return "model";
    }
}

请求地址:
http://localhost:9091/SpringMvc20_war/model/action?name=xx&age=12&job=it


如果返回void类型,则还必须添加一个Model类型参数,并手动将实例添加到Model中
@Controller
@RequestMapping("/model")
public class ModelController {

    private static final Log logger = LogFactory.getLog(ModelController.class);

    @Autowired
    private ProductService productService;

    @ModelAttribute
    public void initModel(@RequestParam String name, @RequestParam int age,Model model){

        logger.info("one调用");
        User user = new User();
        user.setUserName(name);
        user.setAge(age);
        model.addAttribute("user",user);
    }

    @RequestMapping("/action")
    public String modelAction(@RequestParam String job, Model model){
        logger.info("two调用");
        model.addAttribute("job",job);
        return "model";
    }
}
页面取值:
<div>
    <h1>You are so Good</h1>
    <span>Name:${user.userName}</span>
    <br/>
    <span>Age:${user.age}</span>
    <br/>
    <span>Age:${job}</span>
</div>

 13.转换器Converter

Converter(转换器)是通用元件,可以在应用程序的任意层中使用,而Formatter(格式化)则是专门为Web层设计
的。
1.
创建Converter必须实现org.springframework.core.convert.converter.Converter接口,这个接口声明如
下
public interface Converter<S, T> {
    T convert(S var1);
}

这里S表示源类型,T表示目标类型。例如下面,将String转换成LocalDate类型,创建Converter如下

/**
 * @author 小李同学
 * @pageage jp.reborn.converter
 * @date 2021/3/29 14:13
 * @week 星期一
 * @action
 */
public class  StringToLocalDateConverter implements Converter<String, LocalDate> {
   
    //转换的规则 如:yyyy-MM-dd  
    private String datePattern;

    public StringToLocalDateConverter(String datePattern){
        this.datePattern = datePattern;
    }

    @Override
    public LocalDate convert(String s) {
        try{
            return LocalDate.parse(s, DateTimeFormatter.ofPattern(datePattern));
        }catch (Exception e){
             e.printStackTrace();
             throw  new IllegalArgumentException("日期转换错误");
        }
    }
}
2.
在SpringMvc配置文件配置并应用自定义Converter。

<bean/>的id可以自行设置,但是class名必须为
org.springframework.context.support.ConversionServiceFactoryBean。

<!-- 配置Converter-->
<bean id="stringToDateService" class="org.springframework.context.support.ConversionServiceFactoryBean">
	<property name="converters">
		<list>
            <!-- list里面设置自定义的Converter--> 
			<bean class="jp.reborn.converter.StringToLocalDateConverter">
				<constructor-arg type="java.lang.String" value="yyyy-MM-dd" />
			</bean>
		</list>
	</property>
</bean>


<!-- 追加自定义Converter 此处id为上面自定义设置的id-->
<mvc:annotation-driven conversion-service="stringToDateService"/>
3.创建实体

public class Book implements Serializable {


    private String bookName;

    //前端传递字符串类型会自动触发Converter进行准换
    private LocalDate createDate;

    //略get set
}



@Controller
@RequestMapping("/form")
public class FormController {

      @RequestMapping("/input_book")
      public String inputBook(Model model){
          return "book";
      }

      @RequestMapping(value = "/save_book",method = RequestMethod.POST)
      public String saveBook(@ModelAttribute("book") Book book){
          return "bookResult";
      }
}
4.HTML

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div>
   <form  method="post" action="${pageContext.request.contextPath }/form/save_book">
       <input type="text" name="bookName" />
       </br>
       <input type="text" name="createDate">
       <input type="submit" value="提交">
   </form>
</div>
</body>
</html>

 14.格式化Formatter

Formatter与Converter一样,也是将一种类型转换成另一种类型。但是Formatter的源数据类型必须是String
,而Converter则适用于任意的源类型。
1.创建Formatter需要实现org.springframework.format.Formatter接口public interface Formatter<T>。

 这里的T表示输入字符串要转换的目标类型。该接口有

String print(T var1, Locale var2);

T parse(String s, Locale locale);

parse方法将一个String解析成目标类型。而print方法与之相反,它返回目标对象的字符串表示。

package jp.reborn.formatter;

import org.springframework.format.Formatter;

import java.text.ParseException;
import java.util.Locale;

/**
 * @author 小李同学
 * @pageage jp.reborn.formatter
 * @date 2021/3/30 18:44
 * @week 星期二
 * @action
 */
public class StringToIntegerFormatter implements Formatter<Integer> {

    //parse方法执行转换  String -> 需要的目标类型
    @Override
    public Integer parse(String s, Locale locale) throws ParseException {
        if(s.contains("元")){
            return Integer.valueOf(s.substring(0,s.indexOf("元")));
        }
        return Integer.valueOf(s);
    }

    //print 方法返回转换完成目标对象的字符串表示法
    @Override
    public String print(Integer integer, Locale locale) {
        return null;
    }
}
2.创建实体Bean

public class Book implements Serializable {


    private String bookName;

    private Integer price;
   
    //略get set ...
}
3.配置SpringMvc配置文件

<!-- 配置扫描Formatter-->
<context:component-scan base-package="jp.reborn.formatter"/>

	<!-- 配置Formatter-->
<bean id="stringToInteger" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
	<property name="formatters">
		<set>
			<bean class="jp.reborn.formatter.StringToIntegerFormatter" />
		</set>
	</property>
</bean>

<!-- 追加自定义Formatter-->
<mvc:annotation-driven conversion-service="stringToInteger"/>
4.HTML

<div>
   <form  method="post" action="${pageContext.request.contextPath }/form/save_book">
       <input type="text" name="bookName" />
       </br>
       <input type="text" name="price">
       <input type="submit" value="提交">
   </form>
</div>

 15.Registrar注册Formatter

1.注册Formatter的另外一种方法是使用Registrar。


/**
 * @author 小李同学
 * @pageage jp.reborn.config
 * @date 2021/4/3 14:28
 * @week 星期六
 * @action
 */
public class MyFormatterRegistrat implements FormatterRegistrar {
    @Override
    public void registerFormatters(FormatterRegistry formatterRegistry) {
        formatterRegistry.addFormatter(new StringToIntegerFormatter());
    }
}
2.配置SpringMvc配置文件

<!-- 配置Formatter-->
<bean id="stringToInteger" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <!-- 直接使用formatterRegistrars属性-->
	<property name="formatterRegistrars">
		<set>
			<bean class="jp.reborn.config.MyFormatterRegistrat" />
		</set>
	</property>
</bean>

	<!-- 追加自定义Formatter-->
<mvc:annotation-driven conversion-service="stringToInteger"/>

 16.验证器

SpringMvc有两种方式可以验证输入,即利用Spring自带的验证框架(Spring Validation框架早于JSR 303),或者利用JSR303实现。

Converter和Formatter作用于字段级,而验证器作用于对象级。它决定某一个对象中的所有字段是否均有效,以
及是否符合某种规则。

如果有一个程序即使用了Converter|Formatter,又有验证器(validator),那么应用中的事件顺序是这样的:

在调用Controller期间,先转换字段类型,转换成功后,验证器就会调用检测。

 17.Spring验证器

Spring验证器需要实现org.springframework.validation接口。

public interface Validator {
	boolean supports(Class<?> clazz);
	
	void validate(Object target, Errors errors);
}

如果验证器可以处理指定的Class,supports方法将返回true。validate方法会验证目标对象,并将验证错误
填入Errors对象。


Errors对象是org.springframework.validation.Errors接口的一个实例。Errors对象中包含了FieldError
和ObjectError对象。FieldError表示与被验证对象中的某个属性相关的一个错误。


//感觉Spring验证器太麻烦了 案例略。。。

 18.Spring验证器-ValidationUtils类

org.springframework.validation.ValidationUtils类是一个工具,有助于编写Spring验证器。

下面是ValidationUtils中rejectIfEmpty和rejectIfEmptyOrWhitespace方法的方法重载。

public static void rejectIfEmpty(Errors errors, String field, String errorCode) 
public static void rejectIfEmpty(Errors errors, String field, String errorCode, String defaultMessage) 
public static void rejectIfEmpty(Errors errors, String field, String errorCode, Object[] errorArgs)
public static void rejectIfEmpty(Errors errors, String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage)

public static void rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode)
public static void rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode, String defaultMessage) 
public static void rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode, @Nullable Object[] errorArgs) 
public static void rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage)

ValidationUtils还有一个invokeValidator方法,用来调用验证器。
public static void invokeValidator(Validator validator, Object target, Errors errors)

19.Spring验证器实战-ONE

创建UserValidation类实现org.springframework.validation接口。


/**
 * @author 小李同学
 * @pageage com.ldd.validation
 * @date 2021/7/18 17:17
 * @week 星期日
 * @action
 */
public class UserValidation implements Validator {
    @Override
    public boolean supports(Class<?> aClass) {
        /**
         * 如果aClass是User.class 则返回true
         * supports方法返回true才会调用validate进行验证
         */
        return User.class.isAssignableFrom(aClass);
    }

    @Override
    public void validate(Object o, Errors errors) {
        User user = (User) o;
        /**
         * 可以通过ValidationUtils直接校验
         */
        ValidationUtils.rejectIfEmpty(errors, "id", "user.required");
        /**
         * 自定义逻辑判断
         */
        if (!"张三".equals(user.getUserName())) {
            errors.rejectValue("userName", "user.invalid");
        }
    }
}
-------------------------------------
验证器不需要显示注册,但是如果想要从某个属性文件中获取错误消息,则需要通过声明messageSource 
bean,告诉Spring要去哪里查找这个文件。

//SpringBoot配置
@Configuration
public class BeanConfig {

    /**
     * @Author: 13513
     * @Date: 2021/7/18 18:04
     * @Param: * @param
     * @Return: {@link ReloadableResourceBundleMessageSource}
     * 注入messages
     */
    @Bean(name = "messageSource")
    public ReloadableResourceBundleMessageSource messageSource() {
        ReloadableResourceBundleMessageSource bundleMessageSource = new ReloadableResourceBundleMessageSource();
        bundleMessageSource.setBasename("classpath:messages/messages");
        bundleMessageSource.setDefaultEncoding("UTF-8");
        return bundleMessageSource;
    }
//XML文件配置
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
   <property name="basename" value="/WEB-INF/resource/messages"/>
</bean>

bean实际是说,错误码和错误消息可以在/WEB-INF/resource目录下的messages.properies文件中找到。


编写对应的Controller类通过实例化validator类,使用Spring验证器。为了校验验证器是否生成错误消息,
需要在BindingResult中调用hasErrors方法。

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("/update")
    public String userUpdate(@RequestBody User user, BindingResult bindingResult) {
        UserValidation userValidation = new UserValidation();
        userValidation.validate(user, bindingResult);
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            log.error("code:" + fieldError.getCode());
            log.error("field:" + fieldError.getField());
            log.info("result:NO~~~");
            return "NO";
        }
        log.info("result:OK~~~");
        return "OK";
    }
}

com.ldd.controller.UserController        : code:user.id.required
com.ldd.controller.UserController        : field:id
com.ldd.controller.UserController        : result:NO~~~

20.Spring验证器实战-TWO

使用Spring验证器的另一种方法是:在controller中编写initBinder方法,并将验证器传到WebDataBinder,
并调用其validate方法。

public class BaseController {

    /**
     * 将验证器传到WebDataBinder,会使该验证器应用于Controller类中所有处理请求的方法。
     * @param binder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder){
        binder.setValidator(new UserValidation());
        binder.validate();
    }
}

--------------------------------------------------------------------------------

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController extends BaseController{

    @PostMapping("/update")
    public String userUpdate(@RequestBody User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            log.error("code:" + fieldError.getCode());
            log.error("field:" + fieldError.getField());
            log.info("result:NO~~~");
            return "NO";
        }
        log.info("result:OK~~~");
        return "OK";
    }
}


UserController继承BaseController

21.JSR303验证

JSR 303(Java验证规范 发布于2009年11月) "Bean Validation" 和JSR 349 "Bean Validation1.1"(发布
于2013年5月)指定了一整套API,通过注解给对象属性添加约束。JSR 303和JSR 349可以分别从以下网站下载

https://jcp.org/en/jsr/detail?id=303
https://jcp.org/en/jsr/detail?id=349


JSR只是一个规范文档,要使用JSR必须编写它的实现。对于JSR bean validation
存在两个实现,第一个是Hibernate Validator JSR303和JSR349它都实现了。第二个是Apache BVal。

JSR 303 不需要编写验证器,但要利用JSR 303注解类型嵌入约束。
JSR303约束
属性描述案例
@AssertFalse应用于boolean属性,该属性值必须为False

@AssertFalse

boolean hasLi

@AssertTrue应用于boolean属性,该属性值必须为True

@AssertTrue

boolean hasLi

@DecimalMax 该属性值必须为小于或等于指定值的小数

@DecimalMax("1.1")

BigDecimal price

@DecimalMin该属性值必须为大于或等于指定值的小数

@DecimalMin("0.4")

BigDecimal price

@Digits该属性值必须在指定的范围内。integer属性定义该数值的最大整数部分,fraction属性定义该数值的最大小数部分

@Digits(integer=5,fraction=2)

BigDecimal price

@Future 该属性值必须是未来的一个日期

@Future

Date shippDate

@Max该属性值必须为小于或等于指定值的整数

@Max(120)

int age

@Min该属性值必须为大于或等于指定值的整数

@Max(110)

int age

@NotNull该属性值不能为Null

@NotNull

String userName

@Null该属性值必须为Null

@Null

String userName

@Past该属性值必须是过去的一个日期

@Past

Date shippDate

@Pattern该属性值必须与指定的常规表达式相匹配

@Pattern(regext="\\d{3}")

String areaCode

@Size 该属性值必须在指定的范围内

@Size(min=2,max=140)

String gudu

可以在属性文件中以下列格式来覆盖JSR303验证器的错误消息。

标签名.校验对象.属性名

例如,覆盖以@NotNull注解约束的User对象的userName属性,可以在属性文件中配置如下:
NotNull.user.userName=xxxxx

22.Hibernate Validator案列

SpringBoot使用Validator需要引入POM

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

或者直接引入hibernate-validator
<dependency>
	<groupId>org.hibernate.validator</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>6.2.0.Final</version>
	<scope>compile</scope>
</dependency>
构建Car实体

@Data
public class Car {
    @NotNull(message = "carId不能为空")
    private Integer id;
    @NotNull(message = "carName不能为空")
    private String name;
    @Past(message = "非法订单日期:订单日期必须小于当前日期")
    private Date createDate;
}



构建CarController

注意:index方法中,必须用@Valid对Car参数进行注解。

@Slf4j
@RestController
@RequestMapping("/car")
public class CarController {

    @PostMapping("/index")
    public String index(@Valid Car car, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            log.info("\ncode:" + fieldError.getCode() + "\nobject:" + fieldError.getObjectName()
                    + "\nfield:" + fieldError.getField()+"\nmessage:"+fieldError.getDefaultMessage());
            return "NG";
        }
        return "OK";
    }
}

模拟异常数据,请求访问输出如下
code:NotNull
object:car
field:id
message:carId不能为空

22.表达式语言(EL)

学习表达式语言,重新构建一个项目,项目一览如下

EL表达式最初是应用在JSP标准标签库(JSTL)规范中,只需将标准库导入到应用程序中,就可以使用EL表达式。


2013年5月发布了EL3.0版本,EL不再是JSP或者任何其他技术的一部分,而是一个独立的规范。EL3.0添加了
对lambda表达式的支持,并允许集合操作。其lambda支持不需要JavaSE8,JavaSE7即可。
EL表达式以${开头,并以}结束。EL表达式的结构如下:
${expression}
#{expression}

例如 x+y可以写成 

${x+y}
或
#{x+y}。


${exp}和#{exp}结构都由EL引擎以相同的方式进行计算。但是在JSP或者JSF底层技术中,$和#用不同的含义解释。
在JSF中${exp}结构用于立即计算,#{exp}结构用于延迟计算。(即表达式直到系统需要它的值时,才进行计算)。

立即计算的表达式${exp},会在JSP页面编译时同时编译,并在执行JSP页面时被执行。在JSP2.1和更高版本中,
#{exp}表达式只能在接受延迟表达式的标签属性中使用。 

注意:对于一系列的表达式,它们的取值将是从左到右进行运算,计算的结果类型为String。
列如 a+b等于8,c+d等于10,那么这两个表达式的计算结果将是810

${a+b}${c+d}


注意:如果在定制标签的属性值中使用EL表达式,那么该表达式的取值结果字符串将会强制变成该属性需要的类
型。

23.[]和.运算符

EL表达式的结果如果是一个带有属性的对象,则可以利用[]或者.运算符来访问该属性。[]和.运算符类似;
[]是比较规范,.运算符则比较快捷。

案例:

${user["userName"]}
${user.userName}

注意:如果userName不是有效的Java变量名,只能通过[]运算符。比如要访问a-b属性,只能使用[]运算符,因
为a-b不是一个合法的Java变量名。如果用.运算符访问它,将会导致异常。

24.EL表达式取值规则

EL表达式取值是从左到右进行的。对于expr-a[expr-b]形式的表达式,其EL表达式的取值方法如下:
1.先计算expr-a得到value-a。
2.如果value-a为null,则返回null。
3.然后计算expr-b得到value-b。
4.如果value-b为null,则返回null
5.如果value-a为Map,则会查看value-b是否为Map中的一个key。若是,则返回value-a.get(value-b),若
不是,则返回null.
6.如果value-a为List,则要进行一下处理
 6.1强制value-b为int, 如果强制失败,则抛出异常。
 6.2如果value-a.get(value-b)抛出IndexOutOfBoundsException,或者假设Array.get(value-a,value-
 b)抛出ArrayIndexOutOfBoundsException,则返回null。
 6.3否则,若value-a是个List,则返回value-a.get(value-b);若value-a是个array,则返回
Array.get(value-a,value-b)。

7.如果value-a不是一个Map、List或者array,那么,value-a必须是一个JavaBean。在这种情况下,必须强制
value-b为String。如果value-b是value-a的一个可读属性,则要调用该属性的getter方法,从中返回值。
如果getter方法抛出异常,该表达式就是无效的,否则,该表达式有效。

25.EL隐式对象

对象描述
pageContext这是当前JSP的javax.servlet.jsp.pageContext
initParam这是一个包含所有环境初始化参数并用参数名作为key的Map
param这是一个包含所有请求参数并用参数名作为key的Map。每个key的值就是指定名称的第一个参数值。因此,如果两个请求参数同名,则只有第一个能够利用param获取值。要访问同名参数的所有参数值,可用params代替。
paramValues这是一个包含所有请求参数并用参数名作为key的Map。每个key的值就是一个字符串数组,其中包含了指定参数名称的所有参数值。就算该参数只有一个值,它也仍然会返回一个带有一个元素的数组。
header这是一个包含请求标题并用标题名作为key的Map。每个key的值就是指定标题名称的第一个标题。
headerValues这是一个包含请求标题并用标题名作为key的Map。每个key的值就是一个字符串数组,其中包含了指定标题名称的所有参数值。就算该标题只有一个值,它也然后会返回一个带有一个元素的数组。
cookie这是一个包含了当前请求对象中所有Cookie对象的Map,Cookie名称就是key名称,并且每个key都映射到一个Cookie对象。
applicationScope这是一个包含了ServletCntext对象中所有属性的Map,并用属性名作为key。
sessionScope这是一个包含了HttpSession对象中所有属性的Map,并用属性名称作为key。
requestScope这是一个Map,其中包含了当前HttpServletRequest对象中的所有属性,并用属性名称作为key。
pageScope这是一个Map,其中包含了全页面范围的所有属性。属性名称就是Map的key。

26.pageContext

pageContext对象表示当前JSP页面的javax.servlet.jsp.PageContext。它包含了所有其他的JSP隐式对象。

1.request
2.response
3.out
4.session
5.application
6.config
7.PageContext
8.page
9.exception

例如,可以利用一下任意一个表达式来获取当前的ServletRequest;
${pageContext.request}
${pageContext["request"]}

并且,还可以利用以下任意一个表达式来获取请求方法。

${pageContext["request"]["method"]}
${pageContext["request"].method}
${pageContext.request["method"]}
${pageContext.request.method}
pageContext.request中一些有用的属性
characterEncoding请求的字符编码
contentType请求的MIME类型
protocolHTTP协议,例如:HTTP/1.1
remoteAddr客户端IP地址
remoteHost客户端IP地址或主机名
scheme请求发送方案,Http或HTTPS
serverName服务器主机名
serverPort服务器端口
secure请求是否通过安全链接传输
locales所有locale

27.param

隐式对象param用于获取请求参数值。这个对象表示一个包含所有请求参数的Map。例如,要获取userName
参数,可以使用以下任意一种表达式:

${param.userName}
${param["userName"]}

28.header

隐式对象header表示一个包含所有请求标题的Map。为了获取header值,要利用header名称作为key。
例如,为了获取accept-language这个header值,可以使用以下表达式:

29.cookie

隐式对象cookie可以用来获取一个cookie。这个对象表示当前HttpServletRequest中所有cookie的值。例如:
为了获取名为jsessionid的cookie值,要使用以下表达式:
${cookie.jsessionid.value}

为了获取jsessionid cookie的路径值,要使用如下表达式:
${cookie.jsessionid.path}

30.算术运算符

算术运算符有5种:

1.加法(+)
2.减法(-)
3.乘法(*)
4.除法(/ 和 div)
5.取余/取模(% 和 mod)

优先级按照从高到低、从左到右进行。下列运算符是按优先级递减顺序排列的:

*、/、div、%、mod:第一组优先级相同

+、-:第二组优先级相同

第一组优先级高于第二组优先级。

<p>运算符</p>
<span>6/2${6/2}</span>
<span>6 div 2${6 div 2}</span>

<span>6 % 2 ${6 % 2}</span>
<span>6 mod 2 ${6 mod 2}</span>

31.关系运算符

关系运算符:

1.等于(== 和 eq)
2.不等于(!=和eq)
3.大于(>和gt)
4.大于或等于(>=和ge)
5.小于(<和lt)
6.小于或等于(<=和le)

32.逻辑运算符

逻辑运算符

1.和 (&& 和 and)
2.或 (|| 和 or)
3.非 (!  和 not)

33.empty运算符

empty运算符用来检测某个值是否为null或者empty。

例如:${empty X}
如果X为null,或者说X是一个长度为0的字符串,那么该表达式将返回True。如果X是一个空Map、空数组或者
空集合,它也将返回True。否则,返回False。

34.字符串连接运算符

+=y运算符用于连接字符串。例如,以下表达式打印a+b的值。

<span>${"张" += "三"}</span>  "张三"

35.引用静态属性和静态方法

在JSP页面中引用静态字段或方法之前,必须使用page伪指令导入类或类包。java.lang包是一个例外,因为
它是自动导入的。

例如,以下page指令导入java.time包。
<%@ page import="java.time.*" %>

或者,导入单个类,例如
<%@ page import="java.time.LocalDate" %>

举例:使用LocalDate.now()

当前日期:${LocalDate.now()}

36.编程导入包

还有一种导入包的方法,是在ServletContextlistener中以编程方法导入。

先导入POM

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>javax.servlet.jsp-api</artifactId>
  <version>2.3.3</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>javax.el</groupId>
  <artifactId>javax.el-api</artifactId>
  <version>3.0.0</version>
</dependency>
package jp.ldd.listener;

import javax.el.ELContextEvent;
import javax.el.ELContextListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.jsp.JspApplicationContext;
import javax.servlet.jsp.JspFactory;

/**
 * @author 小李同学
 * @pageage jp.ldd.listener
 * @date 2021/7/29 
 * @week 星期四
 * @action
 */
@WebListener
public class ELImportListener implements ServletContextListener {

    /**
     * @Author: 13513
     * @Date: 2021/7/29
     * @Param:  * @param event
     * @Return:编码导入java.time.LocalDate
     */
    public void contextInitialized(ServletContextEvent event) {
        JspFactory jspFactory = JspFactory.getDefaultFactory();
        JspApplicationContext jspApplicationContext = jspFactory.getJspApplicationContext(event.getServletContext());
        ELContextListener elContextListener = new ELContextListener() {
            public void contextCreated(ELContextEvent elContextEvent) {
                elContextEvent.getELContext().getImportHandler()
                .importPackage("java.time");
            }
        };
        jspApplicationContext.addELContextListener(elContextListener);
    }

    public void contextDestroyed(ServletContextEvent event) {
    }
}

37.创建Set、List和Map

动态创建Set
例如,如下表达式创建一个5个数字Set:
${{1,2,3,4,5}}

动态List
${["张三","李四","王五"]}

通过索引访问List
<span>访问</span>
<span>${["张三","李四","王五"][0]}</span>
<span>${["张三","李四","王五"][1]}</span>
<span>${["张三","李四","王五"][2]}</span>


动态创建Map
${{"China":"XiAn","France":"Paris"}}
通过key访问Map
<span>map</span>
<span>${{"China":"XiAn","France":"Paris"}["China"]}</span>

38.EL3.0Stream()流操作

EL3.0新特性通过调用流方法操作集合。

1.toList
toList方法返回一个List,它包含与当前流相同的成员。调用此方法的主要目的是轻松地打印或操作元素。

${[100,200,300].stream().toList()}

2.limit
limit方法限制流中元素的数量。

${[100,200,300,400].stream().limit(2).toList()}

如果传递给limit方法的参数大于元素的数量,则返回所有元素。

3.sort排序

<span>list-sort:${[300,100,200,400].stream().sorted().toList()}</span>

4.average

此方法返回流中所有元素的平均值,其返回值是一个Optional对象,它可能为null。需要调用get()获取实际
值。

此方法返回4.0
${[1,3,5,7].stream().average().get()}

5.sum
此方法返回流中所有元素的总和。例如,表达式返回16。

${[1,3,5,7].stream().sum()}

6.count
此方法返回流中元素的数量。例如,表达式返回4。
${[1,3,5,7].stream().count()}

7.min
此方法返回流的元素中的最小值。其返回值是一个Optional对象,它可能为null,需要调用get()获取实际
值。
8.max
...
9.map
此方法将流中的每个元素映射到另一个流中的另一个元素,并返回该流。此方法接受一个lambda表达式。
${[1,3,5].stream().map(x -> 2 * x).toList()}
返回列表如下:[2,6,10]

10.filter
此方法根据lambda表达式过滤流中的所有元素,并返回包含结果的新流。

11.forEach
此方法对流中的所有元素执行操作。它返回void。
${[1,3,4].stream().forEach(x -> System.out.println(x))}


39.禁用EL计算

JSP页面禁用EL表达式

第一种:将page指令的isELIgnored属性设为True。

<%@ page isELIgnored="true"%>

第二种:在部署描述符中使用jsp-property-group元素...
...
...

40.JSTL

JSTL是JSP标准标签库,是一个定制标签库的集合,用来解决像遍历Map或集合、条件测试、XML处理。

JSTL下载地址:http://jstl.java.net

其中,JSTL API和JSTL实现这两个软件是必须下载的。JSTL API中包含javax.servlet.jsp.jstl包,里面包
含了JSTL规范中定义的类型。JSTL实现中包含了实现类。这两个jar文件都必须复制到应用JSTL的每个应用程序
的WEB-INF/lib目录下。(Maven只需引入坐标即可)
JSTL是标准标签库,但它是通过多个标签库来暴露其行为的。JSTL1.2中的标签可以分成5类区域。
区域子函数URI前缀
核心

1.变量支持

2.流控制

3.URL管理

4.其他

http://java.sun.com/jsp/jstl/corec
XML

1.核心

2.流控制

3.转换

http://java.sun.com/jsp/jstl/xmlx
国际化

1.语言区域

2.消息格式化

3.数字和日期格式化

http://java.sun.com/jsp/jstl/fmtfmt
数据库1.SQLhttp://java.sun.com/jsp/jstl/sqlsql
函数

1.集合长度

2.字符串长度

http://java.sun.com/jsp/jstl/functionsfn

在JSP页面中使用JSTL库,必须通过以下格式使用taglib指令。

<%@ taglib uri="uri" prefix="prefix" %>

例如,要使用Core库,必须在JSP页面的开头处做如下声明:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

这个前缀可以是任意的。但是,采用惯例能使团队的其他开发人员以及后续加入该项目的其他人员更容易熟悉这
些代码。因此,建议使用预定义的前缀。

41.out标签

out标签是将结果输出到当前的JspWriter。
1.语法格式1

<c:out value="value" [escapeXml]="true|false" [default="defaultValue"] />

2.语法格式2

<c:out value="value" [escapeXml]="true|false" >
 default value
</c:out>

value:要计算的表达式
escapeXml:转义结果字符中特殊字符(&lt,&gt...)
default:默认值

注意:[]表示可选的属性。

在JSP2.0版本前,out标签是用于输出对象值最容易的方法。在JSP2.0及其更高的版本中,除非需要对某个值
进行XML转义,否则可以放心地使用EL表达式。

注意:out中default属性可以赋一个默认值,当赋予其value值的表达式返回null是,将会显示默认值。
如果default默认值返回null,out就会显示一个空字符串。
<c:out value="&lt要显示的数据对象(未使用转义字符)&gt" escapeXml="true" default="默认值"></c:out><br/>
输出:&lt要显示的数据对象(未使用转义字符)&gt
<c:out value="&lt要显示的数据对象(使用转义字符)&gt" escapeXml="false" default="默认值"></c:out><br/>
输出:<要显示的数据对象(使用转义字符)>
<c:out value="${null}" escapeXml="false">使用的表达式结果为null,则输出该默认值</c:out><br/>
输出:使用的表达式结果为null,则输出该默认值

42.应用程序上下文

使用${pageContext.request.contextPath}获取应用程序上下文。


<span>应用程序上下文:${pageContext.request.contextPath}</span>
<br/>
<span>相同的表达式将多次使用,所以我还创建了一个快捷方式cp</span>
<span>cp赋值放到HTML注释中,依然生效</span>
<!-- ${cp = pageContext.request.contextPath} -->
<br/>
<span>应用程序的上下文:${cp}</span>

43.set标签

set标签可以完成如下内容:

1.创建一个字符串和一个引用该字符串的有界变量。

语法如下:

<c:set value="value" var="varName" [scope="page|request|session|application"]/>
或者
<c:set var="varName" [scope="page|request|session|application"]>
  value
</c:set>

value:创建的字符串或者现存的有界对象。
varName:引用value的变量名称。
scope:有界变量的范围。
例如:下面set标签创建了字符串"this is my house",并将它赋值给homeName变量,变量homeName范围是
request
<c:set value="this is my house" var="homeName" scope="request"/>

2.设置有界对象的属性。
语法如下:
<c:set target="target" property="propertyName" value="value"/>
target属性定义有界对象以及有界对象的property属性。对该属性的赋值是通过value属性进行的。

例如:
将字符串"XiAn"赋予有界对象address的city属性。
<c:set target="${address}" property="city" value="XiAn"/>

44.remove标签

remove标签用于删除有界变量,语法如下:

<c:remove var="varName" [scope="page|request|session|application"] />

注意,有界变量引用的对象不能删除。因此,如果另一个有界对象也引用了同一个对象,仍然可以通过另一个
有界变量访问。

例如,删除homeName变量
<c:remove var="homeName" scope="request"/>

45.if标签


两种使用方式:

方式1:
<c:if test="true|false">
   显示内容
</c:if>

方式2:
判断的结果会保存在boolValue变量中,搭配EL表示式使用。
<c:if test="true|false" var="boolValue"/>
${(boolValue) ? "显示true":"显示false"}

46.choose、when和otherwise标签

choose和when标签的作用与Java中的关键字switch和case类似。otherwise标签则是用于显示默认的条件块。

<c:choose>
  <c:when test="true">
	  ....
  </c:when>
  <c:when test="true">
	  ....
  </c:when>
  <c:otherwise>
	  默认值
  </c:otherwise>
</c:choose>

47.forEach标签

forEach标签语法有两种形式。第一种形式是固定次数地重复body content。

<c:forEach [var="varName"] begin="begin" end="end" step="step">
 body content
</c:forEach>


例如,下列的forEach标签将显示"1,2,3,4,5"

<c:forEach var="x" begin="1" end="5">
  <c:out value="${x}"/>,
</c:forEach>

第二种形式用于遍历对象集合。

<c:forEach items="collection" [var="varName"] [varStatus="varStatusName"]
     [begin="begin"] [end="end"] [step="step"]>
      body content
</c:forEach>

例如,下面的forEach标签将遍历有界变量address的phones属性

<c:forEach var="phone" items="${address.phones}">
  ${phone}
</c:forEach>
属性类型描述
var字符串引用遍历的当前项目的有界变量名称
items支持的任意类型遍历的对象集合
varStatus字符串保存遍历状态的有界变量名称。类型值为javax.servlet.jsp.jstl.core.LoopTagStatus。LoopTagStatus接口带有count属性,它返回当前遍历的"次数"。第一次遍历时,status.count值为1;第二次遍历时,status.count值为2。可以通过status.count%2的余数,判断该标签正在处理的是偶数还是奇数编号。
begin整数遍历开始索引值。如果指定begin的值必须大于或者等于0
end整数遍历结束索引值。
step整数遍历间隔值。如果指定step的值必须大于或者等于1

48.forTokens标签 

forTokens标签用于遍历以特定分隔符隔开的令牌,语法如下:

<c:forTokens items="stringOfTokens" delims="delimiters" 
   [var="varName"] [varStatus="varStatusName"] [begin="begin"] [end="end"] [step="step"]

>
  body content
</c:forTokens>

delims:字符串,一组分隔符

例如:

<c:forTokens var="item" items="A,B,C" delims=",">
   <c:out value="${item}" /><br/>
</c:forTokens>

当以上forTokens执行产生如下结果:
A
B
C

49.函数

JSTL1.1和JSTL1.2还定义了一套可以在EL表达式中使用的标准函数。这些函数都集中放在function标签库
中。要使用这些函数,必须在JSP的最前面使用以下的taglib指令:

<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

调用函数时,要以下列格式使用一个EL:

${fn:functionName}

functionName是函数名。


1.contains函数

contains函数用于测试一个字符串中是否包含指定的子字符串。如果字符串中包含该字符串,则返回值为True
,否则返回False。

语法如下:contains(String,substring)

${fn:contains("Hello World","Hello")}

2.containsIgnoreCase函数
与contains相似,只不过不区分大小写。
...
3.endWith函数
测试一个字符串是否以指定的后缀结尾。其返回值是一个Boolean。
...
4.escapeXml函数
escapeXml函数用于给String编码。这种转换与out标签将其escapeXml属性设置True一样。
语法如下:
escapeXml(String)
...

50.语言区域

Java.util.Local类表示一个语言区域。一个Locale对象包含3个主要元件:language、country和variant。

language语言码区分。
country国家码区分。
variant是一个特定于供应商或者特定于浏览器的代号。例如,用WIN表示Windows,用MAC表示Macintosh,用
POSIX表示POSIX。

public Locale(String language)
public Locale(String language, String country)
public Locale(String language, String country, String variant)

ISO 639 语言码范例
代码语言
en英语
ja日语
zh汉语

ISO 3166 国家码范例
国家代码
中国CN
美国US
英国GB
例如,要构造一个表示美国所用的英语的Locale对象,可以像下面这样编写:
Locale locale = new Locale("en","US");

51.SpringMvc国际化

1.国际化的应用程序是将每一个语言区域的文本元素都单独保存在一个独立的属性文件中。每个文件中都包含
key/value对,并且每个key都唯一表示一个特定语言区域的对象。

在SpringMvc中,利用messageSource告诉SpringMvc要将属性文件保存在哪里。

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource" >
   <property name="basename" value="/WEB-INF/i18n/messages"/>
	<property name="defaultEncoding" value="UTF-8"/>
</bean>

上面的bean定义中用ReloadableResourceBundleMessageSource类作为实现。另一个实现是
ResourceBundleMessageSource,它是不能重新加载的。这就意味着,如果在任意属性文件中修改了某一个
属性key或者value,并且正在使用ResourceBundleMessageSource,那么要使修改生效,就必须先重启JVM。

另一个区别是,使用ReloadableResourceBundleMessageSource时,是在应用程序目录下搜索这些属性文件
。而使用ResourceBundleMessageSource时,属性文件则必须放到类路径下,即WEB-INF/class目录下。
2.配置SpringMvc使用哪个语言区域


读取用户浏览器的accept-language标题值。accept-language标题提供了关于用户偏好哪种语言的信息。

在SpringMvc中选择语言区域,可以使用语言区域解析器bean。它有几个实现:


AcceptHeaderLocaleResolver
SessionLocaleResolver
CookieLocaleResolver

所有这些实现都是org.springframework.web.servlet.i18n包的组成部分。

<!-- 使用AcceptHeaderLocaleResolver语言区域解析器-->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"/>

如果选择使用AcceptHeaderLocaleResolver语言区域解析器,SpringMvc将会读取浏览器的accept-language
标题,来确定浏览器要接受哪个语言区域。如果浏览器的某个语言区域与SpringMvc应用程序支持的某个语言区
域匹配,就会使用这个语言区域。如果没有找到匹配的语言区域,则使用默认的语言区域。
3.使用message标签
在SpringMvc中显示本地化消息最容易的是使用Spring的message标签。为了使用这个标签,要在使用该标签
的JSP页面声明如下taglib指令。

<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>

<span>locale:${pageContext.response.locale}</span>
<br/>
<span>accept-language header:${header["accept-language"]}</span>
<br/>
<span>标题:<spring:message code="title"/></span>
message标签的属性
属性描述
arguments

该参数写成一个字符串、一个对象数组或者单个对象

argumentSeparator用来分隔该标签参数的字符
code获取消息的key
htmlEscape接受True或者False,表示被渲染文件是否应该进行HTML转义
javaScriptEscape接受True或者False,表示被渲染文件是否应该进行JavaScript转义
messageMessageSourceResolvable参数
scope保存var属性中定义的变量范围
text如果code属性不存在,或者指定码无法获取信息,所显示的默认文件
var用于保存消息的有界变量

 

 

 

52.上传文件

SpringMvc中处理文件上传有两种方式:

1.使用Apache Commons FileUpload组件

2.利用Servlet3.0及其更高版本的内置支持。

 53.利用Servlet3.0及其更高版本的内置支持

客户端编码:

<form action="action" enctype="multipart/form-data" method="post">
   选择上传文件:<input type="file" name="fileName"/>
   <input type="submit" value="上传文件"/>
</form>

HTML5通过在input元素中引入multiple属性,进行多个文件上传选择。编写一下任意一行代码,都可行:

<input type="file" name="fileName" multiple />
<input type="file" name="fileName" multiple="multiple" />
<input type="file" name="fileName" multiple="" />
MultipartFile接口

上传SpringMVC应用程序中的文件会被包在一个MultipartFile对象中。

org.springframework.web.multipart.MultipartFile接口具有以下方法:

它以字节数组的形式返回文件的内容。
byte[] getBytes()

它返回文件的内容类型。
String getContentType()

它返回一个InputStream 从中读取文件的内容。
InputStream getInputStream()

它以多部分的形式返回参数的名称
String getName()

它返回客户端本地驱动器中的初始文件名
String getOriginalFilename()

它以字节为单位,返回文件的大小
long getSize()

它表示被上传的文件是否为空
boolean isEmpty()

它将上传的文件保存到目标目录下
void transferTo(File destination)
用Servlet3及其更高版本上传文件,是围绕着注解类型MultipartConfig和javax.servlet.http.Part
接口进行的。

MultipartConfig属性:
location:表示在Part调用write方法时,要将已上传的文件保存到磁盘中的位置。
maxFileSize:上传文件的最大容量,默认值是-1,表示没有限制。大于指定值的文件将会遭到拒绝。
maxRequestSize:表示多部分HTTP请求允许的最大容量,默认值为-1,表示没有限制。
fileSizeThreshold:上传文件超出这个容量界限时,会被写入磁盘。


1.在SpringMvc配置文件中配置多部件分解器。

<!--配置多部件分解器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"></bean>

2.配置部署符(web.xml)
注意Servlet版本,此处用的是Servlet4.0

<?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">
	<servlet>
		...
		...	
		<multipart-config>
		  <max-file-size>20848820</max-file-size>
		  <max-request-size>418018841</max-request-size>
		  <file-size-threshold>1048576</file-size-threshold>
		</multipart-config>
	</servlet>

</web-app>
@Controller
@RequestMapping("/file")
public class UploadController {

    @PostMapping("/saveFile")
    public void saveFile(MultipartFile multipartFile){
        String filename = multipartFile.getOriginalFilename();
        try{
            File file = new File("C:\\2020-10-07-ideaProject\\spring_mvc_0727\\src\\main\\webapp\\WEB-INF\\images\\",filename);
            multipartFile.transferTo(file);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

//上传完成...

 54.使用Apache Commons FileUpload组件

对于版本低于Servlet3.0容器,则需要Apache Commons FileUpload组件。
...
...
...

55.下载文件

像图片或者HTML文件这样的静态资源,在浏览器中打开正确的URL即可下载。只要该资源是放在应用程序的目录
下,或者放在应用程序目录的子目录下,而不是放在WEB-INF下,Servlet/JSP容器就会将该资源发送到浏览器。
但是有些文件可能受到权限限制或者其他原因无法直接访问,可以通过编程把资源发送到浏览器。

56.下载文件案例

将文件资源发送到浏览器,需要在控制器中完成以下工作:

1.对请求处理方法使用void返回类型,并在方法中添加HttpServletResponse参数
2.将响应的内容类型设为文件的内容类型,设置Content-Type。在此网址http://www.iana.org/assignments/media-types/media-types.xhtml查看Content-Type所有类型。
3.添加一个名为Content-Disposition的HTTP响应标题,并赋值attachment;filename=fileName,这里的
fileName是默认下载文件名。



@GetMapping("/download/resource")
public void downloadResource(HttpServletResponse response,HttpServletRequest request) throws IOException{
	File file = new File("C:\\2020-10-07-ideaProject\\spring_mvc_0727\\src\\main\\webapp\\WEB-INF\\images\\20200827134532.jpg");
	FileInputStream fis = new FileInputStream(file);
	BufferedInputStream bis = new BufferedInputStream(fis);
	byte[] bytes = new byte[bis.available()];
	//将图片文件读取到bytes数组
	fis.read(bytes);
	response.setContentType("image/jpeg");
	response.addHeader("Content-Disposition","attachment;filename=download20210804.jpg");
	ServletOutputStream os = response.getOutputStream();
	//将图片内容输出到浏览器
	os.write(bytes);
}


//将文件发送到HTTP客户端的更好方法是使用Java NIO的Files.copy()方法,代码更短,运行速度更快。
@GetMapping("/download/nio/resource")
public void downloadNioResource(HttpServletResponse response,HttpServletRequest request) throws Exception{
	Path path = Paths.get("C:\\2020-10-07-ideaProject\\spring_mvc_0727\\src\\main\\webapp\\WEB-INF\\images\\", "20200827134532.jpg");
	if(Files.exists(path)){
		response.setContentType("image/jpeg");
		response.addHeader("Content-Disposition","attachment;filename=new20210804.jpg");
		//将图片内容输出到浏览器
		Files.copy(path,response.getOutputStream());
	}
}

57.Servlet API概览

Servlet API有以下4个Java包:

javax.servlet 其中包含定义Servlet和Servlet容器之间契约的类和接口。

javax.servlet.http 其中包含定义HTTP Servlet 和 Servlet容器之间契约的类和接口。

javax.servlet.annotation 其中包含用于Servlet、filter、listener的注解。

javax.servlet.descriptor 其中包含提供程序化登录Web应用程序配置信息的类型。
...
...

58.JSP概述

JSP页面本质上是一个Servlet。JSP页面在JSP容器中运行,一个Servlet容器通常也是JSP容器。例如,Tomcat
就是一个Servlet/JSP容器。 

当一个JSP页面第一次被请求时,Servlet/JSP容器主要做以下两件事情:

1.把JSP页面转换到JSP页面实现类,该实现类是一个实现javax.servlet.jsp.JspPage接口或子接口javax.
servlet.jsp.HttpJspPage的Java类。 JspPage接口是javax.servlet.Servlet的子接口,这使得每一个JSP
页面都是一个Servlet。该实现类的类名由Servlet/JSP容器生成。如果出现转换错误,则相关错误信息将被
发送到客户端。


2.如果转换成功,Servlet/JSP容器随后编译该Servlet类,并装载和实例化该类,像其他正常的Servlet一样
执行声明周期。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值