【spring mvc】Spring mvc 介绍
1、MVC介绍和控制层演示
MVC是模型(Model) 视图(View)和控制器(Controller)
模型:业务逻辑和保存数据的状态,其实落地就是 dao、service。
视图:显示界面,就是 jsp
控制器:取数据,调用业务逻辑,转向指定的界面,就是基于 Servlet。
首先记录一个简单的例子(没有具体的业务层):
1.1、创建普通的Maven项目,导入依赖
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
1.2、创建servlet,逻辑就是获取请求名,然后创建msg参数,重定向显示到指定界面
package com.wcy.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取前端参数
String method = req.getParameter("method");
if (method.equals("add")) {
req.getSession().setAttribute("msg","执行了add方法");
}
if (method.equals("delete")) {
req.getSession().setAttribute("msg","执行了delete方法");
}
//2.调用业务层
//3.视图转发或者重定向
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
1.3、创建web,这里老师用的是这样的方式创建web支持
编写web.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>
<servlet-name>hello</servlet-name>
<servlet-class>com.wcy.servlet.HelloServlet</servlet-class>
</servlet>
<!--对应请求名和method名-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
1.4、编写test.jsp视图显示servlet处理的结果
</head>
<body>
${msg}
</body>
</html>
1.5、结果
2 SpringMVC原理
2.1、简介
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在 Spring Framework 中
官方文档:
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html
2.2、DispatcherServlet
官网原话:Spring MVC 与许多其他 Web 框架一样,是围绕前端控制器模式设计的,其中一个中央控制器Servlet为DispatcherServlet请求处理提供共享算法,而实际工作由可配置的委托组件执行。该模型非常灵活,支持多种工作流程。
下图是DispatcherServlet的继承关系
2.3、实例
官网的图(中文版)
实际DispatcherServlet底层调用关系:实现部分为框架实现的,虚线部分用户自定义。
发起Http请求
HandleMapping为处理器映射,DispatcherServlet调用HandleMapping,HandleMapping根据请求url查找Handle
HandleExcution表示具体的Handle,其主要作用是根据url查找控制器,例子中url被查找控制器为:hello
HandleExcution将解析后的信息交给DispatcherServlet,如解析控制器映射等
HandleAdapter表示处理器适配器,其按照特定的规则去实现Handle
HandleAdapter让具体的Controller执行
Controller将具体的执行信息返回给HandleAdapter,如ModelAndView。
HandleAdapter将逻辑视图名或模型传递给DispatcherServlet
DispatcherServlet调用视图解析器(ViewResolver)来解析DispatcherServlet传递的逻辑视图名
视图解析器将解析的传递给DispatcherServlet
DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图
最终视图呈现给用户
对照这个底层原理,来看代码:
- 首先进入首页,然后发送http请求
- 然后进入到web.xml,这里注册了DispatcherServlet以及绑定了需要处理的请求,hello就在其中
<?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>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--启动级别1-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--匹配所有请求(不包括jsp)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- DispatcherServlet,根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括一个Handler处理器对象、多个HandlerInterceptor拦截器对象),最后以HandlerExecutionChain对象的形式返回。
<--springmvc-servlet.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--处理器映射和处理器声明-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--视图解析器:DispatcherServlet给他的ModelAndView-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!--Handle-->
<bean id="/hello" class="com.wcy.controller.HelloController"/>
</beans>
- DispatcherServlet将解析度控制器信息交给HandleAdapter,HandleAdapter让具体的Controller执行,就是下面这个。
package com.wcy.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//ModelAndView模型和视图
ModelAndView mv = new ModelAndView();
//封装对象,放到ModelAndView中
mv.addObject("msg","HelloSpringMVC!");
//封装要跳转的视图,放到ModelAndView里
mv.setViewName("hello");
return mv;
}
}
-
执行完毕之后将ModelAndView信息返回给HandleAdapter,HandleAdapter返回给DispatcherServlet
-
DispatcherServlet调用ViewResolver来解析逻辑视图名
<!--视图解析器:DispatcherServlet给他的ModelAndView-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/> <--记得这里最后要加/-->
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
- 解析完毕了,发现是给hello.jsp(/WEB-INF/jsp/hello.jsp)渲染图片。
3、SpringMVC-注解实现
3.1、介绍
对比与用继承Controller,然后springmvc-servlet里声明处理器映射和处理器声明,注解非常的方便,也是开发日常真正用的。
3.2、示例
web.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>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--启动级别1-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--匹配所有请求(不包括jsp)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
注意springmvc-servlet.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
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/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
<context:component-scan base-package="com.wcy.controller"/>
<!--让springMVC不处理静态资源,css,js什么的-->
<mvc:default-servlet-handler/>
<!--支持mvc注解驱动-->
<mvc:annotation-driven/>
<!--视图解析器:DispatcherServlet给他的ModelAndView-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
控制器,控制器通过@Controller标注为控制器,@RequestMapping标注请求名,
package com.wcy.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Controller(被这个注解标注的类,会被spring接管,这个类中给所有方法如果返回值为string,并且具体页面可以跳转,那么就会被视图解析器解析)
@RequestMapping("/HelloController")
//@RestController(如果是这个注解,返回值就是json字符串了)
public class HelloController {
//localhost:8080/HelloController/hello
@RequestMapping("/hello")
public String hello(Model model) {
//封装数据
model.addAttribute("msg","hello springMVC annotation");
return "hello";
}
//这里的redirect是重定向的意思,重定向就是说地址会变,例如这里 //localhost:8080/HelloController/hello 会变成//localhost:8080/index
@RequestMapping("/hello2")
public String hello(Model model) {
//封装数据
model.addAttribute("msg","hello springMVC annotation");
return "redirect:index";
}
}
4、SpringMVC-接受请求和数据显示
4.1、Controller
核心就两个,一个 @GetMapping()标签,一个@PostMapping()标签,这两个分别对应get请求和post请求。
然后就是@RequestParam("name"),默认参数名一样能对应上,建议都写@RequestParam("name")绑定参数名字,规范也易区分后端的参数
package com.wcy.controller;
import com.wcy.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/user")
public class UserController {
//localhost:8080/user/t1?name=wcy
@GetMapping("/t1")
public String test(@RequestParam("name") String name, Model model) {
System.out.println("接受到的前端参数信息为:" + name);
model.addAttribute("name",name);
return "test";
}
//localhost:8080/user/t2?id=1&age=24&name=wcy
@GetMapping("/t2")
public String test2(User user, Model model) {
System.out.println("接受到的前端参数信息为:" + user);
model.addAttribute("name",user);
return "test";
}
//ModelMap继承了LinkedHashMap,拥有LinkedHashMap的所有功能
@GetMapping("/t3")
public String test3(ModelMap map) {
return "test";
}
@PostMapping("/e/t1")
public String test1(String name, Model model) {
System.out.println(name);
model.addAttribute("name",name);
return "test";
}
}
4.2、前端显示
如这张图片,@ResponseBody注解会直接返回一个字符串,不走视图解析器
导入jackson的包
@RestController注解和@Controller不同就在于下面所有的方法都只会返回字符串
应用场景:日期格式
@RestController
public class testController {
@RequestMapping("/t1")
public String test() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Date date = new Date();
//自定义日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return mapper.writeValueAsString(sdf.format(date));
}
}
4.3、SpringMVC乱码问题
- 过滤器解决输入进来的数据
首先是加过滤器:这个过滤器是Spring框架帮我们写好的,但是对get的方法似乎支持不太好,有时候还有乱码就是需要找网上大神写的一些过滤器
<!--绑定过滤器,让所有的请求走过滤器-->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern><!--这里有个坑,就是/*才能包括得了jsp,因为加了jsp过滤器 -->
</filter-mapping>
tomcat编码格式配置
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" redirectPort="8443" URIEncoding="utf-8"/> (标黑的是加上的)
自己写的一个过滤器
package com.wcy.filter;
import javax.servlet.*;
import java.io.IOException;
/*
这是自己写的过滤器,会发现只能过滤get请求,springMVC帮忙写了强大的过滤器
*/
public class EncodingFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("UTF-8");
servletResponse.setCharacterEncoding("UTF-8");
filterChain.doFilter(servletRequest,servletResponse);
}
public void destroy() {
}
}
- json前端显示乱码解决
<!--解决json 乱码配置-->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>