文章目录
一、MVC架构
1.1 什么是MVC
- MVC :包括模型、视图、控制器
- 将业务逻辑、数据、显示分离的方法来组织代码
- 作用:降低了视图与业务逻辑间的双向偶合。
Model(模型): 数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或 JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
View(视图): 负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
Controller(控制器): 接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。
1.2 回顾Servlet
- 新建一个 maven 项目
导入依赖:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<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>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
- 新建一个模块
模块名:springMVC-01-Servlet
添加web-app支持(添加框架支持)
编写Servlet类
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/jspDir/test.jsp").forward(req,resp);/*服务器识别 8080/项目名 */
}
}
配置Servlet程序
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.study.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/helloServlet</url-pattern><!--浏览器识别 8080端口-->
</servlet-mapping>
跳转页面 test.jsp
<html>
<head>
<title>Title</title>
</head>
<body>
这是跳转过来的jsp页面
${msg}
</body>
</html>
首页 index.jsp
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="/01Servlet/helloServlet" method="get"> <%--浏览器识别 不加项目名,手动加 --%>
方法:<input type="text" name="method">
<input type="submit" value="提交">
</form>
</body>
</html>
MVC框架要做哪些事情
-
将url映射到java类或java类的方法 .
-
封装用户提交的数据 .
-
处理请求–调用相关的业务处理–封装响应数据 .
-
将响应的数据进行渲染 . jsp / html 等表示层数据 .
二、什么是SpringMVC
Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。
2.1 中心控制器
Spring的web框架围绕DispatcherServlet设计。DispatcherServlet的作用是将请求分发到不同的处理器。
Spring MVC框架以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)。
2.2 SpringMVC执行原理
-
执行流程图
-
执行流程:
-
DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。
我们假设请求的url为 : http://localhost:8080/SpringMVC/hello
如上url拆分成三部分:
http://localhost:8080 服务器域名
SpringMVC 部署在服务器上的web站点
hello 表示控制器
通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。 -
HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。
-
HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。(查找 id 为 /hello 的 bean)
-
HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
-
HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。(适配到 hello 的 controller ,class=“com.study.controller.HelloController”)
-
Handler让具体的Controller执行。
-
Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView(模型和视图)。
-
HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
-
DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名(要跳转的jsp页面)。
-
视图解析器将解析的逻辑视图名传给DispatcherServlet。
-
DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
-
最终视图呈现给用户。
三、第一个SpringMVC程序
- 新建一个模块 ,命名为 SpringMVC-02-hellomvc,添加 web-app框架支持。
- 配置 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">
<!--1.注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--绑定Spring【MVC】配置文件-->
<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页面
/* 表示匹配所有请求,包括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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--添加处理映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--添加处理适配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--添加视图解析器-->
<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
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg","跳转到jsp页面!!!");
mv.setViewName("hello");/*跳转视图名 /WEB-INF/jsp/hello.jsp*/
return mv;
}
}
- 注册bean
<!--注册 HelloController-->
<bean id="/hello" class="com.study.controller.HelloController"/>
- hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
- 配置到Tomcat服务器
访问出现404问题
- 确认导入依赖
- 在IDEA的项目发布中,添加lib依赖
四、使用注解开发SpringMVC
- 新建一个module,添加web-app框架支持
- 配置web.xml(和普通程序一样)
- 配置SpringMVC配置文件 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
http://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.study.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<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(使用注解)
@Controller // 让Spring IOC容器初始化时自动扫描到
@RequestMapping("/HelloController") //映射请求路径
public class HelloController {
//真实访问地址 : 项目名/HelloController/hello
@RequestMapping("/hello")
public String hello(Model model){ //Model类型的参数是为了把Action中的数据带到视图中
model.addAttribute("msg","hello,SpringMVC Annotation!!");
return "hello"; //web-inf/jsp/hello.jsp
}
}
- 创建视图层
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
- 为工程添加 lib 后,启动Tomcat
使用springMVC必须配置的三大件:处理器映射器、处理器适配器、视图解析器
通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置
五、控制器Controller
- 继承Controller
缺点:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦; - 使用@Controller
@Controller
@RequestMapping("/HelloController")
public class HelloController {
//真实访问地址 : 项目名/HelloController/hello
@RequestMapping("/hello")
public String hello(Model model){
model.addAttribute("msg","hello,SpringMVC Annotation!!");
return "hello"; //web-inf/jsp/hello.jsp
}
@RequestMapping("/hello01")
public String hello01(Model model){
model.addAttribute("msg","hello01 ,SpringMVC Annotation!!");
return "hello";
}
}
我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。
六、RequestMapping
@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@Controller
@RequestMapping("/HelloController")
public class HelloController {
//真实访问地址 : 项目名/HelloController/hello
@RequestMapping("/hello")
public String hello(Model model){
return "hello";
}
}
七、RestFul风格
概念
Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
使用RestFul风格,通过相同的RUL但是不同的请求方式,会请求到不同的页面。
RestFul风格 样例
@Controller
public class ControllerRestFul {
/*
springAnnotation工程名
普通请求方式:http://localhost:8080/springAnnotation/t1?a=1&b=2
*/
@RequestMapping("/t1")
public String test1(int a, int b, Model model){
model.addAttribute("msg","普通请求方式 t1:"+(a+b));
return "hello";
}
/*
springAnnotation工程名
RestFul 请求风格:http://localhost:8080/springAnnotation/t2/1/3
*/
@RequestMapping("/t2/{a}/{b}")
public String test2(@PathVariable int a,@PathVariable int b, Model model){
model.addAttribute("msg","RestFul风格: "+(a+b));
return "hello";
}
}
使用method属性指定请求类型
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
@Controller
public class ControllerRestFul {
/*
post请求方式
*/
//@PostMapping("/t1/{a}/{b}")
@RequestMapping(value = "/t1/{a}/{b}",method = RequestMethod.POST)
public String test1(@PathVariable int a,@PathVariable int b, Model model){
model.addAttribute("msg","post请求方式 :"+(a+b));
return "hello";
}
/*
get请求方式
*/
// @RequestMapping(path = "/t1/{a}/{b}",method = RequestMethod.GET)
@GetMapping("/t1/{a}/{b}")// 【推荐使用】
public String test2(@PathVariable int a,@PathVariable int b, Model model){
model.addAttribute("msg"," get请求方式 : "+(a+b));
return "hello";
}
}
八、转发和重定向
视图解析器——默认转发
访问后浏览器地址为 :http://localhost:8080/springAnnotation/m1/t1
@RequestMapping("/m1/t1")
public String test1(Model model){
model.addAttribute("msg","默认转发");
return "hello";//默认为转发
}
不需要视图解析器——转发
@RequestMapping("/m1/t2")
public String test2(Model model){
model.addAttribute("msg","转发 不需要视图解析器");
return "forward:/WEB-INF/jsp/hello.jsp";//转发操作
}
不需要视图解析器——重定向
@RequestMapping("/m1/t3")
public String test3(Model model){
// model.addAttribute("msg","重定向 不需要视图解析器");
return "redirect:/WEB-INF/jsp/hello.jsp";//重定向操作
}
重定向后,无法访问WEB-INF文件夹
九、前端数据的获取的回传
9.1 获取参数
@Controller
public class Data {
//一、不限定参数,通过url传参
//http://localhost:8080/springAnnotation/d1/t2?name=yaojingyang
@RequestMapping("/d1/t1")
public String test1(String name , Model model){
model.addAttribute("msg",name);
return "hello";
}
//二、通过注解限定url参数名称
//限定参数名 http://localhost:8080/springAnnotation/d1/t2?Username=yaojingyang
@RequestMapping("/d1/t2")
public String test2(@RequestParam("Username") String name,Model model){//获取
model.addAttribute("msg",name);
return "hello";
}
/* 当有一个实体类
public class User {
private String name;
private int age;
//get、set tostring方法构造
}
*/
//参数为类 http://localhost:8080/springAnnotation/d1/t3?name=tst&age=12
@RequestMapping("/d1/t3")
public String test3(User user,Model model){
model.addAttribute("msg",user.toString());
return "hello";
}
}
9.2 回传参数
- 通过ModelAndView :返回一个模型视图对象(继承Controller用到)
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg","跳转到jsp页面!!!");
mv.setViewName("hello");/*跳转视图名 /WEB-INF/jsp/hello.jsp*/
return mv;
}
}
- 通过ModelMap
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("name",name);
System.out.println(name);
return "hello";
}
- 通过Model
public String test3(User user,Model model){
model.addAttribute("msg",user.toString());
return "hello";
}
三者对比
十、乱码问题解决
首页提交表单
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form method="post" action="/springAnnotation/e1/t1">
<input type="text" name="name">
<input type="submit" value="提交">
</form>
</body>
</html>
Controller控制器
@Controller
public class EncodingTest {
@RequestMapping("/e1/t1")
public String test1(String name, Model model){
model.addAttribute("msg",name);
return "hello";
}
}
测试出现乱码
配置web.xml
<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文件-->
</filter-mapping>
其他处理方式
- 修改 Tomcat 编码方式 apache-tomcat-8.0.50\conf\server.xml
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
- 自定义过滤器
package com.kuang.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}
// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
//取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}
//取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}