https://start.spring.io/
- 创建 SpringBoot 应用,选中需要的模块
- 使用 SpringBoot 自动配置
- 编写业务代码
@AutoConfiguration 自动配置组件
@Properties 封装配置文件的内容
webjars&静态资源映射规则
1、webjars
配置类:WebMvcAutoConfiguration
webjars 以 jar 包的方式引入静态资源
https://www.webjars.org/
资源路径映射
/webjars/**
=>
classpath:/META-INF/resources/webjars/
添加 jquery 依赖
org.webjars
jquery
3.5.1
访问路径
/webjars/jquery/3.5.1/jquery.js
2、静态资源映射规则
静态资源文件夹
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
/ 当前项目根路径
默认静态文件下查找
# 欢迎页面
index.html
# 图标路径
favicon.ico
自定义静态资源文件路径,默认资源路径失效
spring.resources.static-locations=classpath:/hello/
引入 thymeleaf
JSP、Velocity、Thymeleaf、Freemarker
模板引擎
Template ${name} + Data {"name": "Tom"}
=> TemplateEngine =>
output
Thymeleaf 依赖
1.8
UTF-8
2.1.1.RELEASE
2.0.0
org.springframework.boot
spring-boot-starter-thymeleaf
${springboot-thymeleaf.version}
thymeleaf 语法
https://www.thymeleaf.org/
默认配置
public class ThymeleafProperties {
private String prefix = "classpath:/templates/";
private String suffix = ".html";
}
模板使用示例
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.HashMap;
@Controller
public class IndexController {
@RequestMapping("/hello")
public String hello(HashMap map){
map.put("name", "Tom");
// 模板路径
// src/main/resources/templates/about.html
return "hello";
}
}
模板:
src/main/resources/templates/about.html
Title
Hello
语法规则
th: 任意html属性,用来替换原生属性的值
th:text 改变文本内容(转义)
th:utext 改变文本内容(不转义)
th:attr
th:href
th:src
th:each
th:for
表达式
${} 变量表达式
获取变量值
获取变量属性
调用方法
内置基本对象: #ctx #session...
内置工具对象:
*{} 选择表达式
配合th:object使用
#{} 获取国际化内容
@{} 定义url
~{} 片段表达式
字面量
数学运算
布尔运算
比较运算
条件运算
特殊操作
示例
[[${pet}]]
SpringMVC 自动配置原理
SpringBoot 对 SpringMVC 默认配置
自动配置
ViewResolver 视图解析器
根据方法返回值的到视图对象(View)
视图对象决定如何渲染、转发、重定向
Converter 类型转换器
Formatter 格式化器
HttpMessageConverters 转换请求响应
MessageCodesResolver 定义错误代码生成规则
WebDataBinder 数据绑定器
修改 SpringBoot 默认配置
优先使用用户配置@Bean/@Component 如果没有才自动配置 有些组件可以有多个
eg: ViewResolver 将用户配置和默认配置组合起来
扩展与全面接管 SpringMVC1、扩展配置
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class CustomVmcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
// 浏览器的请求 /demo 到视图 /hello
registry.addViewController("demo").setViewName("hello");
}
}
2、全面接管
增加 @EnableWebMvc
后,自动配置失效
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@EnableWebMvc
@Configuration
public class CustomVmcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
// 浏览器的请求 /demo 到视图 /hello
registry.addViewController("demo").setViewName("hello");
}
}
引入资源
模板资源: https://getbootstrap.net/
模板语法: https://www.thymeleaf.org/
webjars: https://www.webjars.org/
目录设置
resources/
templates 模板文件
static 静态文件
首页设置
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class CustomVmcConfig extends WebMvcConfigurerAdapter {
// 设置首页位置,默认访问 public/index.html 没有经过模板引擎处理
@Bean
public WebMvcConfigurerAdapter CustomVmcConfig() {
WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
}
};
return adapter;
}
}
国际化
默认根据浏览器语言获取对应国际化信息
1、配置语言文件
resources 资源文件夹下
├── i18n
│ ├── login.properties
│ ├── login_en_US.properties
│ └── login_zh_CN.properties
默认配置 login.properties
login.button=登录~
login.title=登录~
login.username=用户名~
login.password=密码~
login.remember=记住我~
英文配置 login_en_US.properties
login.button=Sign In
login.title=Login
login.username=UserName
login.password=Password
login.remember=Remenber Me
中文配置 login_zh_CN.properties
login.button=登录
login.title=登录
login.username=用户名
login.password=密码
login.remember=记住我
2、配置 application.yml
spring:
messages:
basename: i18n.login
3、模板文件中使用
根据浏览器请求头设置语言
GET http://localhost:8080/
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
4、自定义国际化处理器
package com.example.demo.component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
/**
* 区域信息解析器
* 自定义国际化参数,支持链接上携带区域信息
*/
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String lang = request.getParameter("lang");
Locale locale = Locale.getDefault();
if (!StringUtils.isEmpty(lang)) {
String[] list = lang.split("_");
if (list.length == 2) {
locale = new Locale(list[0], list[1]);
}
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
启用自定义国际化处理器
package com.example.demo.config;
import com.example.demo.component.MyLocaleResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* 配置首页视图
*/
@Configuration
public class CustomVmcConfig extends WebMvcConfigurerAdapter {
@Bean
public MyLocaleResolver localeResolver() {
return new MyLocaleResolver();
}
}
优先获取查询参数返回语言设置
http://localhost:8080/?lang=zh_CN
http://localhost:8080/?lang=en_US
登陆&拦截器
开发期间模板引擎修改要实时生效
- 禁用模板引擎缓存
- 重新编译
拦截器进行登录检查
登录
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpSession;
import java.util.Map;
@Controller
public class LoginController {
@PostMapping("/user/login")
// 等价于 @RequestMapping(value = "/user/login", method = {RequestMethod.POST})
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Map map,
HttpSession session
) {
if (!StringUtils.isEmpty(username) && "123".equals(password)) {
session.setAttribute("loginUser", username);
// 登录成功 防止表单重新提交,做一个重定向
return "redirect:/dashboard.html";
} else {
// 登录失败
map.put("msg", "账号或密码不正确");
return "login";
}
}
}
拦截器
package com.example.demo.component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 登录检查
*/
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object loginUser = request.getSession().getAttribute("loginUser");
// 未登录,返回登录页面
if (loginUser == null) {
request.setAttribute("msg", "没有权限,请先登录");
request.getRequestDispatcher("/index.html").forward(request, response);
return false;
}
// 已登录,放行
else {
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
注册拦截器
package com.example.demo.config;
import com.example.demo.component.LoginHandlerInterceptor;
import com.example.demo.component.MyLocaleResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* 配置首页视图
*/
@Configuration
@SuppressWarnings("all")
public class CustomVmcConfig extends WebMvcConfigurerAdapter {
// 设置首页位置,默认访问 public/index.html 没有经过模板引擎处理
@Bean
public WebMvcConfigurerAdapter CustomVmcConfig() {
WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
/**
* 注册视图控制器
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
registry.addViewController("/dashboard.html").setViewName("dashboard");
}
/**
* 注册拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// super.addInterceptors(registry);
// 拦截任意路径下的所有请求, 排除请求
registry.addInterceptor(new LoginHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index.html", "/", "/user/login", "/static/**");
}
};
return adapter;
}
}
Restful CRUD
Rest 风格 URI: /资源/资源标识 HTTP 请求方式区分对资源 CRUD
说明普通 CRUD(URI 区分操作)RestfulCRUD查询getEmpGET emp添加addEmpPOST emp修改updateEmp?id=1PUT emp/{id}删除deleteEmp?id=1DELETE emp/{id}查询接口定义
说明请求方式请求 URI查询所有员工GETemps查询某个员工GETemp/{id}添加页面GETemp添加员工POSTemp修改页面GETemp/{id}修改员工PUTemp删除员工DELETEemp/{id} 员工列表-公共页抽取公共片段抽取
content
content
~{templateName::selector} 模板名::选择器
~{templateName::fragmentName}模板名::片段名
3 种方式引入片段
th:insert 插入
th:replace 替换
th:include 引入片段内容
使用th:insert可以不写~{}
转义[[~{}]]
不转义[(~{})]
引入片段时候传入参数
链接高亮&列表完成
redirect 重定向 forward 转发
日期格式化
spring.mvc.format.date: yyyy-MM-dd
HiddenHttpMethodFilter
SpringMVC 中配置 HiddenHttpMethodFilter 页面创建一个 Post 表单 创建一个 input 项 name="_method",值就是请求方式
有些版本可能需要配置 application.yml
spring.mvc.hiddenmethod.filter.enabled = true
错误页面
ErrorMvcAutoConfiguration
有模板引擎的情况下
error/状态码
eg:
精确匹配
error/404.html
模糊匹配
error/4xx.html
error/5xx.html
自定义异常处理
1、返回 JSON 数据
package com.example.demo.controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class ExceptionController {
@ResponseBody
@ExceptionHandler
public Map handleException(Exception e){
Map map = new HashMap();
map.put("code", -1);
map.put("msg", e.getMessage());
return map;
}
}
{
"msg": "用户不存在",
"code": -1
}
嵌入式 Servlet 容器配置修改
Tomcat 通用配置
# servlet配置
server.port=8001
server.context-path=/demo
# Tomcat配置
server.tomcat.uri-encoding=UTF-8
注册 servlet 三大组件
Servlet/Filter/Listener
1、Servlet
package com.example.demo.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 MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("MyServlet");
}
}
2、Filter
package com.example.demo.filter;
import javax.servlet.*;
import java.io.IOException;
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("MyFilter");
chain.doFilter(request, response);
}
}
3、Listener
package com.example.demo.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}
}
注册组件
package com.example.demo.config;
import com.example.demo.filter.MyFilter;
import com.example.demo.listener.MyListener;
import com.example.demo.servlet.MyServlet;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
/**
* 注册Servlet/Filter/Listener组件
*/
@Configuration
public class MyServletConfig {
@Bean
public ServletRegistrationBean myServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean();
registrationBean.setServlet(new MyServlet());
registrationBean.setUrlMappings(Arrays.asList("/servlet"));
return registrationBean;
}
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new MyFilter());
registrationBean.setUrlPatterns(Arrays.asList("/servlet"));
return registrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
ServletListenerRegistrationBean registrationBean = new ServletListenerRegistrationBean(new MyListener());
return registrationBean;
}
}
访问
http://localhost:8080/servlet
默认拦截: / 修改 server.servletPath
其他 Servlet 容器Tomcat 默认 Jetty 长连接 Undertow 不支持 jsp
嵌入式容器 外部容器
jar 包:执行 SpringBoot 主类的 main 方法,启动 IOC 容器,创建嵌入式的 Servlet 容器 war 包:启动服务器,服务器启动 SpringBoot,启动 IOC 容器