前言:
小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师。
这个SpringBoot基础学习系列用来记录我学习SpringBoot框架基础知识的全过程 (这个系列是参照B站狂神的SpringBoot最新教程来写的,由于是之前整理的,但当时没有发布出来,所以有些地方可能有错误,希望大家能够及时指正!)
之后我将会以一天一更的速度更新这个系列,还没有学习SpringBoot的小伙伴可以参照我的博客学习一下;当然学习过的小伙伴,也可以顺便跟我一起复习一下基础。
最后,希望能够和大家一同进步吧!加油吧!少年们!
由于SpringBoot Web开发涉及的内容比较多,所以蜗牛君打算把这部分将会分成上中下三篇博客,上篇主要分析SpringBoot开发Web的优缺点以及静态资源的配置和使用;中篇主要介绍模板引擎和MVC配置原理,下篇是项目实战,基于SpringBoot构建一个简单的员工管理系统!
废话不多说,让我们开始今天的学习内容吧,由于今天我们来到了SpringBoot基础学习的第五站:SpringBoot Web开发(上篇)!
5.SpringBoot Web开发
5.1 使用SpringBoot的好处和思考
5.1.1 使用SpringBoot的好处
1. 未使用SpringBoot时
- 之前的开发,需要我们手动导入webapp的相关jar包,
- SpringMVC等还需要我们去进行手动配置
- 需要编写大量的配置文件,反而不能专注于业务代码了
2.使用了SpringBoot之后
- 现在我们只需要创建应用,然后选择模块就好了
- Spring已经将环境自动装配好了,也就是各种各种的starter启动器
- 我们只需专注于业务代码的编写,不用去管理SpringMVC等的配置了
SpringBoot的核心就是:自动装配
5.1.2 对SpringBoot的思考
从以下四个维度去进行思考:
SpringBoot到底帮我们配置了什么?我们能不能进行修改?能修改哪些东西?能不能扩展?
- xxxAutoConfiguration:自动配置类,向容器中自动配置组件
- xxxProperties:封装配置文件中的属性,也可以自定义内容
5.1.3 要解决的问题
- 导入静态资源:.css、.js、.jsp文件等
- 定制首页:解决打开页面就是404问题
- 模板引擎:使用Thymeleaf
- 装配扩展SpringMVC:文件上传、返回JSON字符串
- 增删改查:CRUD
- 拦截器:Interceptor
- 国际化:I18N
5.2 静态资源
5.2.1 创建SpringBoot项目
1.选择Spring initializer项目
2.设置项目基本信息
3.选择Spring Web应用资源依赖
4.选择文件存放位置
5.删除多余文件后项目结构
5.2.1 编写控制层进行测试
1.编写HelloController控制类
package com.kuang.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//实现Controller接口并且使返回值为字符串形式
@RestController
public class HelloController {
//设置请求映射路径:http://localhost:8080/hello
@RequestMapping("/hello")
public String hello() {
return "Hello,SpringBoot!";
}
}
2.启动SpringBoot应用
2-1 找到主应用程序类
2-2 启动应用成功
3.测试结果
3-1 访问主页
结果:访问失败,出现了"空白标签错误页面"提示
因此我们需要定制首页,这个后面会提到,目前先不用管它
3-2 访问/hello页面
结果:访问/hello请求成功!
5.2.3 分析WebMvc自动配置原理
1.分析WebMvcAutoConfiguration类
- 查看WebMvcAutoConfiguration类源码
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
//WebMVC自动配置类
public class WebMvcAutoConfiguration {
......
}
- 查看WebMvcAutoConfigurationAdapter类源码
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class,
org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
@Order(0)
//WebMvc的自动配置适配器
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
......
}
- 查看EnableWebMvcConfiguration类
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
//WebMvc的能够自动配置
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
......
}
- 查看addResourceHandlers方法源码
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
//如果静态资源的东西已经被自定义了,直接返回
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
ServletContext servletContext = getServletContext();
addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (servletContext != null) {
registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));
}
});
}
- 再次查看WebMvcAutoConfigurationAdapter类
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class,
org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
@Order(0)
//WebMvc的自动配置适配器
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
......
}
- 查看WebMvcProperties类源码
//封装绑定了配置文件spring.mvc的所有属性
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
......
//静态资源的路径
private String staticPathPattern = "/**";
}
- 在配置文件中测试
spring.mvc.static-path-pattern=
2.分析addResourceHandlers方法
2-1 查看addResourceHandlers类源码
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
//如果静态资源的东西已经被自定义了,直接返回,即默认的资源失效
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
ServletContext servletContext = getServletContext();
//添加静态资源到/webjars/**中,位置在classpath:/META-INF/resources/webjars/
addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (servletContext != null) {
registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));
}
});
}
2-2 什么是webjars?
2-3 导入webjars形式的jquery
<!--导入jquery-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.6.0</version>
</dependency>
2-4 查看webjars形式jquery位置
2-5 使用浏览器访问jquery.js文件
2-5 分析addResourceHandlers方法
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
//如果静态资源的东西已经被自定义了,直接返回,即默认的资源失效
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
ServletContext servletContext = getServletContext();
//添加静态资源到/webjars/**中,位置在classpath:/META-INF/resources/webjars/
addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
//getStaticPathPattern:获取静态资源的路径
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (servletContext != null) {
registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));
}
});
}
2-6 结论
在SpringBoot中,我们可以使用以下方式处理静态资源:
除了 /**"外,还有classpath:/METAINF/resources/;以及classpath:/resources/、classpath:/static/ 和 classpath:/public/
- webjars路径下:访问方式 —> localhost:8080/webjars/
- public、static、/**、resources路径下:访问方式 —> localhost:8080 /
5.2.4 静态资源位置优先级测试
1.存放classpath:/public/路径下
1-1 文件存放位置
1-2 编写JS代码
Hello,JavaScript
1-3 测试结果
2.存放classpath:/resources/路径下
1-1 文件存放位置
1-2 编写JS代码
Hello,Spring
1-3 测试结果
3.使用classpath:/public/ 和classpath:/resources/ 存放同名文件
3-1 文件存放位置
3-2 编写JS代码
- 存放classpath:/public/路径下:
Hello,JavaScript
- 存放classpath:/resources/ 路径下:
Hello,Spring
3-3 测试结果
3-4 测试结论
结果:存放在 classpath:/resources/ 下比classpath:/public/ 下优先级高
4.存放classpath:/static/路径下
4-1 文件存存放位置
- 在static文件夹下存放hello1.js同名文件
4-2 编写JS代码
- classpath:/public/ 路径下:
Hello,JavaScript
- classpath:/resources/ 路径下:
Hello,Spring
- classpath:/static/ 路径下:
Hello,SpringMVC
4-3 测试结果
4-4 测试结论
结论:存放在 classpath:/resources/ 下也比classpath:/static/ 下优先级高
5.使用classpath:/public/ 和 classpath:/static/ 存放同名路径
5-1 删除classpath:/resources/ 下文件
5-2 编写JS文件
- 存放 classpath:/public/ 路径下:
Hello,JavaScript
- 存放 classpath:/static/ 路径下:
Hello,SpringMVC
5-3 测试结果
5-4 测试结论
结论:存放 classpath:/static/ 路径下优先级比存放 classpath:/public/ 下高
6.自定义资源访问路径
6-1 编写application.properties文件
# 在配置文件下自定义资源访问路径
spring.mvc.static-path-pattern=/hello/**
6-2 测试结果
6-3 测试结论
结论:如果在配置文件下自定义资源访问路径,那么存放在resources、static以及public下的文件都会失效
7.使用总结
优先级比较结果:classpath:/resources/ > classpath:/static/ (默认使用) > classpath:/public/
资源存放总结:
- 因此,我们一般在public下存放一些公共资源,大家都会访问到的JS文件<
- 在static下放一些静态资源,比如图片等
- 而在resources下会放一些上传(upload) 的文件
5.2.5 定制首页
1.查看WebMvcAutoConfiguration类源码
- 我们查看WebMvcAutoConfiguration自动配置类源码后发现,其中有个getIndexHtml方法来获取html页面资源
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
//WebMVC自动配置类
public class WebMvcAutoConfiguration {
......
//欢迎页控制映射
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
......
}
......
private Resource getIndexHtml(String location) {
return getIndexHtml(this.resourceLoader.getResource(location));
}
//获取页面资源
private Resource getIndexHtml(Resource location) {
......
}
}
- 接下来我们来继续查看getIndexHtml方法,探究其是如何来获取html页面资源的
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
//WebMVC自动配置类
public class WebMvcAutoConfiguration {
......
//欢迎页控制映射
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
//获取欢迎页面控制器映射(WelcomePageHandlerMapping)对象
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
//获得静态路径参数
this.mvcProperties.getStaticPathPattern());
//设置拦截器
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
//返回到欢迎页面映射
return welcomePageHandlerMapping;
}
......
//获取页面资源
private Resource getIndexHtml(String location) {
//通过定位来返回获取到的html页面
return getIndexHtml(this.resourceLoader.getResource(location));
}
//获取页面资源
private Resource getIndexHtml(Resource location) {
try {
//创建index.html页面资源
Resource resource = location.createRelative("index.html");
//判断资源是否存在或者获取到的资源UR链接是否为空
if (resource.exists() && (resource.getURL() != null)) {
//如果满足条件则返回资源
return resource;
}
}
catch (Exception ex) {
}
//如果不满足条件就返回空
return null;
}
}
2.存放主页到根目录下
2-1 主页存放位置
2-2 编写index.html主页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>
2-3 主页访问测试
2-4 测试结论
结论:直接存放在根目录不能直接访问!
3.存放主页到templates文件下
3-1 主页存放位置
3-2 编写index.html主页
与2-2中的主页代码相同
3-3 编写IndexControlller控制类
package com.kuang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
//在templates目录下的所有页面,只能通过controller来跳转
//这个需要模板应请支持:thymeleaf
//使用@Controller注解来实现Controller接口
@Controller
public class IndexController {
//真实访问路径为:http://loclahost:8080/index
//使用@RequestMapping注解,设置请求映射路径
@RequestMapping("/goIndex")
public String index() {
//返回的视图逻辑名
return "index";
}
}
3-3 主页访问测试
- 直接访问主页
结果:访问失败,可能需要通过控制器来访问主页!
- 使用goIndex请求访问
结果:仍然访问失败,应该是为导入模板引擎 (thymeleaf) 的资源jar包所导致的 !
4.存放index.html主页到static文件下
4-1 主页存放位置
4-2 编写index.html主页
与2-2主页代码相同
4-3 编写IndexController控制类
与3-3控制类代码相同
4-4 测试结果
-
直接访问主页
结果:访问成功!同时也印证了之前说的,可以把大家都会访问的资源放在static文件下
-
通过goIndex请求访问
结果:访问主页失败!应该是视图逻辑名拼接出了问题,因为我们并不清楚SpringBoot中视图解析器如何处理返回的视图逻辑名
5.定制图标
5-1 图标存放位置
5-2 编写application.properties配置文件
# 关闭默认图标
spring.mvc.favicon.enabled=false
新版SpringBoot已经不支持favicon了,不过可以在html的link标签中实现
5-3 编写index.html主页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="icon" href="/favicon.ico">
</head>
<body>
<h1>首页</h1>
</body>
</html>
5-4 测试结果
结果:定制图标成功!
好了,今天的有关 SpringBoot基础学习之SpringBoot Web开发(上篇) 的学习就到此结束啦,欢迎小伙伴们积极学习和讨论,喜欢的可以给蜗牛君点个关注,顺便来个一键三连,我们下期见,拜拜啦!
参考视频链接:https://www.bilibili.com/video/BV1PE411i7CV(【狂神说Java】SpringBoot最新教程IDEA版通俗易懂)