Spring学习
提醒⏰:在开始之前,我们需要配置以下基本环境(还有文件的编码方式也要注意)
File »> Project Structcture(CTRL+ALT+Shift+S) »> Project »> Project SDK:
File »> Project Structcture(CTRL+ALT+Shift+S) »> Project »> Project language level
File »> Project Structcture(CTRL+ALT+Shift+S) »> Modules »> Sources »> Languages level
File »> Settings(CTRL+ALT+S) »> Build,Execution,Deployment »> Compiler »> Java Compiler »> Project bytecode version
File »> Settings(CTRL+ALT+S) »> Build,Execution,Deployment »> Compiler »> Java Compiler »>Per-module bytecode version
配置文件 <pom.xml>
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
<properties>
<project.build.sourceEncoding> UTF-8</project.build.sourceEncoding>
<java.version> 1.8</java.version>
</properties>
<!-- 首先配置一下基本的文件编码和Java版本 -->
<dependencies>
<dependency>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-starter-web</artifactId>
<version> 2.2.2.RELEASE</version>
</dependency>
<!-- 提供一些 mvc,aop 的依赖包,这是玩Spring-boot必备的依赖包 -->
<dependency>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-starter-parent</artifactId>
<version> 2.2.2.RELEASE</version>
<type> pom</type>
</dependency>
<!-- 这个依赖是帮助我们自动选择一些依赖最合适的版本,这个是玩Spring-Boot必备的 -->
<dependency>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-maven-plugin</artifactId>
<version> 2.1.7.RELEASE</version>
</dependency>
<!--该插件是将Springboot项目打包为可执行的jar包 -->
</dependencies>
Copy spring-boot-starter-parent 是版本仲裁者,所以我们在导入依赖时,可以省略一些依赖的版本号
spring-boot-starter-web web 模块的场景启动器组件,还有别的一些 starter 都是被 Sprint boot 抽取出来的,以后在项目里只需导入这些启动器就可以开箱即用
建立Controller & Starter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package top.jokeme;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Starter {
public static void main ( String[] args) {
SpringApplication. run ( Starter. class , args);
}
}
# ~~~~~~~~~~~~~~~~
package top.jokeme;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Controller {
@RequestMapping ( "/word" )
public String restr (){
return "<p style='color:red;text-align:center'>Awesome Java</p>" ;
}
}
Copy 这就是一个最简单的 SpringBoot 应用程序
1
2
3
4
5
6
7
8
9
10
11
12
package top.jokeme;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ComponentScan ( "top.jokeme.ayibe" )
@SpringBootApplication
public class Starter {
public static void main ( String[] args) {
SpringApplication. run ( Starter. class , args);
}
}
Copy @SpringBootApplication 该注解就标识着 Springboot 的主配置类,启动类,从这里的 main 方法开始执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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 }
)}
)
public @interface SpringBootApplication {
......
}
Copy 注: @import 是 SpringBoot の底层注解.其作用就是给容器导入组件
需要注意两个比较重要的注解
Registrar.class
1
2
3
4
@SpringBootApplication 中的
@EnableAutoConfiguration 中的
@AutoConfigurationPackage 中的
@Import({Registrar.class})
Copy 其中 Registrar.clss就是
1
2
3
4
5
6
7
8
9
10
11
12
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions ( AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages. register ( registry, ( new AutoConfigurationPackages. PackageImport ( metadata)). getPackageName ());
}
//这个方法就是将标记了@SpringBootApplication的类,及其子包的所有组件扫描到Spring容器里面去
public Set< Object> determineImports ( AnnotationMetadata metadata) {
return Collections. singleton ( new AutoConfigurationPackages. PackageImport ( metadata));
}
Copy AutoConfigurationImportSelector
1
2
3
@SpringBootApplication 中的
@EnableAutoConfiguration 中的
@Import({AutoConfigurationImportSelector.class})
Copy AutoConfigurationImportSelector 就是导入哪些组件的选择器,并且会将需要导入的组件以全类名の方式返回,然后这些组件就会被添加到容器之中;
在这个过程中,会导入非常多的自动配置类诸如 [ xxxAutoConfiguration ] 等,目的就是给容器导入这些场景所需的所有组件,并自动配置好
上 AutoConfigurationImportSelector 代码片段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public String[] selectImports ( AnnotationMetadata annotationMetadata) {
if (! this . isEnabled ( annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader. loadMetadata ( this . beanClassLoader );
AutoConfigurationImportSelector. AutoConfigurationEntry autoConfigurationEntry = this . getAutoConfigurationEntry ( autoConfigurationMetadata, annotationMetadata);
return StringUtils. toStringArray ( autoConfigurationEntry. getConfigurations ());
}
}
protected AutoConfigurationImportSelector. AutoConfigurationEntry getAutoConfigurationEntry ( AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (! this . isEnabled ( annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this . getAttributes ( annotationMetadata);
List< String> configurations = this . getCandidateConfigurations ( annotationMetadata, attributes);
configurations = this . removeDuplicates ( configurations);
Set< String> exclusions = this . getExclusions ( annotationMetadata, attributes);
this . checkExcludedClasses ( configurations, exclusions);
configurations. removeAll ( exclusions);
configurations = this . filter ( configurations, autoConfigurationMetadata);
this . fireAutoConfigurationImportEvents ( configurations, exclusions);
return new AutoConfigurationImportSelector. AutoConfigurationEntry ( configurations, exclusions);
}
}
Copy
1
2
3
4
5
protected List< String> getCandidateConfigurations ( AnnotationMetadata metadata, AnnotationAttributes attributes) {
List< String> configurations = SpringFactoriesLoader. loadFactoryNames ( this . getSpringFactoriesLoaderFactoryClass (), this . 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;
}
Copy 然后就用到了 SpringFactoriesLoader.loadFactoryNames
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
public static List< String> loadFactoryNames ( Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType. getName ();
return ( List) loadSpringFactories( classLoader). getOrDefault ( factoryTypeName, Collections. emptyList ());
}
private static Map< String, List< String>> loadSpringFactories ( @Nullable ClassLoader classLoader) {
MultiValueMap< String, String> result = ( MultiValueMap) cache. get ( classLoader);
if ( result != null ) {
return result;
} else {
try {
Enumeration< URL> urls = classLoader != null ? classLoader. getResources ( "META-INF/spring.factories" ) : ClassLoader. getSystemResources ( "META-INF/spring.factories" );
LinkedMultiValueMap result = new LinkedMultiValueMap();
while ( urls. hasMoreElements ()) {
URL url = ( URL) urls. nextElement ();
UrlResource resource = new UrlResource( url);
Properties properties = PropertiesLoaderUtils. loadProperties ( resource);
Iterator var6 = properties. entrySet (). iterator ();
while ( var6. hasNext ()) {
Entry<?, ?> entry = ( Entry) var6. next ();
String factoryTypeName = (( String) entry. getKey ()). trim ();
String[] var9 = StringUtils. commaDelimitedListToStringArray (( String) entry. getValue ());
int var10 = var9. length ;
for ( int var11 = 0; var11 < var10; ++ var11) {
String factoryImplementationName = var9[ var11];
result. add ( factoryTypeName, factoryImplementationName. trim ());
}
}
}
cache. put ( classLoader, result);
return result;
} catch ( IOException var13) {
throw new IllegalArgumentException( "Unable to load factories from location [META-INF/spring.factories]" , var13);
}
}
}
Copy SpringBoot 在启动的时候,主要就是从类路径下的 META-INF/factories 里获取 EnableAutoConfiguration 指定的值,并将这些值作为自动配置类导入容器中,然后自动配置类生效,帮我们实现自动配置的工作
这些都归功于:
spring-boot-autoconfigure-2.2.2.RELEASE.jar
注: Part 2.1的一些内容可能与 Part 3 的内容有冲突,以 Part 3 为准,Part 2.1有点老
自动配置原理
application.properties 配置文件里面都可以配置什么内容?
SpringBoot在启动的时候会加载主配置类,这个主配置类就是我们添加了注解 [ @SpringBootApplication ]的类
并且为这个类开启了自动配置功能 (继承至注解:@SpringBootApplication の注解 @EnableAutoConfiguration )
小技巧 :在使用Idea的时候,我们想要看导入的一些第三方代码的源码可以按 ALT+CTRL+鼠标点击你要看源码的 类/接口/注解…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.springframework.boot.autoconfigure;
import ......;
@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 }
)}
)
public @interface SpringBootApplication {
......
}
Copy EnableAutoConfigurationの作用:
使用 AutoConfigurationImportSelector 给容器导入相关组件
虽然没太看懂 selectImports 代码,但是我猜大概的意思是:
依赖 AutoConfigurationMetadataLoader 来导入 META-INF 下面的 properties 文件
详细解释可以看这个 Other+ 的文章
1
2
3
4
5
6
7
8
9
public String[] selectImports ( AnnotationMetadata annotationMetadata) {
if (! this . isEnabled ( annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader. loadMetadata ( this . beanClassLoader );
AutoConfigurationImportSelector. AutoConfigurationEntry autoConfigurationEntry = this . getAutoConfigurationEntry ( autoConfigurationMetadata, annotationMetadata);
return StringUtils. toStringArray ( autoConfigurationEntry. getConfigurations ());
}
Copy
所有可以在配置文件中配置的属性,都在xxxProperties类中
由于**@SpringBootApplication** 有一个默认的**ComponentScan**注解指定了扫描包の方式,所以在我们需要DIY扫包方式的时候就需要用 **@ComponentScan** 来指定我们需要扫描的包
eg: @ComponentScan(“top.jokeme.ayibe”) 指定扫描 ayibe 这个包….如果指定以后就不会 使用默认的扫描方式,只会扫描指定の包
YAML & Properties
yaml基本语法:
1
2
3
4
5
K : V
##############
K :
V1 : 443
V2 : sew
Copy yaml是大小写敏感的,并且需要注意缩进和空格
注意:yaml里面
" “会转义字符串,比如 \n 等
' ' 不会转义字符串,输入啥,输出啥
yamlの 对象
1
2
3
4
5
food :
name : apple
tasty : true
####################
food : {name: apple,tasty : true }
Copy yamlの数组
1
2
3
4
5
6
interesting :
- 13
- 14
- 15
#####################
interesting : [13 ,14 ,15 ]
Copy yaml文件 & 数据绑定
1
2
3
4
5
person :
sname : zhangsan
sage : 18
Sod : 'AnHui Provience'
Lovelydog : {age: 13,name : lisi}
Copy
1
2
3
4
5
6
7
8
9
10
11
12
package top.day2_yaml;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Component
@ConfigurationProperties ( prefix = "person" )
public class Person {
}
Copy 如果不出意外:
1
2
3
# localhost: 8080/ se
# 会出现以下效果
Person{ sname= ' zhangsan' , sage= 18, Sod= ' AnHui Provience' , Lovelydog= dog{ age= 13, name= ' lisi' }}
Copy 注意1:@Component 注解是帮助绑定值的一个注解,所以不能忘记添加该注解
注意2:这里可能需要导入以下依赖
1
2
3
4
5
6
<dependency>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-configuration-processor</artifactId>
<version> 2.3.0.RELEASE</version>
</dependency>
<!-- 配置文件处理模块,配置文件的数据绑定就依赖该模块 -->
Copy Properties
properties の用法大致与 yaml 差不多,但是比 yaml 费事一点点
1
2
3
4
5
6
person.sname=zhangsan
person.sage=18
person.Sod='AnHui Provience'
person.lovelydog.age=13
person.lovelydog.name=lisi
server.port=8088
Copy ∵ Properties 是大小写不敏感的
∴ person.age = person.Age
@Value
@Value 使用方法
1
2
3
4
5
6
7
8
package top.day3_atValue;
import org.springframework.beans.factory.annotation.Value;
public class human {
@Value ( "Person" )
String Typeof;
}
Copy @ConfigurationProperties & @Value の区别
@ConfigurationProperties
@Value
特征
批量注入配置文件里的属性
一个个指定
松散语法
?YES
?NO
SpEL
?NO
?YES
JSR303
?YES
?NO
复杂类型封装
?YES
?NO
eg: 复杂类型封装就是指只, map/list/…
我们在何时使用他们
如果我们只是某项业务逻辑中需要获取某个属性,推荐使用
如果我们是需要进行大规模配置文件,就要使用 @ConfigurationProperties
@PropertySource & @ImportResource
@PropertySource使用方法
1
2
3
4
5
6
7
8
package top.day3_atValue;
import org.springframework.context.annotation.PropertySource;
@PropertySource ( value={ "classpath:per.properties" })
public class human {
}
Copy @PropertySource 加载指定☞的配置文件
@ConfigurationProperties 默认获取全局の配置文件里的属性
⚠注意: @PropertySource 必须和 @ConfigurationProperties 一起使用才有效,单独使用没有效果
@ImportResource使用方法
其作用是导入 Spring 里面的配置文件并让其生效
1
@ImportResource ( locations = { classpath: "abc.xml" })
Copy 配置文件占位符
先来一个实例:
1
2
3
4
5
6
person.sname = ${random.uuid}
person.sage = ${random.int}
person.Sod = 'AnHui Provience'
person.lovelydog.age = ${person.lol:233}
person.lovelydog.name = lisi
server.port = 8080
Copy 这就是用随机数 $random 来生成的,还有用占位符生成の
${person.lol:233} の意思就是 有person.lol就用它,没有就用233
1
Person{sname='d573e71a-f8b2-4a48-a688-10449b4b55fc', sage=-1817647899, Sod=''AnHui Provience'', Lovelydog=dog{age=233, name='lisi'}}
Copy Profile文件支持
多Profile文件
properties
可以用多个带有 application-{env}.properties 来切换环境
注: 默认的profile是 application.properties
如果需要切换生产环境,可以在 application.properties 里面配置
1
spring.profiles.active = env
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
dog2.SunDay = 24
dog2.Example.k1 = sim
dog2.Example.k2 = sib
dog2.Friends = {lisi,zhangsan}
dog2.soul.Weight = 30
dog2.soul.Data = 35
dog2.soul.Power = 80
dog2.soul.Lovely = true
spring.profiles.active = ssr
person.sname = ${random.uuid}
person.sage = ${random.int}
person.Sod = 'AnHui Provience'
person.lovelydog.age = ${person.lol:233}
person.lovelydog.name = lisi
server.port = 8080
spring.profiles = ssr
Copy yaml
如果配置文件是 yaml 我们还可以用更为简便の多文档块の方式
这里就涉及到了基本の yaml 语法 - - - 这种连续三个 - 就表示文本块,看操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
person :
sname : zhangsan
sage : 18
Sod : 'AnHui Provience'
Lovelydog : {age: 13,name : lisi}
spring :
profiles :
active : ssr
---
person :
sname : ${random.uuid}
sage : ${random.int}
Sod : 'AnHui Provience'
Lovelydog : {age: 13,name : lisi}
server.port : 8088
spring :
profiles : ssr
Copy SpringBoot配置文件の加载顺序
首先关心一下,配置文件放哪里才会被加载
1.file:./config
2.file:/
3.classpath:/config/
4.classpath:/
按照顺序,1-4按顺序扫描,然后1-4の优先级依次降低,当配置文件里的属性冲突时,以优先级高的配置文件为准
还有一点需要注意的就是,这些文件都会被加载,形成互补配置
外部配置文件加载顺序
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
1 、命令行参数:所有的配置都可以在命令行参数中指定,每个配置项前使用 -- ,多个配置间使用空格隔开,例如:
java - jar spring- boot- 02 - 0.0.1 - SNAPSHOT.jar -- server.port= 8088 -- server.context- path= boot
2 、来自 java:comp/ env的JNDI属性
3 、 java的系统属性(System.getProperties("" ))
4 、操作系统环境变量
5 、 RandomValuePropertySource配置的random.* 属性值
6 、 jar包外部的application- {profile}.properties或application- {profile}.yml(带 spring.profile配置)
7 、 jar包内部的application- {profile}.properties或application- {profile}.yml(带 spring.profile配置)
8 、 jar包外部的application.properties或application.yml(不带 spring.profile配置)
9 、 jar包内部的application.properties或application.yml(不带 spring.profile配置)
10 、@ Configuration注解类上的@ PropertySource
11 、通过 SpringApplication.setDefaultProperties()指定的默认属性
注:
①以上配置文件的优先级顺序由高到低,高优先级的覆盖低优先级的并形成互补
② 6 、 8 所指的 jar包外指的是和jar包同一个文件夹下
Copy