0%

Spring-Boot入门

前言

学完Spring和SpringMVC,直接就开始学这个框架,不多BB。

传统的SSM框架的劣势是什么?其实也不是很劣势啦,只是配置有那么一内内繁琐(其实我个人觉得真的只是一点点繁琐而已),而springboot则是大大简化了这些繁琐。

Quick Start

直接用idea生成一个最最普通的maven结构,然后上 https://start.spring.io/ 获取到依赖关系,然后复制粘贴进自己的项目,稍等一会依赖就搞定了。当然你也可以使用idea自带的工具来生成,也很方便。

然后只需要最最简单的代码就可以搞定了:

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootApplication
@RestController
public class Demo {
public static void main(String[] args) {
SpringApplication.run(Demo.class, args);
}

@GetMapping("/hello")
@ResponseBody
public String hello() {
return "Hello World";
}
}

直接运行就可以跑起来了,甚至连Tomcat都不需要自己配。

最后部署的话,只需要生成jar包即可,到目标服务器中运行这个jar包就完成啦。果然是轻松简单的很。

稍微探究一下

依赖关系

1
2
3
4
5
6
7
8
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

再向上是spring-boot-dependencies,在这里就有全部的依赖管理了,可以直接使用

那么如果我不想使用springboot提供的呢,那么可以直接在自己的项目的pom中直接指定(因为就近原理):

1
2
3
<properties>
<mysql.version>5.1.47</mysql.version>
</properties>

这样就会覆盖掉自动版本裁定了。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

仔细观察会发现上面两个都是spring-boot-starter-*,称为场景启动器,里面有web需要的东西。String boot将这些常用的功能场景都抽取了出来,只需要导入starter,那么项目中就会都存在这些依赖了。

一般官方的启动器都是以spring-boot-starter开头的,而一般第三方的都是以这个结尾。

所有的这些starter,底层依赖的都是spring-boot-starter,这个之后会详细展开讲。

自动配置

springboot之所以可以直接启动,因为它为我们配置好了一些东西,比如:

  • 内置的Tomcat容器,引入了相关的依赖,并且进行了配置(之后详细看)
  • 配置好了SpringMVC,也是引入了相关的依赖,并且在容器中配置了相关的组件,比如DispatcherServlet这个类等。
  • 配置好了包结构,自带了包扫描功能,主程序所在的包及其子包,所有的组件都会被扫描并加入到容器中。这个可以通过scanBasePackages来进行指定,或者可以通过@ComponentScan来指定。
  • 各种配置都有默认配置,这些默认配置都是映射到某个类上面的。也就是配置文件会对应一个类,而这个类的对象会在容器中存在。
  • 所有配置按需加载。只有引入了对应的场景,才会开启对应的自动配置。所有自动配置功能其实都是通过spring-boot-autoconfigure来实现的。
  • 其他种种。

主程序

@SpringBootApplication标志着这个类是总配置类。打开它的源码查看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)

可以看到其实这个注解非常的麻烦,慢慢分析:

  • 首先是@SpringBootConfiguration,这个从名字就知道这个注解是用来注解配置类的,这个注解在深入,其实就是@Configuration这个注解。需要注意配置类也是容器中的一个组件。
  • @EnableAutoConfiguration开启自动配置功能,把我们以前需要配置的东西,让spring boot帮我们配置。
    • 其底部又包括@AutoConfigurationPackage自动配置包,底层用的是@Import,给容器中导入一个组件。debug追踪一下可以发现,其实就是导入主配置类所在的包,所以记得要把你要导入的组件(controller、service等)放到配置类的同级或者对应的子包下。
    • 还有一个@Import({AutoConfigurationImportSelector.class}),从字面意思就可以知道,是导入哪些组件的选择器。跟踪源代码可以发现,是需要导入组件的全类名。会给容器中导入非常非常多的自动配置类,而这些自动配置类就能够给容器中导入该场景需要的所有组件,并配置好这些组件。其本质上就是从META-INF/spring.factories中获取了EnableAutoConfiguration的值。image-20200410002032401

好了初步探究到这里就差不多了。

容器功能

springboot本质上还是一个spring容器,那我们首先来看看之前基础的spring是怎么做的。

spring的话我们需要在类路径下创建好xml,并且配置好各种bean。像这样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user" class="cn.chenlangping.learnspringboot.bean.User">
<property name="name" value="Zhangsan"/>
<property name="age" value="11"/>
</bean>

<bean id="pet" class="cn.chenlangping.learnspringboot.bean.Pet">
<property name="name" value="tomcat"/>
</bean>
</beans>

如果你之前的项目是spring的,然后想切换到springboot,可以直接使用一个ImportResource把这个对应的文件制定好就可以了。

导入组件

springboot则相对来说要变通很多——我们一般是在config包下,创建对应的配置类,这个类就相当于对应的配置文件,配置类中的bean对应的就是一个一个方法,返回值就是对应的bean对象,而方法名就是对象的id。

1
2
3
4
5
6
7
8
@Configuration
public class MyConfig {

@Bean(name = "new Id")
public User user01() {
return new User("zhangsan", 190);
}
}

上面这段代码会为容器增加两个组件,一个是MyConfig类的对象(更精确的说,是被spring cglib增强过后的对象),因为配置类自身也是一个组件;还有一个是User类的对象,而且是单实例对象。

springboot2之后新增了一个proxyBeanMethods,默认是true。因为上面说了配置类本身也是一个组件,那么在获取了这个组件之后,我们可以调用其方法创建对象。这个对象仍然是从容器中获取的,而且不论多少次都是从容器中获取的,那么就一直是同一个对象。也就是这个属性的本身含义,代理bean方法,spring容器代理了这个方法,导致其一直从容器中获取相同的对象。

但是如果指定成了false之后,那么容器中的MyConfig对象就不再是被spring增强的对象了,那么调用它的方法所创建出来的User对象自然不是单实例的了。为什么会有这样子的设计,就是因为在配置类中,方法之间可能需要互相调用来创建不同的对象,如果是true的话就一直是相同的对象。如果是false就可以跳过容器中的检查,加速程序。

@Import

直接导入对应的类。会自动调用对应类的无参构造器创建出对应的对象(默认组件的名字是全限定类名),并放入到容器中。

@Conditional

当满足某些条件的时候,才会给容器中放入组件。注意一般和@Bean一起使用。

配置绑定

一般我们会在我们的配置文件中配置好对应的信息,然后通过读取这个配置文件,生成对应的java类,然后再通过这个java类来进行操作。

那么我们之前是如何做到的呢?

1
2
3
1. 准备好一个外部的文件,如config文件
2. 将这个文件读入并且找到对应的k-v对,进行读取
3. 利用读取的信息生成对应的javabean

但是这样子比较麻烦,springboot为我们提供了一个很好的注解,叫@ConfigurationProperties(当然你也必须将其放入到容器中才能生效),然后可以直接到对应的配置文件下面,写好对应的配置信息即可。

当然我们一般是会有一个专门的配置类进行配置的,所以实际中一般是到配置类上面去,写上@EnableConfigurationProperties(xxx.class)来将对应的类加入容器,并开启配置绑定功能。

自动配置原理入门

在主程序上有一个注解,这个注解本质上是三个注解的合集,即:

1
2
3
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

@SpringBootConfiguration

点进去非常容易,其实本质上就是一个@Configuration,也就是主程序所在的类,本质上也是一个配置类,所以你可以直接在里面写对应的bean,当然非常不推荐这么做。

@ComponentScan

显然是用来做包扫描的,并不是重点,可以先跳过。

@EnableAutoConfiguration

看名字也知道,这个注解才是真正的核心所在。它本质上是两个注解的合成,分别是@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)。而第一个注解本质上是@Import(AutoConfigurationPackages.Registrar.class),所以其实本质上,就是为容器新增了两个组件而已。

AutoConfigurationPackages.Registrar

那首先来看看Registrar这个静态内部类的代码:

1
2
3
4
5
6
7
8
9
10
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}

注意第四行,获取了metadata的包名,而我们的这个注解是标注在主程序上的,相当于把整个主程序所在的包的所有组件都进行了注册。这也解释了为什么包扫描的默认是主程序所在的包下面。

AutoConfigurationImportSelector

核心的方法在于:

1
2
3
4
5
6
7
8
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

显然是使用了getAutoConfigurationEntry()方法来获取元数据,然后根据元数据把对应的组件导入到容器中了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

这个方法也很简单,仔细看其实就是对configurations进行了一堆的处理,然后得到最终需要的。

1
2
3
4
5
6
7
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass()
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}

跟踪下去,其本质上其实就是获取了一个META-INF下面的spring.factories文件下的东西。而这个其实就是到spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面的META-INF/spring.factories下获取。

虽然这些自动配置类都写好了,但是并不是真正加入到容器中。靠什么?之前讲过的@ConditionalOnClass这类的注解。

配置文件

之所以我们需要研究怎么来写配置文件,一来是为了让我们能够更好的使用配置文件,二来也是希望能够通过自己使用配置文件,来搞清楚springboot是如何自动化进行配置的。

虽然springboot简化了操作,但是必要的配置文件还是并不可少的。springboot使用这两个配置文件:application.ymlapplication.properties,这个配置文件就是用来修改springboot的默认值。properties这个文件应该非常熟悉了,下面简单介绍一下yaml这种文件格式。

YAML

yaml的核心是数据为中心,所以配置就是键值对,用冒号分割,冒号后面必须加一个空格。缩进相同的代表属于同一级。

字面量

  • 数字,直接写
  • 字符串,直接写;如果用双引号引起来,特殊字符就是特殊字符,例如"\n"就是换行;如果单引号引起来,那么里面是什么东西,它会原封不动给你输出出来。

对象

1
2
3
4
5
person: 
name: 张三
age: 13
---
person: {name: 张三,age: 13}

数组

1
2
3
4
5
6
animal: 
- dog
- cat
- pig
---
animal: [dog,cat,pig]

通过实际演示使用一下就行了。首先定义一个Person类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Person {

private String name;
private Integer age;
private Date birthday;
private Boolean isMan;

private Map<String,Object> maps;
private List<Object> lists;
// dog只有名字和年龄
private Dog dog;

// getter setter和toString()省略
}

然后对应的yaml文件即为:

1
2
3
4
5
6
7
8
9
10
person:
name: 张三
age: 14
birthday: 1990/01/01
maps: {a: 1,b: 3,c: lala}
lists: [1,2,3]
dog:
name: 旺旺
age: 1
man: false

上面请特别特别注意一下最后一行,是man而不是isMan哦。因为实际是根据setter的函数名字来决定的,而idea最后设置的setter是setMan,所以是man。(如果还是有疑问,可以妙用idea的提示功能,万无一失)

最后回到类上面,声明一下即可。

1
2
@Component
@ConfigurationProperties(prefix = "person")

注意还需要加入一个依赖:

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

然后就大功告成了。相比于@Value一个一个指定,显然这个一次性导入比较方便。

Properties

1
2
3
4
5
6
7
8
9
10
person.age=18
person.birthday=1993/08/08
person.dog.name=旺旺
person.dog.age=1
person.lists=1,2,3
person.man=false
person.name=张三
person.maps.k1=111
person.maps.k2=你好
person.maps.k3=abc

如果出现中文乱码的情况,记得把idea的file encoding有个Transparent native-to-ascii conversion,勾选上即可。这个的意思就是把你输入的所有字符转换成Unicode序列码保存,即变成了下面这个样子:

1
2
3
4
5
6
7
8
9
10
person.age=18
person.birthday=1993/08/08
person.dog.name=\u65FA\u65FA
person.dog.age=1
person.lists=1,2,3
person.man=false
person.name=\u5F20\u4E09
person.maps.k1=111
person.maps.k2=\u4F60\u597D
person.maps.k3=abc

最后当Java去读取properties时,也会将自动将\uxxx的Unicode转成对应的char,不会出现乱码问题。

注入值校验

在类上面使用@Validated,则可以启用注入值校验,它自带了一些校验规则,可以直接使用。

ImportResource

由于springboot里面没有Spring的配置文件,所以需要这个注解来导入spring的配置文件并且生效。当然springboot本身是推荐使用全注解的方式来实现的。

多配置

我们可能针对不同的环境(开发、测试、生产等)或者根据不同的语言(中文、英文等)需要实现不同的配置文件,那么可以通过类似 application-{profile}.properties来实现;如果用yml的话,可以用多文档块的方式直接配置。

在使用的时候,当然需要直接指定需要激活哪个配置文件:

  • 直接在配置文件中指定:spring.profiles.active=dev
  • 在启动的时候在命令行中指定:java -jar …jav –spring.profiles.active=dev;
  • 在虚拟机参数中加入:-Dspring.profiles.active=dev

配置文件的加载顺序

由于springboot是约定大于配置,所以默认会从下面的位置加载文件名为application.properties或者的application.yml文件:

  • file:./config/

  • file:./

  • classpath:/config/

  • classpath:/

优先级从高到低,需要注意的是,高优先级会覆盖掉低优先级的配置,且不会因为在某个位置进行了而短路掉其它的配置,利用这个特性我们就可以实现配置的互补了。

当然我们还可以通过在启动项目的时候额外指定配置文件,这样就能够和之前的配置形成互补了。

外部配置

请参考:参考官方文档

自动配置的原理

首先我们在主配置类里面写了@SpringBootApplication,而这个里面即开启了自动配置的功能。然后就能通过配置文件来获得需要开启哪些xxAutoConfiguration类。下面以HttpEncodingAutoConfiguration这个类为案例来分析下自动配置的原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Configuration(
proxyBeanMethods = false
) // 说明这是一个配置类
@EnableConfigurationProperties({HttpProperties.class}) //把配置文件和这个类组合起来
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class}) // 判断当前的类是否是web应用,底层用的是@Conditional注解
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
) // 判断是不是有这个配置,不存在也同样生效。
public class HttpEncodingAutoConfiguration {

// 可以发现这个已经和配置文件进行了映射,所以已经加入了容器
private final Encoding properties;

public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}

@Bean
@ConditionalOnMissingBean // 容器中如果没有这个bean就加,有就不加了
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}

@Bean
public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
}

// 省略一个方法。
}

首先要做的是,判断下目前这个配置类它是否生效,生效就会给容器中加东西,加入的东西都是通过配置文件来获取的。

所以我们不难发现,springboot启动的时候,它其实是加载了各种各样的自动配置类,然后这些自动配置类会替我们加载组件,如果我们需要的组件已经被添加了,那直接拿来用就可以了。自动配置类增加组件的方法就是从properties中获取属性,而这些属性恰恰是配置文件里指定的。

日志

日志框架

框架主要分成两种:抽象类和实现类,抽象类包含了JCL、SLF4j以及Jboss-logging,而实现则有Log4j、Log4j2、Logback以及Java自带的JUL

springboot选择的是SLF4j+Logback,而spring使用的则是JCL。

SLF4j的使用

只需要导入抽象类和实现类的包,下面是示例代码:

1
2
3
4
5
6
7
8
9
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}

特别的简单。这里需要注意的是,由于日志框架参差不齐,有些项目用的是log4j,有的用的jul,我们就需要对它们进行转换,思路其实也特别简单,只需要一个转化的jar包,这个jar包会让那些使用log4j等jar包的以为自己用的就是原来的日志包,但是实际上用的却是logback包。

所以如果我们要同时使用各类框架,而这些框架的实现类各不相同,那么我们只需要:把原来的框架使用的jar排除,然后用中间的包来替换就可以了。

image-20200418133514067

使用

1
2
3
4
5
6
7
8
9
@Test
void contextLoads() {
Logger logger = LoggerFactory.getLogger(getClass());
logger.trace("这是trace日志...");
logger.debug("这是debug日志...");
logger.info("这是info日志...");
logger.warn("这是warn日志...");
logger.error("这是error日志...");
}

你会发现默认情况下,trace和debug是不会输出的,我们需要到application.properties文件中去配置,配置以logging开头。具体可以修改输出内容的格式、级别以及输出到哪里等。当然你也可以在类路径下面放上每个框架的对应配置文件,下面是springboot中各种对应的日志配置文件

Logging System Customization
Logback logback-spring.xml, logback-spring.groovy, logback.xml or logback.groovy
Log4j2 log4j2-spring.xml or log4j2.xml
JDK (Java Util Logging) logging.properties

如果用的是带-spring这个后缀的,则不是直接被对应的日志框架识别,而是先被springboot识别,这样就可以针对不同的profile来实现针对不同环境拥有不同的日志记录功能:比如我在开发的情况下需要事无巨细的记录下各种日志,而到了生产中只需要error和warn就可以了。

切换

每个日志框架其实都对应了自己的核心包,以及别的框架使用自己的包,只需要根据自己的需要对应的添加排除信息即可。

Web

SpringBoot创建web应用还是非常方便的,我们只需要在初始化的时候选好自己需要的模块,然后自己再加那么一内内的配置,然后就可以把重心放到业务代码上面了。

静态资源的映射

我们的一些js文件、css文件、图标等都属于静态文件,那springboot是如何加载它们的呢?利用的是WebMvcAutoConfiguration这个类。

访问jar包内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}

String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}

}
}

可以看到,在/webjars下面的所有资源,它都会去classpath:/META-INF/resources/webjars目录下找资源,而我们可以通过引入相对应的jar包,里面的资源就可以直接被我们所使用了,这些jar包可以通过这个网站查找。

比如我希望我的项目中用到jQuery,那么可以直接到该网站找到它,并且引入到项目里面:

1
2
3
4
5
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.0</version>
</dependency>

然后可以通过包的结构分析发现:

image-20200418160114470

这个和上面的类路径下面的路径完全一致,直接拿来用就可以了,现在只需要访问:http://localhost:8080/webjars/jquery/3.5.0/jquery.js 就可以看到结果了。

访问当前项目的资源

如果要访问当前项目的任何资源,那么会去这五个路径下寻找:

  • classpath:/META-INF/resources/
  • classpath:/resources/
  • classpath:/static/
  • classpath:/public/
  • /:当前项目的根路径

模板引擎

我们熟悉的jsp就是一个模板引擎,通过特定的占位符替换内容,组装出我们需要的html页面。

而在SpringBoot中,强力推荐的Thymeleaf,因为它的语法更简单,功能更强大;如果你对jsp比较熟悉,上手它还是比较容易的,这里就不多做介绍了。

SpringMVC自动配置

springboot本身自己自动配置好了springmvc,下面是一些详细信息:

  • 注册了ContentNegotiatingViewResolverBeanNameViewResolver这两个组件。
  • 解决了静态资源的位置
  • 解决了静态首页的访问
  • 配置了页面的图标
  • 自动注册了Converter, GenericConverter, Formatter
    • Converter:把String转成对应的类型
    • Formatter:格式化内容,比如日期文本转成Date
  • 等等等等

扩展使用SpringMVC

自动配置肯定是不尽如人意的,就需要我们手动来解决。我们可以创建一个类来继承WebMvcConfigurerAdapter,然后自己编写相对应的方法,就可以完成我们需要的“定制版”的springMVC了。

1
2
3
4
5
6
7
8
9
// 扩展SpringMVC的功能
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello").setViewName("success");
}
}

全面接管

我不需要springboot帮我做任何springMVC的功能,我希望我自己来,此时只需要在上面那个类上加上@EnableWebMvc,springboot配置的springmvc就会失效,此时你就需要自己来做组件了。原理就是springboot在自动配置springmvc的时候会判断下组件里有没有WebMvcConfigurationSupport这个类,有了就不启用;而@EnableWebMvc就是给容器中加入这个类(确切的说是它的子类)。

修改springboot的默认配置

虽然前面提到了我们可以全面接管springMVC,但是其实相辅相成才是最好的。此时我们就需要来修改它。

springboot在自动配置的时候,会先确认下有没有用户配置的,如果有就不自动配置了,没有才会自动配置。当然如果有些组件可以存在多份,那么springboot就会把自动配置的,和我们配置的组合起来用。

同时springboot也提供了很好的扩展性,比如xxxConfigurer帮我们进行扩展配置,而xxxCustomizer帮助我们进行定制配置。

简单的CRUD

默认访问首页,我们只需要把首页放在上面说的几个静态文件夹里面即可。PS:如果确定没问题,但是又频繁404,不妨重启下(或者maven和项目都清理下)。

国际化

我们首先需要编写好国际化的配置文件,springboot为我们自动配置了国际化资源文件,叫MessageSourceAutoConfiguration,然后使用特定的语法,就可以针对浏览器头部发送的Accept-Language信息来判断了。

针对国际化,我们有一个对象是Locale,而springboot里面有一个LocaleResolver来根据请求头的信息来使用对应的语言,所以我们只需要自己实现这个接口,并将它加入到容器中,我们就可以自己根据请求头中的内容来进行国际化内容的输出了。

数据库集成部分

Druid配置

首先是导入jdbc和Druid的jar包,然后只需要在application.properties里面写上:

1
2
3
4
5
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.url=jdbc:mysql://localhost/TEST
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

然后再配置一下即可使用:

1
2
3
4
5
6
7
8
@Configuration
public class DruidConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
}

这里特别注意一下,是spring.datasource.username而不是spring.datasource.data-username

Mybatis

Springboot中的mybatis除了需要对应的mybatis的starter外,还需要mysql的jdbc连接的依赖,以及配置好对应的jdbc的设置。

1
2
3
4
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

以及对应的四项即可:

1
2
3
4
spring.datasource.driverClassName= com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://ip_address:3306/df?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=password