💠
💠 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/"/><!–设置JSP文件的目录位置–>-->
<!--<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);
});
}
|
中文编码问题
参考博客 但是奇怪的是某些方法用第二种正常,有些还是要用第一种
- 单个方法:
@GetMapping(value = "/target/all", produces = "application/json; charset=utf-8")
- 或者整个应用 注意:
</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 参数,导致页面各种问题