前言:和之前学习的ssm类似,现在开始了SpringBoot的学习,其实SpringBoot是用来简化Spring应用的初始搭建以及开发过程的框架,简洁了配置,最大的特点就是自动配置,接下来我们开始学习使用SpringBoot的Web开发。
此文为跟随狂神学习笔记,记录学习过程,如有逻辑错误或理论有误,欢迎在评论区指正。如果觉得有用,一键三连更是对我最大的鼓励💬🥂🥂
Web静态资源导入探究
SpringBoot中,SpringMVC的web配置都WebMvcAutoConfiguration 这个配置类里面(插一嘴,在SpringBoot中基本上所有的方法都是xxxConfiguration里面配置的,所以一同百通,不懂的地方就去扒源码);我们可以去看看 WebMvcAutoConfigurationAdapter 中有很多配置方法。有一个方法:addResourceHandlers 添加资源处理
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
registration.addResourceLocations(resource);
}
});
}
读一下源代码:比如所有的 /webjars/** , 都需要去 classpath:/META-INF/resources/webjars/ 找对应的资源。
1、什么是webjars静态资源呢?
Webjars本质就是以jar包的方式引入我们的静态资源 , 我们以前要导入一个静态资源文件,直接导入即可。
一般是要使用jQuery,我们只要要引入jQuery对应版本的pom依赖即可!
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.4.1</version>
</dependency>
导入完毕,查看webjars目录结构,并访问Jquery.js文件!
访问:只要是静态资源,SpringBoot就会去对应的路径寻找资源,我们这里访问:http://localhost:8080/resources/webjars/jquery/3.4.1/jquery.js
2、静态资源映射规则
那我们项目中要是使用自己的静态资源该怎么导入呢?我们看下一行代码。
我们去找staticPathPattern发现第二种映射规则 :/** , 访问当前的项目任意资源,它会去找 resourceProperties 这个类,我们可以点进去看一下分析:
public String[] getStaticLocations() {
return this.staticLocations;
}
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
};
由源码可以得出结论,我们可以将静态资源放在四个目录下:
"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
我们可以在resources根目录下新建对应的文件夹,都可以存放我们的静态文件。
比如我们访问 http://localhost:8080/1.js , 他就会去这些文件夹中寻找对应的静态资源文件。
3、自定义静态资源路径
我们也可以自己通过配置文件来指定,哪些文件夹是需要我们放静态资源文件的,在application.properties中配置。
spring.resources.static-locations=classpath:/coding/,classpath:/cetus/
【注意】一旦自己定义了静态文件夹的路径,原来的自动配置就都会失效了!
4、首页处理
在学习springMVC中,当项目启动时,会自动跳转到index页面,那再这里的学习也一样,只是不同的是我们需要自己再url输入框中输入。我们来看一下源码。
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {
if (welcomePage != null && "/**".equals(staticPathPattern)) {
logger.info("Adding welcome page: " + welcomePage);
setRootViewName("forward:index.html");
}
else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
logger.info("Adding welcome page template: index");
setRootViewName("index");
}
}
可以看到,欢迎页、静态资源文件夹下的所有 index.html 页面;被 /** 映射。比如我访问 http://localhost:8080/ ,就会找静态资源文件夹下的 index.html,新建一个 index.html ,在我们上面的3个目录中任意一个。
自定义网站的图标:
与其他静态资源一样,Spring Boot在配置的静态内容位置中查找 favicon.ico。如果存在这样的文件,它将自动用作应用程序的favicon。
1、关闭SpringBoot默认图标
#关闭默认图标
spring.mvc.favicon.enabled=false
2、自己放一个图标在静态资源目录下,我放在 public 目录下
3、清除浏览器缓存!刷新网页,发现图标已经变成自己的了!
thymeleaf模板引擎
前端交给我们的页面,是html页面。如果是我们以前开发,我们需要把他们转成jsp页面,jsp好处就是当我们查出一些数据转发到JSP页面以后,我们可以用jsp轻松实现数据的显示,及交互等。
jsp支持非常强大的功能,包括能写Java代码,但是呢,我们现在的这种情况,SpringBoot这个项目首先是以jar的方式,不是war,而且我们用的还是嵌入式的Tomcat,所以呢,他现在默认是不支持jsp的。
那不支持jsp,如果我们直接用纯静态页面的方式,那给我们开发会带来非常大的麻烦,那怎么办呢?
SpringBoot推荐你可以来使用模板引擎:
模板引擎,我们其实大家听到很多,其实jsp就是一个模板引擎,还有用的比较多的freemarker,包括SpringBoot给我们推荐的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他们的思想都是一样的,什么样一个思想呢我们来看一下这张图:
模板引擎的作用就是我们来写一个页面模板,比如有些值呢,是动态的,我们写一些表达式。而这些值,从哪来呢,就是我们在后台封装一些数据。然后把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容给我们写出去,这就是我们这个模板引擎,不管是jsp还是其他模板引擎,都是这个思想。首先,我们来看SpringBoot里边怎么用。
1、引入Thymeleaf
怎么引入呢,对于springboot来说,什么事情不都是一个start的事情嘛,我们去在项目中引入一下。可以再thymeleaf官网去找,找到对应的pom依赖:可以适当点进源码看下本来的包!
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
Maven会自动下载jar包,我们可以去看下下载的东西。
2、Thymeleaf分析
前面呢,我们已经引入了Thymeleaf,那这个要怎么使用呢?我们首先得按照SpringBoot的自动配置原理看一下我们这个Thymeleaf的自动配置规则,在按照那个规则,我们进行使用。我们去找一下Thymeleaf的自动配置类:ThymeleafProperties
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
}
我们可以在其中看到默认的前缀和后缀!我们只需要把我们的html页面放在类路径下的templates下,thymeleaf就可以帮我们自动渲染以html结尾的页面了。使用thymeleaf什么都不需要配置,只需要将他放在指定的文件夹下即可!
3、Thymeleaf语法学习
学习一门语法,最直接的就是跟着官方文档来学习。Thymeleaf 官网:https://www.thymeleaf.org/ , 简单看一下官网!我们去下载Thymeleaf的官方文档!
做个测试练习一下:
- 修改请求,增加数据传输;
@RequestMapping("/t1")
public String test1(Model model){
model.addAttribute("msg","<h1>Hello,Thymeleaf</h1>");
return "test";
}
- 我们要使用thymeleaf,需要在html文件中导入命名空间的约束,方便提示。我们可以去官方文档的#3中看一下命名空间拿来过来:
xmlns:th="http://www.thymeleaf.org"
- 我们去编写下前端页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>title</title>
</head>
<body>
<h1>测试页面</h1>
<div th:text="${msg}"></div>
<div th:utext="${msg}"></div>
</body>
</html>
- 启动成功!
OK,入门搞定,我们来认真研习一下Thymeleaf的使用语法!
MVC自动装配原理
在进行项目编写前,我们还需要知道一个东西,就是SpringBoot对我们的SpringMVC还做了哪些配置,包括如何扩展,如何定制。
地址 :https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration
Spring MVC Auto-configuration
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
The auto-configuration adds the following features on top of Spring’s defaults:
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
Support for serving static resources, including support for WebJars
Automatic registration of Converter, GenericConverter, and Formatter beans.
Support for HttpMessageConverters (covered later in this document).
Automatic registration of MessageCodesResolver (covered later in this document).
Static index.html support.
Custom Favicon support (covered later in this document).
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration
(interceptors, formatters, view controllers, and other features), you can add your own
@Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide
custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or
ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.
我们来仔细对照,看一下它怎么实现的,它告诉我们SpringBoot已经帮我们自动配置好了SpringMVC,然后自动配置了哪些东西呢?
1、ContentNegotiatingViewResolver 内容协商视图解析器
自动配置了ViewResolver,就是我们之前学习的SpringMVC的视图解析器;即根据方法的返回值取得视图对象(View),然后由视图对象决定如何渲染(转发,重定向)。我们去看看这里的源码:我们找到 WebMvcAutoConfiguration , 然后搜索ContentNegotiatingViewResolver。找到如下方法!
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
我们可以点进这类看看!找到对应的解析视图的代码。
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
if (requestedMediaTypes != null) {
List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
if (bestView != null) {
return bestView;
}
}
}
里面有一个getCandidateView()的方法,获取候选的视图,接着点进源码,发现里面是使用iterator迭代的方式进行遍历所有的视图,所以得出结论:ContentNegotiatingViewResolver 这个视图解析器就是用来组合所有的视图解析器的 。
我们再去研究下他的组合逻辑,看到有个属性viewResolvers,看看它是在哪里进行赋值的!
protected void initServletContext(ServletContext servletContext) {
Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
ViewResolver viewResolver;
if (this.viewResolvers == null) {
this.viewResolvers = new ArrayList(matchingBeans.size());
}
}
既然它是在容器中去找视图解析器,我们是否可以猜想,我们就可以去实现一个视图解析器了呢?
2、修改SpringBoot的默认配置
这么多的自动配置,原理都是一样的,通过这个WebMVC的自动配置原理分析,SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置@bean),如果有就用用户配置的,如果没有就用自动配置的;如果有些组件可以存在多个,比如我们的视图解析器,就将用户配置的和自己默认的组合起来!
扩展使用SpringMVC 官方文档如下:
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.
我们要做的就是编写一个@Configuration注解类,并且类型要为WebMvcConfigurer,还不能标注@EnableWebMvc注解;我们去自己写一个;我们新建一个包叫config,写一个类MyMvcConfig。
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/test").setViewName("test");
}
}
这就类似springMVC中的controller层的代码,实际上再企业开发中经常需要编写的大多是controller层的这种处理前端页面返回的代码。
我们可以去分析一下原理:
-
WebMvcAutoConfiguration 是 SpringMVC的自动配置类,里面有一个类WebMvcAutoConfigurationAdapter -
这个类上有一个注解,在做其他自动配置时会导入:@Import(EnableWebMvcConfiguration.class) -
我们点进EnableWebMvcConfiguration这个类看一下,它继承了一个父类:DelegatingWebMvcConfiguration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
}
-
我们可以在这个类中去寻找一个我们刚才设置的viewController当做参考,发现它调用了一个 protected void addViewControllers(ViewControllerRegistry registry) {
this.configurers.addViewControllers(registry);
}
-
我们点进去看一下 public void addViewControllers(ViewControllerRegistry registry) {
Iterator var2 = this.delegates.iterator();
while(var2.hasNext()) {
WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
delegate.addViewControllers(registry);
}
}
所以得出结论:所有的WebMvcConfiguration都会被作用,不止Spring自己的配置类,我们自己的配置类当然也会被调用;
3、全面接管SpringMVC
If you want to take complete control of Spring MVCyou can add your own @Configuration annotated with @EnableWebMvc.
全面接管即:SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己去配置!只需在我们的配置类中要加一个@EnableWebMvc。再此时候,所有SpringBoot里自动配置的类都失效了,所以不推荐使用全面接管SpringMVC。扒开源码总结一句话:@EnableWebMvc将WebMvcConfigurationSupport组件导入进来了;而导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能!
在SpringBoot中会有非常多的扩展配置,只要看见了这个,我们就应该多留心注意~
4、配置一个基本SpringMVC
在写与前端打交道的代码层,首先从controller层入手,SpringBoot也不例外,在前端点击提交from表单时,来到了/user/login这个控制层。
然后编写对应的controller层并增加一些判断,注意此处使用@Controller注解,并且添加了一个session,确保会话有效。
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password, Model model, HttpSession session){
if (!StringUtils.isEmpty(username)&& "123456".equals(password)){
session.setAttribute("loginUser",username);
return "redirect:/main.html";
}else {
model.addAttribute("msg","用户名或者密码错误");
return "index";
}
}
}
此时可以访问,并登录进来,但是存在一些不安全性,在首次访问进来之后推出,再次访问dashboard.html时,并不会被拦截,需要写一个拦截器去拦截恶意请求。
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;
}
}
}
这里面的msg也大有用处,在前端展示时可以给出提示。
接下来最需要注意的一点,不论是配置一个国际化组件还是一个拦截器等,都需要在修改配置的MyMVCconfig类添加进去。
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/","/user/login","/css/**","/img/**","/js/**");
}
}
页面国际化
有的时候,我们的网站会去涉及中英文甚至多语言的切换,这时候我们就需要学习国际化了!先在IDEA中统一设置properties的编码问题!
1、配置文件的编写
-
我们在resources资源文件下新建一个i18n(internationalization)目录,存放国际化配置文件 -
建立一个login.properties文件,还有一个login_zh_CN.properties和login.en_US.properties;发现IDEA自动识别了我们要做国际化操作;文件夹变了! -
接下来,我们就来编写配置,我们可以看到idea下面有另外一个视图(需要安装一个插件) -
这个视图我们点击 + 号就可以直接添加属性了;我们新建一个login.tip,可以看到边上有三个文件框可以输入 -
然后去查看我们的配置文件;
2、配置文件生效探究
我们去看一下SpringBoot对国际化的自动配置!这里又涉及到一个类:MessageSourceAutoConfiguration。里面有一个方法,这里发现SpringBoot已经自动配置好了管理我们国际化资源文件的组件 ResourceBundleMessageSource;
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
messageSource.setBasenames(StringUtils
.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
我们真实 的情况是放在了i18n目录下,所以我们要去配置这个messages的路径。
spring.messages.basename=i18n.login
3、配置页面国际化
去前端页面获取国际化的值,通过#{} 的取值方式。我们测试一下:
我们可以去启动项目,访问一下http://localhost:8080,发现已经自动识别为中文的了!
在Spring中有一个国际化的Locale (区域信息对象),里面有一个叫做LocaleResolver (获取区域信息对象)的解析器!(可查看源码)那假如我们现在想点击链接让我们的国际化资源生效,就需要让我们自己的Locale生效!我们去自己写一个自己的LocaleResolver,可以在链接上携带区域信息!修改一下前端页面的跳转连接:
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
我们去写一个处理的组件类!
package com.cetus.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 language = request.getParameter("l");
Locale locale = Locale.getDefault();
if (!StringUtils.isEmpty(language)){
String[] split = language.split("_");
locale = new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
【注意】为了让我们的区域化信息能够生效,我们需要再配置一下这个组件!在我们自己的MyMvcConfig下添加bean;
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
重新启动之后,就能实现国际化的翻译了。
|