什么是MVC
面试题:什么是三层模型,并说一说MVC架构模式与三层模型的区别?
什么是SpringMVC
SpringMVC概述
SpringMVC帮我们做了什么
SpringMVC框架的特点
相关版本
第一个SpringMVC程序
创建maven模块
<dependencies>
<!-- Spring MVC依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.4</version>
</dependency>
<!--日志框架Logback依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.3</version>
</dependency>
<!--Servlet依赖-->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<!--Spring6和Thymeleaf整合依赖-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
</dependencies>
添加web支持
配置web.xml文件
编写控制器FirstController
配置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"
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">
<!--组件扫描-->
<context:component-scan base-package="com.powernode.springmvc.controller"/>
<!--视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<!--作用于视图渲染的过程中,可以设置视图渲染后输出时采用的编码字符集-->
<property name="characterEncoding" value="UTF-8"/>
<!--如果配置多个视图解析器,它来决定优先使用哪个视图解析器,它的值越小优先级越高-->
<property name="order" value="1"/>
<!--当 ThymeleafViewResolver 渲染模板时,会使用该模板引擎来解析、编译和渲染模板-->
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器负责根据模板位置、模板资源名称、文件编码等信息,加载模板并对其进行解析-->
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<!--设置模板文件的位置(前缀)-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--设置模板文件后缀(后缀),Thymeleaf文件扩展名不一定是html,也可以是其他,例如txt,大部分都是html-->
<property name="suffix" value=".html"/>
<!--设置模板类型,例如:HTML,TEXT,JAVASCRIPT,CSS等-->
<property name="templateMode" value="HTML"/>
<!--用于模板文件在读取和解析过程中采用的编码字符集-->
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
提供视图
控制器FirstController处理请求返回逻辑视图名称
测试
执行流程总结
笔记
1. 第一个Spring MVC程序的开发流程
1.1 创建Maven模块
第一步:创建一个空的工程springmvc
第二步:设置JDK版本
第三步:设置Maven
第四步:创建Maven模块(我这里创建的是一个普通的Maven模块)
第五步:在pom文件中设置打包方式:war
第六步:引入依赖:
springmvc依赖
logback依赖
thymeleaf和spring6整合依赖
servlet依赖(scope设置为provided,表示这个依赖最终由第三方容器来提供。)
1.2 给Maven模块添加web支持
在模块下的src\main目录下新建 webapp目录(默认是带有小蓝点的,没有小蓝点,自己添加Web支持就有小蓝点了。)
另外,在添加web支持的时候,需要添加web.xml文件,注意添加的路径。
1.3 在web.xml文件中配置:前端控制器(SpringMVC框架内置的一个类:DispatcherServlet),所有的请求都应该经过这个DispatcherServlet的处理。
重点:<url-pattern>/</url-pattern>
这里的 / 表示:除xx.jsp结尾的请求路径之外的所有请求路径。
也就是说,只要不是JSP请求路径,那么一定会走DispatcherServlet。
1.4 编写FirstController,在类上标注 @Controller 注解,纳入IoC容器的管理。
当然,也可以采用 @Component注解进行标注。 @Controller 只是 @Component 注解的别名。
1.5 配置/编写 SpringMVC框架自己的配置文件:
这个配置文件有默认的名字:<servlet-name>-servlet.xml
这个配置文件有默认的存放位置:WEB-INF 目录下。
两个配置:
第一个:配置组件扫描
第二个:配置视图解析器
1.6 提供视图
在/WEB-INF/templates目录下新建 first.thymeleaf 文件
在该文件中编写符合 Thymeleaf 语法格式的字符串(编写Thymeleaf的模板语句)
1.7 提供请求映射
@RequestMapping("/test")
public String hehe(){
// 处理业务逻辑....
// 返回一个逻辑视图名称
return "first";
}
最终会将逻辑视图名称转换为物理视图名称:
逻辑视图名称:first
物理视图名称:前缀 + first + 后缀
最终路径是:/WEB-INF/templates/first.thymeleaf
使用Thymeleaf模板引擎,将/WEB-INF/templates/first.thymeleaf转换成html代码,最终响应给浏览器。
1.8 测试
配置Tomcat服务器
解决Tomcat服务器控制台日志乱码问题
启动Tomcat服务器,在浏览器地址栏上直接发送请求:http://localhost:8080/springmvc/test
一个Controller可以编写多个方法
动态获取(thymeleaf)
第二个SpringMVC程序
创建Maven模块
添加web支持
配置web.xml文件
RequestMapping的作用
RequestMapping的出现位置
类上与方法上结合使用
第一种方案
第二种方案
RequestMapping注解的value属性
value属性的使用
小结
Ant风格的value
小结
value中的占位符(重点)
小结
RequestMapping注解的method属性
method属性的作用
衍生Mapping
小结
web的请求方式
小结
GET和POST的区别
区别是什么
怎么选择
RequestMapping注解的params属性
params属性的理解
params属性的4种用法
测试params属性
这里用的是thymeleaf语法,也可以是网页?&
小结
RequestMapping注解的headers属性
认识headers属性
headers属性的4种用法
测试headers属性
小结
获取请求数据
创建模块,添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.powernode.springmvc</groupId>
<artifactId>springmvc-003</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!--springmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.4</version>
</dependency>
<!--logback依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.3</version>
</dependency>
<!--servlet依赖-->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<!--thymeleaf和spring6整合的依赖-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
添加web支持
编写web.xml文件
创建UserController
编写springmvc.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"
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">
<!--组件扫描-->
<context:component-scan base-package="com.powernode.springmvc.controller"/>
<!--视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<!--作用于视图渲染的过程中,可以设置视图渲染后输出时采用的编码字符集-->
<property name="characterEncoding" value="UTF-8"/>
<!--如果配置多个视图解析器,它来决定优先使用哪个视图解析器,它的值越小优先级越高-->
<property name="order" value="1"/>
<!--当 ThymeleafViewResolver 渲染模板时,会使用该模板引擎来解析、编译和渲染模板-->
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器负责根据模板位置、模板资源名称、文件编码等信息,加载模板并对其进行解析-->
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<!--设置模板文件的位置(前缀)-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--设置模板文件后缀(后缀),Thymeleaf文件扩展名不一定是html,也可以是其他,例如txt,大部分都是html-->
<property name="suffix" value=".html"/>
<!--设置模板类型,例如:HTML,TEXT,JAVASCRIPT,CSS等-->
<property name="templateMode" value="HTML"/>
<!--用于模板文件在读取和解析过程中采用的编码字符集-->
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
编写register.html文件
部署测试
1.使用原生的Servlet API进行获取
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户注册</title>
</head>
<body>
<h3>用户注册</h3>
<hr>
<form th:action="@{/register}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
性别:
男 <input type="radio" name="sex" value="1">
女 <input type="radio" name="sex" value="0">
<br>
爱好:
抽烟 <input type="checkbox" name="hobby" value="smoke">
喝酒 <input type="checkbox" name="hobby" value="drink">
烫头 <input type="checkbox" name="hobby" value="perm">
<br>
简介:<textarea rows="10" cols="60" name="intro"></textarea><br>
<input type="submit" value="注册">
</form>
</body>
</html>
小结
2.使用RequestParam注解标注
RequestParam注解的基本使用
RequestParam注解的required属性
RequestParam注解的defaultValue属性
小结
3.依靠控制器方法上的形参名来接收
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
<configuration>
<source>21</source>
<target>21</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
小结
4.使用POJO类/JavaBean接收请求参数
package com.powernode.springmvc.pojo;
import java.util.Arrays;
/**
* ClassName: User
* Description:
* Datetime: 2024/3/15 10:51
* Author: 老杜@动力节点
* Version: 1.0
*/
public class User {
private Long id;
private String username;
private String password;
private String sex;
private String[] hobby;
private String intro;
public User() {
}
public User(Long id, String username, String password, String sex, String[] hobby, String intro) {
this.id = id;
this.username = username;
this.password = password;
this.sex = sex;
this.hobby = hobby;
this.intro = intro;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", sex='" + sex + '\'' +
", hobby=" + Arrays.toString(hobby) +
", intro='" + intro + '\'' +
'}';
}
}
package com.powernode.springmvc.pojo;
import java.util.Arrays;
/**
* ClassName: User
* Description:
* Datetime: 2024/3/15 10:51
* Author: 老杜@动力节点
* Version: 1.0
*/
public class User {
private Long id;
private String uname;
private String upwd;
private String usex;
private String[] uhobby;
private String uintro;
public User() {
}
public User(Long id, String username, String password, String sex, String[] hobby, String intro) {
this.id = id;
this.uname = username;
this.upwd = password;
this.usex = sex;
this.uhobby = hobby;
this.uintro = intro;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return uname;
}
public void setUsername(String username) {
this.uname = username;
}
public String getPassword() {
return upwd;
}
public void setPassword(String password) {
this.upwd = password;
}
public String getSex() {
return usex;
}
public void setSex(String sex) {
this.usex = sex;
}
public String[] getHobby() {
return uhobby;
}
public void setHobby(String[] hobby) {
this.uhobby = hobby;
}
public String getIntro() {
return uintro;
}
public void setIntro(String intro) {
this.uintro = intro;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + uname + '\'' +
", password='" + upwd + '\'' +
", sex='" + usex + '\'' +
", hobby=" + Arrays.toString(uhobby) +
", intro='" + uintro + '\'' +
'}';
}
}
小结
RequestHeader注解
CookieValue注解
请求的中文乱码问题
get请求乱码
小结
post请求乱码
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.filter;
import java.io.IOException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Servlet Filter that allows one to specify a character encoding for requests.
* This is useful because current browsers typically do not set a character
* encoding even if specified in the HTML page or form.
*
* <p>This filter can either apply its encoding if the request does not already
* specify an encoding, or enforce this filter's encoding in any case
* ("forceEncoding"="true"). In the latter case, the encoding will also be
* applied as default response encoding (although this will usually be overridden
* by a full content type set in the view).
*
* @author Juergen Hoeller
* @since 15.03.2004
* @see #setEncoding
* @see #setForceEncoding
* @see jakarta.servlet.http.HttpServletRequest#setCharacterEncoding
* @see jakarta.servlet.http.HttpServletResponse#setCharacterEncoding
*/
public class CharacterEncodingFilter extends OncePerRequestFilter {
@Nullable
private String encoding;
private boolean forceRequestEncoding = false;
private boolean forceResponseEncoding = false;
/**
* Create a default {@code CharacterEncodingFilter},
* with the encoding to be set via {@link #setEncoding}.
* @see #setEncoding
*/
public CharacterEncodingFilter() {
}
/**
* Create a {@code CharacterEncodingFilter} for the given encoding.
* @param encoding the encoding to apply
* @since 4.2.3
* @see #setEncoding
*/
public CharacterEncodingFilter(String encoding) {
this(encoding, false);
}
/**
* Create a {@code CharacterEncodingFilter} for the given encoding.
* @param encoding the encoding to apply
* @param forceEncoding whether the specified encoding is supposed to
* override existing request and response encodings
* @since 4.2.3
* @see #setEncoding
* @see #setForceEncoding
*/
public CharacterEncodingFilter(String encoding, boolean forceEncoding) {
this(encoding, forceEncoding, forceEncoding);
}
/**
* Create a {@code CharacterEncodingFilter} for the given encoding.
* @param encoding the encoding to apply
* @param forceRequestEncoding whether the specified encoding is supposed to
* override existing request encodings
* @param forceResponseEncoding whether the specified encoding is supposed to
* override existing response encodings
* @since 4.3
* @see #setEncoding
* @see #setForceRequestEncoding(boolean)
* @see #setForceResponseEncoding(boolean)
*/
public CharacterEncodingFilter(String encoding, boolean forceRequestEncoding, boolean forceResponseEncoding) {
Assert.hasLength(encoding, "Encoding must not be empty");
this.encoding = encoding;
this.forceRequestEncoding = forceRequestEncoding;
this.forceResponseEncoding = forceResponseEncoding;
}
/**
* Set the encoding to use for requests. This encoding will be passed into a
* {@link jakarta.servlet.http.HttpServletRequest#setCharacterEncoding} call.
* <p>Whether this encoding will override existing request encodings
* (and whether it will be applied as default response encoding as well)
* depends on the {@link #setForceEncoding "forceEncoding"} flag.
*/
public void setEncoding(@Nullable String encoding) {
this.encoding = encoding;
}
/**
* Return the configured encoding for requests and/or responses.
* @since 4.3
*/
@Nullable
public String getEncoding() {
return this.encoding;
}
/**
* Set whether the configured {@link #setEncoding encoding} of this filter
* is supposed to override existing request and response encodings.
* <p>Default is "false", i.e. do not modify the encoding if
* {@link jakarta.servlet.http.HttpServletRequest#getCharacterEncoding()}
* returns a non-null value. Switch this to "true" to enforce the specified
* encoding in any case, applying it as default response encoding as well.
* <p>This is the equivalent to setting both {@link #setForceRequestEncoding(boolean)}
* and {@link #setForceResponseEncoding(boolean)}.
* @see #setForceRequestEncoding(boolean)
* @see #setForceResponseEncoding(boolean)
*/
public void setForceEncoding(boolean forceEncoding) {
this.forceRequestEncoding = forceEncoding;
this.forceResponseEncoding = forceEncoding;
}
/**
* Set whether the configured {@link #setEncoding encoding} of this filter
* is supposed to override existing request encodings.
* <p>Default is "false", i.e. do not modify the encoding if
* {@link jakarta.servlet.http.HttpServletRequest#getCharacterEncoding()}
* returns a non-null value. Switch this to "true" to enforce the specified
* encoding in any case.
* @since 4.3
*/
public void setForceRequestEncoding(boolean forceRequestEncoding) {
this.forceRequestEncoding = forceRequestEncoding;
}
/**
* Return whether the encoding should be forced on requests.
* @since 4.3
*/
public boolean isForceRequestEncoding() {
return this.forceRequestEncoding;
}
/**
* Set whether the configured {@link #setEncoding encoding} of this filter
* is supposed to override existing response encodings.
* <p>Default is "false", i.e. do not modify the encoding.
* Switch this to "true" to enforce the specified encoding
* for responses in any case.
* @since 4.3
*/
public void setForceResponseEncoding(boolean forceResponseEncoding) {
this.forceResponseEncoding = forceResponseEncoding;
}
/**
* Return whether the encoding should be forced on responses.
* @since 4.3
*/
public boolean isForceResponseEncoding() {
return this.forceResponseEncoding;
}
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String encoding = getEncoding();
if (encoding != null) {
if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
if (isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}
}
小结
Servlet中的三个域对象
request
session
application
request域对象
1.使用原生Servlet API方式
2.使用Model接口
3.使用Map接口
4.使用ModelMap类
Model、Map、ModelMap的关系
5.使用ModelAndView类
ModelAndView源码分析
小结
session域对象
1.使用原生Servlet API
2.使用SessionAttributes注解
application域对象
小结
SpringMVC中视图的实现原理
Spring MVC视图支持可配置
Spring MVC支持的常见视图
实现视图机制的核心接口
实现视图机制的原理描述
逻辑视图名到物理视图名的转换
Thymeleaf视图
JSP视图(了解)
转发与重定向
回顾转发和重定向区别
forward
redirect
小结
mvc:view-controller(需配合mvc:annotation-driven注解)
mvc:annotation-driven/
访问静态资源
1.使用默认Servlet处理静态资源
2.使用 mvc:resources 标签配置静态资源
小结
RESTFul编程风格
RESTFul是什么
RESTFul风格与传统方式对比
小结
RESTFul方式演示查询(GET)
根据id查询(GET /api/user/1)
<?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">
<!--组件扫描-->
<context:component-scan base-package="com.powernode.springmvc.controller"/>
<!--视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8"/>
<property name="order" value="1"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/thymeleaf/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!--启用注解-->
<mvc:annotation-driven/>
<!--视图控制器映射-->
<mvc:view-controller path="/" view-name="index"/>
</beans>
查询所有(GET /api/user)
RESTFul方式演示增加(POST /api/user)
RESTFul方式演示修改(PUT)
小结
HiddenHttpMethodFilter原理剖析
使用RESTFul实现用户管理系统
静态页面准备
user.css
.header {
background-color: #f2f2f2;
padding: 20px;
text-align: center;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
li {
float: left;
}
li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
li a:hover:not(.active) {
background-color: #111;
}
.active {
background-color: #4CAF50;
}
form {
width: 50%;
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}
label {
display: block;
margin-bottom: 8px;
}
input[type="text"], input[type="email"], select {
width: 100%;
padding: 6px 10px;
margin: 8px 0;
box-sizing: border-box;
border: 1px solid #555;
border-radius: 4px;
font-size: 16px;
}
button[type="submit"] {
padding: 10px;
background-color: #4CAF50;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
button[type="submit"]:hover {
background-color: #3e8e41;
}
table {
border-collapse: collapse;
width: 100%;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
.header {
background-color: #f2f2f2;
padding: 20px;
text-align: center;
}
a {
text-decoration: none;
color: #333;
}
.add-button {
margin-bottom: 20px;
padding: 10px;
background-color: #4CAF50;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
.add-button:hover {
background-color: #3e8e41;
}
user_index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理系统</title>
<link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body>
<div class="header">
<h1>用户管理系统</h1>
</div>
<ul>
<li><a class="active" href="user_list.html">用户列表</a></li>
</ul>
</body>
</html>
user_list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户列表</title>
<link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body>
<div class="header">
<h1>用户列表</h1>
</div>
<div class="add-button-wrapper">
<a class="add-button" href="user_add.html">新增用户</a>
</div>
<table>
<thead>
<tr>
<th>编号</th>
<th>用户名</th>
<th>性别</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>张三</td>
<td>男</td>
<td>zhangsan@powernode.com</td>
<td>
修改
删除
</td>
</tr>
<tr>
<td>2</td>
<td>李四</td>
<td>女</td>
<td>lisi@powernode.com</td>
<td>
修改
删除
</td>
</tr>
</tbody>
</table>
</body>
</html>
user_add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>新增用户</title>
<link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body>
<h1>新增用户</h1>
<form>
<label>用户名:</label>
<input type="text" name="username" required>
<label>性别:</label>
<select name="gender" required>
<option value="">-- 请选择 --</option>
<option value="1">男</option>
<option value="0">女</option>
</select>
<label>邮箱:</label>
<input type="email" name="email" required>
<button type="submit">保存</button>
</form>
</body>
</html>
user_edit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>修改用户</title>
<link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body>
<h1>修改用户</h1>
<form>
<label>用户名:</label>
<input type="text" name="username" value="张三" required>
<label>性别:</label>
<select name="gender" required>
<option value="">-- 请选择 --</option>
<option value="1" selected>男</option>
<option value="0">女</option>
</select>
<label>邮箱:</label>
<input type="email" name="email" value="zhangsan@powernode.com" required>
<button type="submit">修改</button>
</form>
</body>
</html>
SpringMVC环境搭建
创建module:usermgt
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.powernode</groupId>
<artifactId>usermgt</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.5</version>
</dependency>
<!--servlet api-->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
</dependency>
<!--logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.3</version>
</dependency>
<!--thymeleaf+spring6整合依赖-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
添加web支持
配置web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<!--字符编码过滤器-->
<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>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--HTTP请求方式过滤器-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--前端控制器-->
<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.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
配置springmvc.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--组件扫描-->
<context:component-scan base-package="com.powernode.usermgt.controller,com.powernode.usermgt.dao"/>
<!--视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8"/>
<property name="order" value="1"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/thymeleaf/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!--开启注解-->
<mvc:annotation-driven/>
<!--开启默认Servlet-->
<mvc:default-servlet-handler/>
</beans>
显示首页
实现用户列表
package com.powernode.usermgt.bean;
public class User {
private Long id;
private String name;
private String email;
private Integer gender;
public User() {
}
public User(Long id, String name, String email, Integer gender) {
this.id = id;
this.name = name;
this.email = email;
this.gender = gender;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
'}';
}
}
实现新增功能
跳转到新增页面
实现新增功能
跳转到修改页面
实现修改功能
实现删除功能
HttpMessageConverter
什么是HTTP消息
转换器转换的是什么
Spring MVC中的AJAX请求
<?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">
<!--组件扫描-->
<context:component-scan base-package="com.powernode.springmvc.controller"/>
<!--视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8"/>
<property name="order" value="1"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/thymeleaf/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!--视图控制器映射-->
<mvc:view-controller path="/" view-name="index"/>
<!--开启注解驱动-->
<mvc:annotation-driven/>
<!--静态资源处理-->
<mvc:default-servlet-handler/>
</beans>
Vue3+Thymeleaf+Axios发送AJAX请求:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
<script th:src="@{/static/js/vue3.4.21.js}"></script>
<script th:src="@{/static/js/axios.min.js}"></script>
</head>
<body>
<h1>首页</h1>
<hr>
<div id="app">
<h1>{{message}}</h1>
<button @click="getMessage">获取消息</button>
</div>
<script th:inline="javascript">
Vue.createApp({
data(){
return {
message : "这里的信息将被刷新"
}
},
methods:{
async getMessage(){
try {
const response = await axios.get([[@{/}]] + 'hello')
this.message = response.data
}catch (e) {
console.error(e)
}
}
}
}).mount("#app")
</script>
</body>
</html>
@ResponseBody
StringHttpMessageConverter
MappingJackson2HttpMessageConverter
小结
@RestController
@RequestBody
FormHttpMessageConverter
MappingJackson2HttpMessageConverter
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
<script th:src="@{/static/js/vue3.4.21.js}"></script>
<script th:src="@{/static/js/axios.min.js}"></script>
</head>
<body>
<div id="app">
<button @click="sendJSON">通过POST请求发送JSON给服务器</button>
<h1>{{message}}</h1>
</div>
<script>
let jsonObj = {"username":"zhangsan", "password":"1234"}
Vue.createApp({
data(){
return {
message:""
}
},
methods: {
async sendJSON(){
console.log("sendjson")
try{
const res = await axios.post('/springmvc/send', JSON.stringify(jsonObj), {
headers : {
"Content-Type" : "application/json"
}
})
this.message = res.data
}catch(e){
console.error(e)
}
}
}
}).mount("#app")
</script>
</body>
</html>
小结
RequestEntity
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
<script th:src="@{/static/js/vue3.4.21.js}"></script>
<script th:src="@{/static/js/axios.min.js}"></script>
</head>
<body>
<div id="app">
<button @click="sendJSON">通过POST请求发送JSON给服务器</button>
<h1>{{message}}</h1>
</div>
<script>
let jsonObj = {"username":"zhangsan", "password":"1234"}
Vue.createApp({
data(){
return {
message:""
}
},
methods: {
async sendJSON(){
console.log("sendjson")
try{
const res = await axios.post('/springmvc/send', JSON.stringify(jsonObj), {
headers : {
"Content-Type" : "application/json"
}
})
this.message = res.data
}catch(e){
console.error(e)
}
}
}
}).mount("#app")
</script>
</body>
</html>
ResponseEntity
小结
文件上传(UUTD文件命名)
<!--前端控制器-->
<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:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<multipart-config>
<!--设置单个支持最大文件的大小-->
<max-file-size>102400</max-file-size>
<!--设置整个表单所有文件上传的最大值-->
<max-request-size>102400</max-request-size>
<!--设置最小上传文件大小-->
<file-size-threshold>0</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
package com.powernode.springmvc.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.UUID;
@Controller
public class FileController {
@RequestMapping(value = "/file/up", method = RequestMethod.POST)
public String fileUp(@RequestParam("fileName") MultipartFile multipartFile, HttpServletRequest request) throws IOException {
String name = multipartFile.getName();
System.out.println(name);
// 获取文件名
String originalFilename = multipartFile.getOriginalFilename();
System.out.println(originalFilename);
// 将文件存储到服务器中
// 获取输入流
InputStream in = multipartFile.getInputStream();
// 获取上传之后的存放目录
File file = new File(request.getServletContext().getRealPath("/upload"));
// 如果服务器目录不存在则新建
if(!file.exists()){
file.mkdirs();
}
// 开始写
//BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file.getAbsolutePath() + "/" + originalFilename));
// 可以采用UUID来生成文件名,防止服务器上传文件时产生覆盖
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file.getAbsolutePath() + "/" + UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."))));
byte[] bytes = new byte[1024 * 100];
int readCount = 0;
while((readCount = in.read(bytes)) != -1){
out.write(bytes,0,readCount);
}
// 刷新缓冲流
out.flush();
// 关闭流
in.close();
out.close();
return "ok";
}
}
小结
文件下载
小结
什么是异常处理器
默认的异常处理器
自定义的异常处理器
配置文件方式
注解方式
拦截器概述
拦截器的创建与基本配置
定义拦截器
拦截器基本配置
拦截器部分源码分析
方法执行顺序的源码分析
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 调用所有拦截器的 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 调用处理器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 调用所有拦截器的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 处理视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// 渲染页面
render(mv, request, response);
// 调用所有拦截器的 afterCompletion 方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
拦截与放行的源码分析
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 调用所有拦截器的 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 如果 mappedHandler.applyPreHandle(processedRequest, response) 返回false,以下的return语句就会执行
return;
}
}
}
public class HandlerExecutionChain {
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
// 如果 interceptor.preHandle(request, response, this.handler) 返回 false,以下的 return false;就会执行。
return false;
}
this.interceptorIndex = i;
}
return true;
}
}
拦截器的高级配置
拦截器的执行顺序
执行顺序
如果所有拦截器preHandle都返回true
如果其中一个拦截器preHandle返回false
源码分析
从源码角度看执行流程
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 根据请求对象request获取
// 这个对象是在每次发送请求时都创建一个,是请求级别的
// 该对象中描述了本次请求应该执行的拦截器是哪些,顺序是怎样的,要执行的处理器是哪个
HandlerExecutionChain mappedHandler = getHandler(processedRequest);
// 根据处理器获取处理器适配器。(底层使用了适配器模式)
// HandlerAdapter在web服务器启动的时候就创建好了。(启动时创建多个HandlerAdapter放在List集合中)
// HandlerAdapter有多种类型:
// RequestMappingHandlerAdapter:用于适配使用注解 @RequestMapping 标记的控制器方法
// SimpleControllerHandlerAdapter:用于适配实现了 Controller 接口的控制器
// 注意:此时还没有进行数据绑定(也就是说,表单提交的数据,此时还没有转换为pojo对象。)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 执行请求对应的所有拦截器中的 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 通过处理器适配器调用处理器方法
// 在调用处理器方法之前会进行数据绑定,将表单提交的数据绑定到处理器方法上。(底层是通过WebDataBinder完成的)
// 在数据绑定的过程中会使用到消息转换器:HttpMessageConverter
// 结束后返回ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 执行请求对应的所有拦截器中的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 处理分发结果(在这个方法中完成了响应)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// 根据每一次的请求对象来获取处理器执行链对象
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// HandlerMapping在服务器启动的时候就创建好了,放到了List集合中。HandlerMapping也有多种类型
// RequestMappingHandlerMapping:将 URL 映射到使用注解 @RequestMapping 标记的控制器方法的处理器。
// SimpleUrlHandlerMapping:将 URL 映射到处理器中指定的 URL 或 URL 模式的处理器。
for (HandlerMapping mapping : this.handlerMappings) {
// 重点:这是一次请求的开始,实际上是通过处理器映射器来获取的处理器执行链对象
// 底层实际上会通过 HandlerMapping 对象获取 HandlerMethod对象,将HandlerMethod 对象传递给 HandlerExecutionChain对象。
// 注意:HandlerMapping对象和HandlerMethod对象都是在服务器启动阶段创建的。
// RequestMappingHandlerMapping对象中有多个HandlerMethod对象。
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// 渲染
render(mv, request, response);
// 渲染完毕后,调用该请求对应的所有拦截器的 afterCompletion方法。
mappedHandler.triggerAfterCompletion(request, response, null);
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 通过视图解析器返回视图对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// 真正的渲染视图
view.render(mv.getModelInternal(), request, response);
}
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
// 通过视图解析器返回视图对象
View view = viewResolver.resolveViewName(viewName, locale);
}
}
从图片角度看执行流程
WEB服务器启动时都做了什么
手写Springmvc(略)
全注解开发
web.xml文件的替代
编写WebAppInitializer
package com.powernode.springmvc.config;
import jakarta.servlet.Filter;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* ClassName: WebAppInitializer
* Description:
* Datetime: 2024/3/29 16:50
* Author: 老杜@动力节点
* Version: 1.0
*/
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* Spring的配置
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* SpringMVC的配置
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMVCConfig.class};
}
/**
* 用于配置 DispatcherServlet 的映射路径
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 配置过滤器
* @return
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceRequestEncoding(true);
characterEncodingFilter.setForceResponseEncoding(true);
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
}
}
Spring MVC的配置
组件扫描
开启注解驱动
视图解析器
// 指定该类是一个配置类,可以当配置文件使用
@Configuration
// 开启组件扫描
@ComponentScan("com.powernode.springmvc.controller")
// 开启注解驱动
@EnableWebMvc
public class SpringMVCConfig {
@Bean
public ThymeleafViewResolver getViewResolver(SpringTemplateEngine springTemplateEngine) {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(springTemplateEngine);
resolver.setCharacterEncoding("UTF-8");
resolver.setOrder(1);
return resolver;
}
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver iTemplateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(iTemplateResolver);
return templateEngine;
}
@Bean
public ITemplateResolver templateResolver(ApplicationContext applicationContext) {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/thymeleaf/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCharacterEncoding("UTF-8");
resolver.setCacheable(false);//开发时关闭缓存,改动即可生效
return resolver;
}
}
开启默认Servlet处理
view-controller
异常处理器
拦截器
SSM整合
引入相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.powernode</groupId>
<artifactId>ssmtest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.4</version>
</dependency>
<!--spring jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.1.4</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.15</version>
</dependency>
<!--mybatis spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
</dependency>
<!--德鲁伊连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.22</version>
</dependency>
<!--jackson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
<!--servlet api-->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<!--logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.3</version>
</dependency>
<!--thymeleaf和spring6的整合依赖-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
创建包结构
创建webapp目录
Spring整合MyBatis
编写jdbc.properties
编写DataSourceConfig
package com.powernode.ssm.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
/**
* ClassName: DataSourceConfig
* Description:
* Datetime: 2024/4/1 14:25
* Author: 老杜@动力节点
* Version: 1.0
*/
public class DataSourceConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
编写MyBatisConfig
package com.powernode.ssm.config;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
/**
* ClassName: MyBatisConfig
* Description:
* Datetime: 2024/4/1 14:25
* Author: 老杜@动力节点
* Version: 1.0
*/
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage("com.powernode.ssm.bean");
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.powernode.ssm.dao");
return msc;
}
}
编写SpringConfig
package com.powernode.ssm.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
/**
* ClassName: SpringConfig
* Description:
* Datetime: 2024/4/1 14:22
* Author: 老杜@动力节点
* Version: 1.0
*/
@Configuration
@ComponentScan({"com.powernode.ssm.service"})
@PropertySource("classpath:jdbc.properties")
@Import({DataSourceConfig.class, MyBatisConfig.class})
public class SpringConfig {
}
Spring整合Spring MVC
编写WebAppInitializer(web.xml)
package com.powernode.ssm.config;
import jakarta.servlet.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* ClassName: WebAppInitializer
* Description:
* Datetime: 2024/4/1 14:59
* Author: 老杜@动力节点
* Version: 1.0
*/
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* Spring的配置
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* SpringMVC的配置
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
/**
* 用来配置DispatcherServlet的 <url-pattern>
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 配置过滤器
* @return
*/
@Override
protected Filter[] getServletFilters() {
// 配置字符编码过滤器
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceResponseEncoding(true);
characterEncodingFilter.setForceRequestEncoding(true);
// 配置HiddenHttpMethodFilter
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
}
}
编写SpringMvcConfig
package com.powernode.ssm.config;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.thymeleaf.spring6.SpringTemplateEngine;
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring6.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;
import java.util.List;
/**
* ClassName: SpringMvcConfig
* Description:
* Datetime: 2024/4/1 15:02
* Author: 老杜@动力节点
* Version: 1.0
*/
@Configuration
@ComponentScan("com.powernode.ssm.handler")
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
// 以下三个方法合并起来就是开启视图解析器
@Bean
public ThymeleafViewResolver getViewResolver(SpringTemplateEngine springTemplateEngine) {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(springTemplateEngine);
resolver.setCharacterEncoding("UTF-8");
resolver.setOrder(1);
return resolver;
}
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver iTemplateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(iTemplateResolver);
return templateEngine;
}
@Bean
public ITemplateResolver templateResolver(ApplicationContext applicationContext) {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/thymeleaf/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCharacterEncoding("UTF-8");
resolver.setCacheable(false);//开发时关闭缓存,改动即可生效
return resolver;
}
// 开启静态资源处理,开启默认的Servlet处理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// 视图控制器
@Override
public void addViewControllers(ViewControllerRegistry registry) {}
// 配置异常处理器
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}
// 配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {}
}
添加事务控制
实现功能测试ssm整合
数据库表
pojo类编写
package com.powernode.ssm.bean;
/**
* ClassName: User
* Description:
* Datetime: 2024/4/1 15:42
* Author: 老杜@动力节点
* Version: 1.0
*/
public class User {
private Long id;
private String name;
private String password;
private String email;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
public User() {
}
public User(Long id, String name, String password, String email) {
this.id = id;
this.name = name;
this.password = password;
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
dao编写
package com.powernode.ssm.dao;
import com.powernode.ssm.bean.User;
import org.apache.ibatis.annotations.Select;
/**
* ClassName: UserDao
* Description:
* Datetime: 2024/4/1 15:43
* Author: 老杜@动力节点
* Version: 1.0
*/
public interface UserDao {
@Select("select * from tbl_user where id = #{id}")
User selectById(Long id);
}
service编写
package com.powernode.ssm.service;
import com.powernode.ssm.bean.User;
/**
* ClassName: UserService
* Description:
* Datetime: 2024/4/1 15:45
* Author: 老杜@动力节点
* Version: 1.0
*/
public interface UserService {
/**
* 根据id获取用户信息
* @param id
* @return
*/
User getById(Long id);
}
package com.powernode.ssm.service.impl;
import com.powernode.ssm.bean.User;
import com.powernode.ssm.dao.UserDao;
import com.powernode.ssm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* ClassName: UserServiceImpl
* Description:
* Datetime: 2024/4/1 15:45
* Author: 老杜@动力节点
* Version: 1.0
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User getById(Long id) {
return userDao.selectById(id);
}
}
handler编写
package com.powernode.ssm.handler;
import com.powernode.ssm.bean.User;
import com.powernode.ssm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* ClassName: UserHandler
* Description:
* Datetime: 2024/4/1 15:46
* Author: 老杜@动力节点
* Version: 1.0
*/
@RestController
@RequestMapping("/users")
public class UserHandler {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User detail(@PathVariable("id") Long id){
return userService.getById(id);
}
}
前端发送ajax
引入js文件
开启静态资源处理
视图控制器
编写ajax
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ssm整合</title>
<!--引入vue-->
<script th:src="@{/static/js/vue3.4.21.js}"></script>
<!--引入axios-->
<script th:src="@{/static/js/axios.min.js}"></script>
</head>
<body>
<div id="app">
<button @click="getMessage">查看id=1的用户信息</button>
<h1>{{message}}</h1>
</div>
<script th:inline="javascript">
Vue.createApp({
data(){
return {
message : ''
}
},
methods : {
async getMessage(){
let response = await axios.get([[@{/}]] + 'users/1')
this.message = response.data
}
}
}).mount("#app")
</script>
</body>
</html>
在线笔记
https://www.yuque.com/dujubin/java/myxi54xu063hgsl4?singleDoc# 《第1章 初识SpringMVC》
https://www.yuque.com/dujubin/java/ogwoytryn7bugbl6?singleDoc# 《第2章 RequestMapping注解》
https://www.yuque.com/dujubin/java/szhdmdqgkl0id6km?singleDoc# 《第3章 获取请求数据》
https://www.yuque.com/dujubin/java/xx2pep14urc8i5b3?singleDoc# 《第4章 三个域对象》
https://www.yuque.com/dujubin/java/nnv0b4etaq37rhi9?singleDoc# 《第5章 视图View》
https://www.yuque.com/dujubin/java/ya030vq1147hp0zc?singleDoc# 《第6章 RESTFul编程风格》
https://www.yuque.com/dujubin/java/zo4ab0u8ku4fzu6y?singleDoc# 《第7章 HttpMessageConverter》
https://www.yuque.com/dujubin/java/rmw13pozxr3zr0bk?singleDoc# 《第8章 文件上传与下载》
https://www.yuque.com/dujubin/java/gus9uv1y746q4o2g?singleDoc# 《第9章 异常处理器》
https://www.yuque.com/dujubin/java/otwub2uw65d1aid3?singleDoc# 《第10章 拦截器》
https://www.yuque.com/dujubin/java/eldauy9vai1fu36h?singleDoc# 《第11章 Spring MVC执行流程》
https://www.yuque.com/dujubin/java/osovozxwkeipiexu?singleDoc# 《第12章 手写Spring MVC》
https://www.yuque.com/dujubin/java/px94n1f868chscci?singleDoc# 《第13章 全注解开发》
https://www.yuque.com/dujubin/java/iptnrxyr74aeehc8?singleDoc# 《第14章 SSM整合》