视频学习:
视频学习链接(点击跳转):
项目代码:
项目源代码(腾讯云):
链接:https://share.weiyun.com/mRoytxQc 密码:6dd54b
文章完整学习链接:
SpringMVC框架学习上篇
SpringMVC框架学习下篇
一.关于什么是MVC框架
- 三层架构
三层架构(表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)),是系统架构设计模式,属于宏观的解决方案,由于原有业务的复杂度提升,客户端和服务器端的职能需要调整,从而实现负载均衡,使系统具有良好的伸缩性。三层架构对原有程序进行了分层,不同的层运行在不同的机器上,所以可以非常自然地支持分布式开发,系统的分布式部署,便于提升系统性能,增加系统功能
- MVC架构
MVC(Model View Controller 模型-视图-控制器),是一种软件设计模式,属于微观的解决方案。由于原有业务的复杂度提升,对用户界面进行管理的程序的职责混乱导致代码难以维护。 MVC 关注的重点在于表现层的代码组织方式,通过将对数据的操作 M 与对视图V的操作分离开,用C 逻辑作为二者的粘合剂来降低代码间的耦合度,使代码更容易维护。
- 三层架构和MVC架构的区别
本质上,MVC和三层架构没有可比性,是应用于不同领域的一种模式,三层架构和MVC设计模式侧重点不一样,三层是一种笼统的架构思想,没有限制具体的设计;而MVC就比较具体的说明它的设计方法 。
二.关于什么是SpringMVC框架
Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。
SpringMVC执行原理图:
- 发起请求到前端控制器(DispatcherServlet)
- 前端控制器请求HandlerMapping查找 Handler,可以根据xml配置、注解进行查找
- 处理器映射器HandlerMapping向前端控制器返回Handler
- 前端控制器调用处理器适配器去执行Handler
- 处理器适配器去执行Handler
- Handler执行完成给适配器返回ModelAndView
- 处理器适配器向前端控制器返回ModelAndView,ModelAndView是springmvc框架的一个底层对象,包括Model和view
- 前端控制器请求视图解析器去进行视图解析,根据逻辑视图名解析成真正的视图(jsp)
- 视图解析器向前端控制器返回View
- 前端控制器进行视图渲染,视图渲染将模型数据(在ModelAndView对象中)填充到request域
- 前端控制器向用户响应结果
1.编写第一个SpringMVC程序
项目结构:
先创建一个Maven的空项目,HelloSpringMVC:
File->New->Project
在pom.xml中导入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</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<!--Mybatis中资源导出失败解决之build配置-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
添加项目的框架支持:
项目中就会多了红框框的几个文件:
在resources中新增一个SpringConfig(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>
<bean id="/hello" class="controller.HelloController" />
</beans>
在Java文件中新增一个controller文件,并在controller文件中新增HelloController类并实现Controller接口(这个操作和下面的bean是一一对应的)
先在java文件下创建一个controller文件
然后再创建一个HelloController类
让HelloController类实现Controller接口(选择mvc这个包)
实现该接口下的handleRequest方法
在该方法中编写如下代码
在WEB-INF文件下新建一个JSP文件
在jsp文件新增一个hello.jsp(注意一定是hello.jsp)
因为这里的hello和下面是一一对应的:
在hello.jsp中编写如下代码:
然后对web.xml进行配置,配置信息如下
<!-- 注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 关联配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动级别-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- /匹配所有,不包括jso-->
<!-- /*匹配,包括jsp-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
然后启动Tomcat:
选择+号:
并选择Local:
然后选择Artifact
在WEB_INF中创建一个lib并且将依赖导入
输入lib并将项目的依赖加入到这个文件中去:
点击apply然后点击ok
然后运行项目即可:
在地址栏输入/hello然后回车
2.使用注解实现SpringMVC程序
首先在springmvc-servlet.xml配置文件中导入头文件(context)
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
相关配置:
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
<context:component-scan base-package="controller"/>
在Java包中创建HelloController2类
运行一下:
三.RestFul 风格
Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
传统方式操作资源和使用RestFul 操作资源
- 传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get
- 使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!
1.实例1
需求:
访问这种形式(http://127.0.0.1/item/1/2)的路径(传统方式:http://127.0.0.1/item?num1=1&num2=2)
创建HelloController3类,代码如下:
运行效果:
2.实例2
需求:
对于同一个请求根据请求的方式(GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等)有多种处理方法。
有二种方式实现方式:
- 方式一通过定义@RequestMapping的method设置请求方式
@RequestMapping(value = "/item/{num1}/{num2}",method = {RequestMethod.GET})
public String hello(Model model, @PathVariable int num1, @PathVariable int num2){
//封装数据
model.addAttribute("msg", num1+"+"+num2+"="+(num1+num2));
return "hello";
}
请求方式如下图:
- 方式二直接使用对应请求方式的Mapping
@PostMapping("/item/{num1}/{num2}")
public String hello2(Model model, @PathVariable int num1, @PathVariable int num2){
//封装数据
model.addAttribute("msg", num1+"+"+num2+"="+(num1+num2));
return "hello";
}
注解有以下几种:
@GetMapping get请求
@PostMapping Post请求
@PutMapping put请求
@DeleteMapping Delete请求
@PatchMapping Patch请求
四.转发和重定向
在前面我们通过视图解析器自动拼链接(跳转链接)实现跳转(通过转发方式)
下图就是当访问/hello的时候转发到/WEB-INF/jsp/hello
.jsp路径(这里的hello就是下图方法返回的字符串)
字符串redirect:/hello
实现重定向,不加默认为转发,当然如果加上forward:/hello
也表示转发。
当然可以不用视图解析器也可以直接实现转发和重定向(不推荐这样使用
)
先把视图解析器注释了
其实就是把路径限定死,不用视图解析器进行拼接
但是如果用重定向访问hello.jsp会出错(因为在WEB-INF里的内容重定向是无法访问的)
五.数据处理
- 提交的域名称和处理方法的参数名一致
在HelloController2类中新增一个tohello方法
运行效果:
- 提交的域名称和处理方法的参数名不一致(使用@RequestParam设置名称)
运行效果:
- 提交的数据是一个对象
在pom.xml中导入lombok的依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
创建一个pojo包,在该包下创建一个User类
HelloController2类中创建tohello2方法
运行效果:
备注:前端传递的参数要和User类的字段名一致,如果不一致会返回默认值(未初始化的全局变量的值为对应数据类型的默认值)
六.数据显示
- 通过ModelAndView
在HelloController类中我们就是通过ModeAndView返回数据给前端的
- 通过Model(使用的最多)
在HelloController2,HelloController3类中对于数据的返回我们都是通过Mode进行返回的,跳转的路径通过return进行返回。
查看Model的源代码我们可以发现Model其实是一个接口
- 通过ModelMap
在HelloController2类中编写一个tohello3的方法
运行效果:
看看ModelMap的源代码:
ModelMap类中仅仅只有几个设置参数的方法,由于继承LinkedHashMap类,所有它可以使用LinkedHashMap类的所有方法。
七.乱码问题
- 使用SpringMVC提供的过滤器处理乱码
在index.jsp中增加一个表单
新增一个Encoding类用来处理用户提交表单的操作
运行效果:
点击提交查询,出现乱码!
以前乱码问题通过过滤器解决 , 而SpringMVC给我们提供了一个过滤器 , 可以在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>
</filter-mapping>
运行效果(一定要重启Tomcat服务器):
在输入框中输入张三然后点击提交查询,乱码问题得以解决!
为了避免一些极端情况我们可以通过修改tomcat配置文件的编码和自定义过滤器的方式来解决:
- 修改tomcat配置文件中的编码
在tmomcat服务器的conf目录下右击server.xml通过记事本打开,然后设置<Connector 标签的URIEncoding="utf-8"。
- 自定义过滤器
创建过滤器类GenericEncodingFilter
package controller;
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;
}
}
然后在Web.xml中使用自定义过滤器
<!--使用自定义过滤器-->
<filter>
<filter-name>encoding</filter-name>
<filter-class>controller.GenericEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
运行一下: