💠

💠 2024-03-30 11:43:28


SpringMVC

Spring MVC 4.2.4.RELEASE 中文文档

springmvc + mybatis

MVC思想

参考博客 图

原理

统一使用一个Servlet 进行请求的收发, 通过配置的URL对应的方法, 进行调用, 然后返回视图解析器进行渲染

  • 核心类是DispatchServlet 由它来接收各种请求
    • 实现路由转发
    • 全局异常处理
  • 发出request请求,到controller解析器,得到Model和view等的名字
  • 发送到controller执行,返回view名字
  • 发送到视图解析器
  • 执行视图返回到dispatchServlet

API

简洁的API设计


传统项目配置完整流程

也就是Maven的Web结构,甚至是Eclipse那样的DynamicWeb项目结构, 参考 博客

配置依赖

Maven

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    <properties>
        <spring.version>4.3.9.RELEASE</spring.version>
    </properties>
    ......
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!-- 如果使用JSP作为视图层,还需 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>

Gradle

1
2
    compile('org.springframework:spring-web:4.3.9.RELEASE')
    compile('org.springframework:spring-webmvc:4.3.9.RELEASE')

web.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
  <servlet>
    <servlet-name>mysql</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet-mapping>
    <servlet-name>mysql</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

ApplicationContext.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    <?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-3.2.xsd
                    http://www.sprinControllergframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context-3.2.xsd
                http://www.springframework.org/schema/mvc
                http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--启用spring的一些annotation -->
    <context:annotation-config/>
    <!-- 自动扫描该包,使SpringMVC认为包下用了@controller注解的类是控制器 -->
    <context:component-scan base-package="com.test.controller">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!--HandlerMapping 无需配置,springmvc可以默认启动-->
    <!--静态资源映射-->
    <!--本项目把静态资源放在了WEB-INF的statics目录下,资源映射如下-->
    <!--<mvc:resources mapping="/css/**" location="/WEB-INF/statics/css/"/>-->
    <!--<mvc:resources mapping="/js/**" location="/WEB-INF/statics/js/"/>-->
    <!--<mvc:resources mapping="/image/**" location="/WEB-INF/statics/image/"/>-->
    <!--但是项目部署到linux下发现WEB-INF的静态资源会出现无法解析的情况,但是本地tomcat访问正常,因此建议还是直接把静态资源放在webapp的statics下,映射配置如下-->
    <!--<mvc:resources mapping="/css/**" location="/statics/css/"/>-->
    <!--<mvc:resources mapping="/js/**" location="/statics/js/"/>-->
    <!--<mvc:resources mapping="/image/**" location="/statics/images/"/>-->
    <!-- 配置注解驱动 可以将request参数与绑定到controller参数上 -->
    <mvc:annotation-driven/>
    <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀(如果最后一个还是表示文件夹,则最后的斜杠不要漏了) 使用JSP-->
    <!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- -->
    <!--<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
        <!--<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>-->
        <!--<property name="prefix" value="/WEB-INF/views/"/>&lt;!&ndash;设置JSP文件的目录位置&ndash;&gt;-->
        <!--<property name="suffix" value=".jsp"/>-->
    <!--</bean>-->
    <!-- springmvc文件上传需要配置的节点-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="20971500"/>
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="resolveLazily" value="true"/>
    </bean>
    </beans>

全局异常处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        Map<String, Object> model = new HashMap<>();
        model.put("ex", ex);
        ex.printStackTrace();//打印异常信息
        // 根据不同错误转向不同页面
        if (ex instanceof CSRFException) {//受到csrf攻击
           return new ModelAndView("/errorPage/error", model);
        }
        if (ex instanceof BusinessException) {//业务逻辑处理出错
            return new ModelAndView("errorPage/businessError", model);
        } else if (ex instanceof ParameterException) {//参数处理出错。
            return new ModelAndView("errorPage/parameterError", model);
        } else {  //其他数据类型错误
            return new ModelAndView("errorPage/error", model);
        }
        return new ModelAndView("error", model);
    }
}

// 或者是在 Controller 层直接处理
    @ControllerAdvice
    public class ExceptionHandle{
    @EXceptionHandler({Exception.class})
    public ModelAndView dealException(Exception e){
        ModelAndView view = new ModelAndView("exception";
        Exception e = new Exception("错误信息");
        view.addObject("",e.getMessage());
        return view;
    }

但如果是前后端分离的话, 就只能统一处理异常然后然后对应的错误码和提示信息了 参考博客 ResponseBody方案

自定义错误页面

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    // 自定义错误页面 需要放在静态资源下面
    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer() {
        return (container -> {
            ErrorPage error401Page = new ErrorPage(HttpStatus.FORBIDDEN, "/500.html");
            ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");
            ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html");
            container.addErrorPages(error401Page, error404Page, error500Page);
        });
    }

中文编码问题

参考博客 但是奇怪的是某些方法用第二种正常,有些还是要用第一种

  1. 单个方法:@GetMapping(value = "/target/all", produces = "application/json; charset=utf-8")
  2. 或者整个应用 注意:</mvc:annotation-driven> 只能有一个,要将上面的覆盖掉
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
 <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                    <!-- 如果是前后端使用JSON作为主要数据交换格式就把JSON列为第一个, 否则就会被认为是Text -->
                        <value>application/json; charset=UTF-8</value>
                        <value>text/plain; charset=UTF-8</value>
                        <value>text/html; charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

创建Controller

包 com.test.controller 下创建一个类

1
2
3
4
5
6
7
8
@RestController
@RequestMapping("/hi")
public class Hi {
    @RequestMapping("/hi")
    public String hi(){
        return "Hi";
    }
}

使用上 ResponseEntity 让响应结果规范

1
2
3
4
5
6
7
8
 @RequestMapping("/handle")
 public ResponseEntity<String> handle() {
   URI location = ...;
   HttpHeaders responseHeaders = new HttpHeaders();
   responseHeaders.setLocation(location);
   responseHeaders.set("MyResponseHeader", "MyValue");
   return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
 }

使用

在Springboot框架中,static templates 文件夹下分别代表了tomcat管理的静态文件和MVC负责跳转的HTML文件或JSP文件 在static中对于路径的使用一定要带上应用路径,而在templates中就只要写相对路径即可

配置类型转换

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    <mvc:annotation-driven conversion-service="conversionService" />
    <!--配置ConversionService -->
    <bean id="conversionService"
        class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="DateConverter" />
            </set>
        </property>
    </bean>

拦截器

拦截器机制

implements HandleInterceptor 有三个方法

preHandle 返回true就继续往后,false就被拦截 PostHandle 在渲染视图之前, afterCompletion 渲染视图之后调用,释放资源

1
2
3
4
5
6
    <mvc:interceptors>
        <mvc:interceptor>
            <bean class=""></bean>
            <mvc:mapping path="/**"/>
        </mvc:interceptor>
    </mvc:interceptors>

自定义拦截器

定义拦截器类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class MythInterceptor extends HandlerInterceptorAdapter{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Long startTime = System.currentTimeMillis();
        request.setAttribute("startTime",startTime);
        return true;// true就继续跳转,false就停止
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        long startTime = (Long)request.getAttribute("startTime");
        request.removeAttribute("startTime");
        Long endTime = System.currentTimeMillis();
        log.info(request.getRequestURL()+"发起请求耗时:[ "+ (endTime - startTime) +"  ms]");
    }
}

配置MVC的配置类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
    //自定义拦截器bean
    @Bean
    public MythInterceptor mythInterceptor(){
        return new MythInterceptor();
    }
    //注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //拦截器的URL正则
        registry.addInterceptor(mythInterceptor()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
}

Tips

URL 中带了 jsessionid 参数,导致页面各种问题