SpringMVC 是一种基于Java 的实现 MVC 设计模型的请求驱动类型的轻量级Web框架。
1 maven从0搭建
1.1 添加依赖
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.10</version>
</dependency>
<!--spring-webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.10</version>
</dependency>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!--jsp-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
1.2 添加spring-mvc配置
<beans
xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描注解-->
<context:component-scan base-package="com.test"/>
</beans>
1.3 创建webapp
创建maven项目后,src/main下添加webapp文件夹及下级文件及文件夹,如下图:
web.xml文件内容:
<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">
<!--springmvc核心控制器-->
<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:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
注意:/代表对所有请求都进行过滤操作,都要经过dispatcherServlet,但是某些静态资源通过dispatcherServlet是找不到的,所以需要在spring-mvc.xml中增加排除过滤的配置
<mvc:resources mapping="/文件夹/**" location="/文件夹/"/> 或者dispatcherServlet找不到时交由tomcat找的配置<mvc:default-servlet-handler/>。
1.4 测试Controller
@Controller
public class TestController {
@RequestMapping("/test")
@ResponseBody
public String printSome(){
return "spring mvc test";
}
}
tomcat配置:
启动tomcat,测试结果:
2 核心解析
2.1 核心控制处理方法
DispatcherServlet中doDispatch方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//1 查询handler,返回处理器执行链
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//2 请求执行handler
//2.1 获取处理器适配器
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//2.2 执行handler,并返回结果
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
// 3 视图解析器进行处理
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
2.2 三大组件
处理器映射器、处理器适配器、视图解析器是springmvc的三大组件,spring-webmvc包下org/springframework/web/servlet/DispatcherServlet.properties
中配置了这三大组件:
# 处理器映射器
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
# 处理器适配器
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
# 视图解析器
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
2.3 修改视图解析器默认参数
修改视图解析器中默认值:
(UrlBasedViewResolver中默认prefix、suffix均为"")
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀,给controller中的返回值加前缀,可指定路径-->
<property name="prefix" value="/"></property>
<!--后缀,给controller中的返回值加后缀,可指定文件类型-->
<property name="suffix" value=".jsp"></property>
</bean>
3 Controller响应
3.1 页面跳转
3.1.1 返回字符串
@RequestMapping("/test")
public String test1(){
return "test";
}
返回值为:视图解析器中默认的参数prefix+“test”+suffix,如果要跳转去执行另一个controller方法,返回值可以为:
forward:路径
redirect:路径
3.1.2 返回ModelAndView对象
@RequestMapping("/test2")
public ModelAndView test2(){
ModelAndView modelAndView = new ModelAndView();
//重定向
//modelAndView.setViewName("redirect:test.jsp");
//转发
modelAndView.setViewName("forward:test.jsp");
modelAndView.addObject("user","marry");
return modelAndView;
}
test.jsp中可以通过${user}显示。
3.2 回写数据
3.2.1 输出流
@RequestMapping("/test3")
public void test3(HttpServletResponse response) throws IOException {
response.getWriter().print("testspringmvc");
}
3.2.2 返回字符串
增加@ResponseBody注解:
@RequestMapping("/test4")
@ResponseBody
public String test4(){
return "test4";
}
3.2.3 返回对象或集合
添加jackson依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
spring-mvc.xml中添加配置,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
或者使用springmvc的注解驱动:
<!--自动加载RequestMappingHandlerMapping和 RequestMappingHandlerAdapter-->
<mvc:annotation-driven/>
3.2.3.1 返回对象
@RequestMapping("/test6")
@ResponseBody
public Map test6() throws Exception {
Map<String,Object> map = new HashMap();
map.put("name","kk");
map.put("skil",new String[]{"lis","lll"});
return map;
}
3.2.3.2 返回集合
@RequestMapping("/test7")
@ResponseBody
public List test7() throws Exception {
List<Object> list = new ArrayList<>();
Map<String,Object> map1 = new HashMap();
map1.put("name","kk");
map1.put("skil",new String[]{"lis","lll"});
list.add(map1);
Map<String,Object> map2 = new HashMap();
map2.put("name","ss");
map2.put("skil",new String[]{"ooo","ppp"});
list.add(map2);
return list;
}
4 Controller请求
4.1 基本数据类型参数
@RequestMapping("/test8")
@ResponseBody
public String test8(String userName) {
return String.format("user:%s",userName);
}
请求方式:/test8?userName=kk
4.2 POJO类型参数
@RequestMapping("/test9")
@ResponseBody
public User test9(User user) {
return user;
}
请求方式:/test9?userName=kk
注意:
①userName需与User中的字段相同
②需要引入依赖jackson
两者都满足,才能实现自动映射
4.3 数组类型参数
@RequestMapping("/test10")
@ResponseBody
public String test10(String[] parms) {
return Arrays.toString(parms);
}
请求方式:/test10?parms=kk&parms=ss&&parms=pp
4.4 集合类型参数
@RequestMapping("/test11")
@ResponseBody
public List<User> test11(@RequestBody List<User> users) {
return users;
}
请求方式:postman中以POST请求方式发送请求,请求参数为json格式字符串,见下图。
也可以在使用前端页面发送ajax请求时指定contentType为json格式。
4.5 自定义类型转换器
客户端输入字符串,controller中的接收参数为Date,如果不自定义转换器,报错如下:
自定义一个日期类型转换器:
//string类型转换为Date类型
public class DateConvert implements Converter<String, Date> {
@Override
public Date convert(String source) {
try {
return new SimpleDateFormat("yyyy-MM-dd").parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
spring-mvc.xml文件中添加配置:
<mvc:annotation-driven conversion-service="converterService"/>
<bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.test.convert.DateConvert"/>
</list>
</property>
</bean>
自定义类型转换器的好处:
①可以实现数据类型校验,不符合转换规则的数据可以在提交时就阻断;
②减少重复进行数据转换的代码。
5 注解
注解 | 说明 |
---|---|
@RequestMapping | 用于指定请求的URL |
@ResponseBody | 用于指定按照返回的类型返回数据 |
@RequestBody | post请求时,接收请求参数值 |
@RequestParam | 将请求参数与方法参数名进行映射,并可设置参数是否必传 |
@PathVariable | 获取请求路径中参数的值,如/user/{id},(@PathVariable(value=“id”) int id) |
@RequestHeader | 获取请求头,(@RequestHeader(value=“User-Agent”,required = false) String userAgent) |
@CookieValue | 获取指定Cookie的值,(@CookieValue(value = “JSESSIONID”) String jid) |