Skip to content

SpringMVC

SpringMVC概述

为了更好地在web工程中,使用Spring框架

SpringMVC 入门

快速入门

导入依赖

  • pom.xml
xml
<dependency>
  <groupId>jakarta.servlet</groupId>
  <artifactId>jakarta.servlet-api</artifactId>
  <version>6.0.0</version>
  <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.7</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.7</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.7</version>
</dependency>

配置Spring容器

  • application-context.xml
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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.futureweaver"/>

</beans>

配置前置控制器

  • web.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicaiton-context.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>

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

</web-app>

编写Controller

  • UserController
java
@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/login")
    public String login() {
        // 需要在WEB-INF的pages目录中,添加一个index.jsp
        System.out.println("用户访问了登录");
        return "/index.jsp";
    }
}

配置视图解析器

  • springmvc.xml
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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.futureweaver"/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>

</beans>

SpringMVC执行流程分析【重点】

RequestLifecycle

  • HandlerMapping

    映射处理器,找到Controller

  • HandlerAdapter

    适配处理器,调用Controller、传参

  • ViewResolver

    视图解析器,处理Controller的返回值。加前缀、加后缀

RequestMapping注解参数

value

给HandlerMapping来用的映射地址

path

给HandlerMapping来用的映射地址

method

请求方式,RequestMethod.GET、RequestMethod.POST...

params

能够指请求参数,在Java代码就可以直接接到这些参数了,不再需要request.getParameter...

可以不写

参数绑定

基本类型与String类型

java
@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping(value = "/login")
    public String login(String username, String password) {
        System.out.println("用户访问了登录");
        return "index";
    }

    @RequestMapping(value = "/delete")
    public String delete(int id) {
        return "index";
    }
}

POJO类型

POJO

Plain Ordinary Java Object

  • User.java
java
public class User {
    private String id;
    private String username;
    private String password;
    private String gender;
    private String mobile;
    private String email;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}
  • UserController.java
java
@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping(
            value = "/login",
            method = {RequestMethod.POST, RequestMethod.GET},
            params = {"username", "password"})
    public String login(String username, String password) {
        System.out.println("用户访问了登录");
        return "index";
    }

    @RequestMapping(value = "/delete")
    public String delete(int id) {
        return "index";
    }

    @RequestMapping("/register")
    public String register(User user) {
        return "index";
    }
}

内置转换器

参考 spring-core 依赖

org.springframework.core.convert.support包内的,都是 Spring 内置的转换器。一部分的转换器描述了原始类型和目标类型,通过范型描述

自定义类型转换器

实现接口
java
public class StringToDateConverter implements Converter<String, Date> {
    // 将String类型转换成Date类型
    @Override
    public Date convert(String s) {
        // format: 将Date转换成String
        // parse: 将String转换成Date
        Date result = null;
        try {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            result = format.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        } finally {
            return result;
        }
    }
}
配置转换器

需要将自己编写的转换器,放到Spring容器当中

java
<bean id="converterService"
      class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <array>
            <bean class="com.futureweaver.web.converter.StringToDateConverter"></bean>
        </array>
    </property>
</bean>
引用自定义转换器
java
<mvc:annotation-driven conversion-service="converterService"/>

编码过滤器【掌握】

xml
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
  <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

常用注解【重点】

RequestParam

描述参数名、默认值、是否必填等等...

java
@RequestMapping("/query")
public String query(
  @RequestParam(defaultValue = "1") int currentPage,
  @RequestParam(defaultValue = "10") int pageSize) {
    return "/index.jsp";
}

PathVariable

java
// 当前端访问时,uri为: /user/delete/10时,如何把id=10接收到
@RequestMapping("/delete/{id}")
public void delete(
  @PathVariable("id") String id) {
}

RequestHeader

java
@RequestMapping("/request_header")
public String requestHeader(
        /*HttpServletRequest request*/
        @RequestHeader("User-Agent") String userAgent) {
    return "/index.jsp";
}

CookieValue

@CookieValue使用方法同@RequestParam

注意@CookieValue("JSESSIONID")必须大写

java
@RequestMapping("/cookie_value")
public String cookieValue(
        /*HttpServletRequest request*/
        @CookieValue("JSESSIONID") String jsessionid) {
    return "/index.jsp";
}

SessionAttribute

java
@RequestMapping("/session_attribute")
public String sessionAttribute(
        /*HttpServletRequest request*/
        /*HttpSession session*/
        @SessionAttribute(name = "username", required = false) String username) {
    return "/index.jsp";
}

Model

Model参数

java
@RequestMapping("/model_sample")
public String modelSample(Model model) {
    model.addAttribute("gender", "male");

    // 默认情况下是转发
    return "/index.jsp";
}

ModelMap参数

java
@RequestMapping("/model_map_sample")
public String modelMapSample(ModelMap model) {
    model.put("gender", "male");
    return "/index.jsp";
}

ModelAttribute注解

java
@Controller
public class ModelAttributeController {
    // 在编写RequestMapping函数时
    // 真正地产生这个函数的调用之前,就应该向Model模型当中,注入一部分内容

    @ModelAttribute
    public void m1(Model model) {
        model.addAttribute("name1", "value1");
    }

    @ModelAttribute("name2")
    public String m2() {
        return "value2";
    }

    @RequestMapping("/model_attribute1")
    public String modelAttribute1(Model model) {
        System.out.println(model.getAttribute("name1"));
        System.out.println(model.getAttribute("name2"));

        return "/index.jsp";
    }

    @RequestMapping("/model_attribute2")
    public String modelAttribute2(@ModelAttribute("name1") String a,
                                  @ModelAttribute("name2") String b) {
        System.out.println(a);
        System.out.println(b);
        return "/index.jsp";
    }
}

SessionAttributes注解

java
@Controller
@SessionAttributes("mobile")
// 所有在本类中,对model做键为mobile的操作,都在session中进行
public class SessionController {
    @RequestMapping("/session_attr_send")
    public String sessionSend(Model model) {
        // 期望把model放到会话域当中
        model.addAttribute("mobile", "134-1234-5678");
        return "/index.jsp";
    }

    @RequestMapping("/session_attr_receive")
    public String sessionReceive(
            @SessionAttribute(name = "mobile", required = false) String mobile) {
        return "/index.jsp";
    }
}

页面跳转

转发

java
@RequestMapping("/forward1")
public String forward1() {
    return "/index.jsp";
}

@RequestMapping("/forward2")
public String forward2() {
    return "forward:/index.jsp";
}

重定向

java
@RequestMapping("/redirect")
public String redirect() {
    return "redirect:/index.jsp";
}

SpringMVC深入

响应数据和结果视图

返回值分类【了解】

字符串

给视图解析器识别的视图名字(jsp、html...)

void
java
// 操作响应体
@RequestMapping("/void1")
public void voidMethod1(HttpServletResponse response) throws IOException {
    response.getWriter().write("<h1>Hello</h1>");
}

// 如果不响应页面,就需要响应状态。
@RequestMapping("/void2")
@ResponseStatus(HttpStatus.OK)
public void voidMethod2() {
}
ModelAndView
java
@RequestMapping("/modeAndView")
public ModelAndView modelAndView() {
    ModelAndView modelAndView = new ModelAndView();

    // 当做model使用
    modelAndView.getModel().put("email", "future-weaver@future-weaver.com");

    // 当做视图使用
    // 可以进行渲染
    modelAndView.getView();

    // 设置视图名
    modelAndView.setViewName("forward:/index.jsp");

    // 增加数据
    modelAndView.addObject("age",18);

    return modelAndView;
}

响应ResponseBody

字符串类型
java
@RequestMapping("/res_body_str")
@ResponseBody
// 用户在浏览器看到的响应体就是返回的字符串,不再让视图解析器找视图
public String resBodyStr() {
    return "abcdefg";
}
对象类型
  1. 导入依赖

    xml
    <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-databind</artifactId>
       <version>2.11.3</version>
    </dependency>
  2. 添加配置

    xml
    <mvc:annotation-driven/>
  3. 编写 Controller

    java
    @RequestMapping("/res_body_obj")
    @ResponseBody
    public Map<String, Object> resBodyObj() {
       Map<String, Object> map = new HashMap<>();
       map.put("username", "zhangsan");
       map.put("gender", "male");
    
       return map;
    }

请求RequestBody

java
@RequestMapping("/request_body")
// 当客户端发来的请求体是json格式时,可以利用RequestBody注解,协助转换
public String requestBody(@RequestBody User user) {
    return "/index.jsp";
}

静态资源问题

默认情况下,DispatcherServlet会截获所有的请求,并在所有的已注册的Controller中检索。

会出现404 - 未找到错误

解决方案1【重点】

xml配置方式
xml
<!-- 
  额外的增加一个handler,且其requestMapping:  "/**" 可以匹配所有请求,但是优先级最低
  所以如果其他所有的handler都匹配不上,请求会转向 "/**" ,恰好,这个handler就是处理静态资源的
  处理方式:将请求转会到tomcat中名为default的Servlet
  -->
<mvc:default-servlet-handler/>
注解配置方式
java
@Configuration
@EnableWebMvc
public class ResourceConfig implements WebMvcConfigurer {
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

解决方案2【了解】

xml配置方式
  • mapping是访问路径,location是静态资源存放的路径
  • 将/html/ **中 /**匹配到的内容,拼接到 /hhh/后 http://..../html/a.html 访问 /hhh/a.html
xml
<mvc:resources mapping="/html/**" location="/hhh/"/>

解决方案3【了解】

web.xml配置

所有为url-pattern使用后缀名匹配

xml
<servlet>
      <servlet-name>DispatcherServlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

组合注解

  • RestController
  • GetMapping
  • PostMapping
  • DeleteMapping
  • PutMapping

文件上传

导入依赖

xml
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
    <exclusions>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

配置解析器

xml
<!-- 上传解析器 
    id: 必须是"multipartResolver"
    maxUploadSize:最大可上传的文件大小,单位:byte 超出后会抛出MaxUploadSizeExceededException异常,可以异常解析器捕获
    defaultEncoding: 用来设置上传文件名称的参数,如果不设置的话,图片名称如果有中文,保存时会乱码
 -->
<bean id="multipartResolver" 
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
      p:maxUploadSize="1048576"
      p:defaultEncoding="utf-8"/>

编写Controller

java
/**
 * 文件上传
 * 需要通过MultipartFile类型来接收上传数据
 *
 * @throws Exception
 * @throws IllegalStateException
 */
@RequestMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) throws Exception {
    if (file!=null) {
        //将文件存储到指定路径
        file.transferTo(new File("d:/upload/" + file.getOriginalFilename()));
    }
    return "/index.jsp";
}

validation-api【扩展】

导入依赖

xml
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.0.Final</version>
</dependency>

常用注解

  • @Email

    The string has to be a well-formed email address. Exact semantics of what makes up a valid email address are left to Jakarta Bean Validation providers. Accepts CharSequence.

  • @NotBlank

    The annotated element must not be null and must contain at least one non-whitespace character.

  • @NotEmpty

    The annotated element must not be null nor empty.

  • @NotNull

    The annotated element must not be null.

  • @Pattern

    The annotated CharSequence must match the specified regular expression.

  • @Valid

    Marks a property, method parameter or method return type for validation cascading.

validation-api使用

只在@RequestBody,即json格式的参数时才生效

  • User.java
java
public class User {
    @NotEmpty
    @Pattern(regexp = "[a-zA-Z]{6,16}")
    private String username;

    @NotEmpty
    @Email
    private String email;

    // ...
}
  • UserController.java
java
@RestController
@RequestMapping("/user")
public class UserController {

    // 接收的数据是json时,才能使用validation-api
    @RequestMapping("/jsonregister")
    public String register(@Valid @RequestBody User user) {
        return "jsonregister";
    }
}

异常处理(前后端不分离模式)

自定义异常解析器

  • ExceptionHandler.java
java
@Component
public class ExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView result = new ModelAndView();

        result.getModelMap().put("ex", ex);
        result.setViewName("/500.jsp");

        return result;
    }
}

自定义异常视图

  • 500.jsp
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<h1>${ex.localizedMessage}</h1>

</body>
</html>

异常处理

  • ResponseResult.java
java
public class ResponseResult<T> {
    int code = 200;
    String message = "successful";
    T data;

    // TODO: 生成 toString
    // TODO: 生成 getter & setter
}
  • ExceptionInterceptor.java
java
@ControllerAdvice
public class ExceptionInterceptor {
import java.nio.file.FileAlreadyExistsException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 异常拦截器
 * 
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 系统异常
     * 
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseResult<String> systemExceptionHandler(HttpServletRequest req, Exception ex) {
        // 处理日志
        logger.error(req.getQueryString());
        logger.error(req.getRequestURI());
        logger.error("SystemException", ex);

        // 处理响应
        ResponseResult<Exception> result = new ResponseResult<>();
        result.setCode(1500);
        result.setMessage("system exception");
        result.setData(ex);
        return result;
    }
}

增强器

java
import java.lang.reflect.Field;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 作为ResponseResult的填充器:
 * - [正确值]当RESTful方法返回void时,填充响应JSON
 * - [正确值]当RESTful方法返回的不是void时,填充TO对象的responseResult属性
 * - [错误值]若处理错误响应,则使用异常拦截器填充
 *
 */
@ControllerAdvice
public class ResponseResultAssembler implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        if (returnType.getMethod().getReturnType().equals(void.class)) {
            return true;
        }

        Field[] fields = returnType.getMethod().getReturnType().getDeclaredFields();
        for (Field fieldItem : fields) {
            if (fieldItem.getType().equals(ResponseResult.class)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public Object beforeBodyWrite(
            Object body, 
            MethodParameter returnType, 
            MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType, 
            ServerHttpRequest request,
            ServerHttpResponse response) {

        if (returnType.getMethod().getReturnType().equals(void.class)) {
            ResponseResult<String> responseResult = new ResponseResult<>();
            return responseResult;
        }

        Field[] fields = returnType.getMethod().getReturnType().getDeclaredFields();
        for (Field fieldItem : fields) {
            if (fieldItem.getType().equals(ResponseResult.class)) {
                fieldItem.setAccessible(true);
                ResponseResult<?> responseResult = (ResponseResult<?>) ReflectionUtils.getField(fieldItem, body);
                if (responseResult == null) {
                    responseResult = new ResponseResult<>();
                    ReflectionUtils.setField(fieldItem, body, responseResult);
                }
            }
        }

        return body;
    }
}

拦截器

  • HandlerInterceptor
  • ClientHttpRequestInterceptor
  • RequestInterceptor

自定义拦截器

java
public class ProcessInterceptor implements HandlerInterceptor {

    /**
     * 在Handler方法执行之前执行
     * 返回值,返回true拦截器放行 false拦截器不通过,后续业务逻辑不再执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception {
        System.out.println("ProcessInterceptor,预处理回调方法正在执行");
        return true;
    }

    /**
     * 在执行完Handler方法之后执行
     */
    @Override
    public void postHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("ProcessInterceptor,后处理回调方法正在执行");
    }

    /**
     * 在视图渲染完成之后执行
     */
    @Override
    public void afterCompletion(HttpServletRequest request, 
                                HttpServletResponse response, 
                                Object handler, 
                                Exception ex)
            throws Exception {
        System.out.println("ProcessInterceptor,请求完成回调方法正在执行");
    }
}

注册自定义拦截器

xml
<!--配置拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 白名单 -->
        <mvc:mapping path="/**"/>

        <!--黑名单-->
        <mvc:exclude-mapping path="/inter/a/**"/>

        <!-- 拦截器 -->
        <bean class="com.futureweaver.interceptor.ProcessInterceptor"></bean>
    </mvc:interceptor>
    <!--
    <mvc:intercepter>
        ...
    </mvc:intercepter>
    -->
</mvc:interceptors>

jackson注解

JsonFormat

java
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")

JsonProperty

java
@JsonProperty("new_name")

JsonIgnore

java
@JsonIgnore

JsonInclude

java
// 若"name==null" 忽略此属性
@JsonInclude(JsonInclude.Include.NON_NULL)

// 若hobby长度为0或==null 忽略此属性
@JsonInclude(value= JsonInclude.Include.NON_EMPTY)

JsonSerialize

java
@JsonSerialize(using = DoubleSerializer.class)
java
public class DoubleSerializer extends JsonSerializer<Double> {

    // value即 Double salary的值
    @Override 
    public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        // 将Double salary的值 四舍五入
        String number = BigDecimal.valueOf(value).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
        // 输出 四舍五入后的值
        gen.writeNumber(number);
    }
}

跨域访问

  • 方案 1
java
@CrossOrigin
  • 方案 2
xml
<mvc:cors>
   <mvc:mapping path="/**"
             allowed-origins="http://127.0.0.1:8020,http://127.0.0.1:8081"
             allowed-methods="GET,POST,PUT,DELETE,OPTIONS"
             allowed-headers="Content-Type,token"
             allow-credentials="true"
             max-age="123" />
</mvc:cors>
  • 方案 3
java
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig{
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOriginPattern("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setAllowCredentials(true);
        config.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        return new FilterRegistrationBean<>(new CorsFilter(source));
    }
}

Idea模板

  1. 进入Idea欢迎页面(如果已开启某个项目,点击菜单栏-File-Close Project)
  2. 左侧导航栏,选择Customize,选择All Settings...
  3. 左侧导航栏,选择Editor->File And Code Templates

pom.xml

  1. 右侧视图,选择Other选项卡
  2. 右侧窗口中的左侧导航栏,选择Maven->Maven Project.xml
  3. 下拉到底部,将以下文本,CV到</project>上侧
xml
    <dependencies>
        <!-- jakarta.servlet-api -->
        <!-- jakarta.servlet.jsp.jstl-api -->
        <!-- jakarta.servlet.jsp.jstl -->

        <!-- druid -->
        <!-- mysql-connector-j -->
        <!-- mybatis -->
        <!-- pagehelper -->

        <!-- spring-context -->
        <!-- spring-aspects -->
        <!-- spring-jdbc -->
        <!-- mybatis-spring -->
        <!-- log4j -->

        <!-- spring-web -->
        <!-- spring-webmvc -->
        <!-- jackson-databind -->
        <!-- hibernate-validator -->
        <!-- commons-io -->
        <!-- !commons-fileupload! -->

        <!-- spring-test -->
        <!-- junit -->
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>*.xml</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>

            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>*</include>
                    <include>**/*</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

web.xml

  1. 右侧视图,选择Files选项卡
  2. 点击+
  3. Name -> web-xml
  4. Extention -> xml
  5. File Name -> web.xml
  6. 将以下内容,粘贴到模板内部
xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application-context.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>

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

    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
      <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

application-context.xml

  1. 右侧视图,选择Files选项卡
  2. 点击+
  3. Name -> application-context-xml
  4. Extention -> xml
  5. File Name -> application-context.xml
  6. 将以下内容,粘贴到模板内部
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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/util
        https://www.springframework.org/schema/util/spring-util.xsd">

    <context:component-scan base-package="${base_package}"/>

    <util:properties id="druidConfiguration" location="classpath:druid.properties"/>
    <bean id="dataSource"
          class="com.alibaba.druid.pool.DruidDataSourceFactory"
          factory-method="createDataSource"
          c:properties-ref="druidConfiguration"/>

    <bean id="mybatis-config"
          class="org.springframework.core.io.ClassPathResource"
          c:path="mybatis-config.xml"/>

    <bean id="sqlSessionFactory"
          class="org.mybatis.spring.SqlSessionFactoryBean"
          p:configLocation-ref="mybatis-config"
          p:dataSource-ref="dataSource"/>

    <bean id="mapperScannerConfigurer"
          class="org.mybatis.spring.mapper.MapperScannerConfigurer"
          p:basePackage="${mapper_package}"/>

    <bean id="tx"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>

    <aop:aspectj-autoproxy/>
    <mvc:annotation-driven/>
    <mvc:default-servlet-handler/>
</beans>

mybatis-config.xml

注意

因为mybatis不再使用默认连接池,而是利用spring,将mybatis与druid整合。所以需要将mybatis-config.xml模板中与连接池相关的配置删除。

又因为mybatis的mapper扫描,已经委托给了spring,所以需要将mybatis-config.xml模板中与mapper扫描相关的配置删除

xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="autoMappingBehavior" value="FULL"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="cacheEnabled" value="false"/>
        <setting name="localCacheScope" value="STATEMENT"/>
    </settings>

    <typeAliases>
        <package name="${type_aliases_package}"/>
    </typeAliases>

    <!--
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
    </plugins>
    -->
</configuration>

课程目标总结

  • 能够描述 SpringMVC 框架
  • 能够实现 SpringMVC 的环境搭建
  • 能够描述 SpringMVC 的执行流程
  • 能够描述 SpringMVC 中的常用组件
  • 掌握 SpringMVC 的常用注解
  • 掌握 SpringMVC 的参数绑定
  • 掌握 SpringMVC 的返回值绑定
  • 掌握 SpringMVC 中的转发和重定向使用
  • 掌握 SpringMVC 与 json 交互
  • 掌握 SpringMVC 中的图片上传
  • 了解 SpringMVC 中的拦截器
  • 掌握 SSM 整合(SpringMVC + Spring + MyBatis)