springboot介绍、配置文件介绍、自动配置读取原理
- springBoot学习
- 代码说明
- 为什么java -jar springJar包后项目就可以启动
- 配置文件介绍
- 配置文件加载顺序
- 其他约定配置文件加载顺序
- profile配置文件加载
- 配置文件绑定类属性
- 通过@Value的方式进行属性注入
- 通过@ConfigurationProperties的方式进行属性注入
- 配置文件占位符
- 配置文件-属性注入:数据校验
- 通过@PropertySource可以引入外部properties配置文件
- Springboot自动配置底层原理
- springboot配置读取原理
- 这些注解里面最主要的就是@EnableAutoConfiguration
- @Import({AutoConfigurationImportSelector.class}详细解读
- process获取配置方法解读
- 自动配置类原理
springBoot学习
依赖引入
<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.target>8</maven.compiler.target><maven.compiler.source>8</maven.compiler.source></properties><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.6</version></parent><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies><!--打包好帮我们把所有依赖的jar 统统放到jar文件里面的BOOT-INF\lib中设置MANIFEST.MF设置了启动类 .JarLauncher:自定义类加载器加载所有的jar包,调用start-class。从而启动项目--><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
代码说明
所有的springboot都必须继承spring-boot-starter-parent,通过里面的<artifactId>spring-boot-dependencies</artifactId>帮我们管理了依赖的版本
为什么引入了starter后其他依赖也就被引入了?
一个starter也就是启动器,内置了很多的依赖,就相当于一个聚合,引入一个starter也就都引入了
为什么java -jar springJar包后项目就可以启动
- 先打包
将打的jar包解压可以看到有个BOOT-INF的,里面还有个lib包这个lib包里存的就是项目依赖的其他jar包,叫fat-jar
META-INF中有个MANIFEST.MF文件,里面的Main-Class是所有的jar文件都有的,这个属性会自定义一个类加载器去加载fat-jar中的jar包。然后调用Start-Class去启动项目
META-INF中的MANIFEST.MF文件中有一个Main-Class属性,这个属性指向了一个自定义的类加载器。这个类加载器在启动Spring Boot应用时会加载fat-jar中的所有依赖,包括starter依赖。
Start-Class属性则指定了应用启动时要执行的类。通过这种方式,Spring Boot能够在构建的fat-jar中自动寻找并加载所有必要的依赖,从而实现应用的启动。
配置文件介绍
springboot使用一个全局的配置文件 核心配置文件,配置文件名在 约定的情况下 名字是固定的就叫application
配置文件的作用:修改springboot自动配置的默认值;springboot在底层都给我们自动配置好了。比如端口号,springboot默认端口号8080,但是我们可以通过配置文件覆盖默认配置。
配置文件加载顺序
配置文件读取优先级:bootstrap.properties>bootstrap.yml>application.properties>application.yml 优先级大的会覆盖优先级小的
其他约定配置文件加载顺序
1、classpath根目录下
2、classpath根目录/config
3、项目根目录
如果当前项目是继承/耦合 关系maven项目的话,项目根目录=父maven项目的根目录。如果是项目只有一个模块那么项目根目录 那就是当前模块的根目录
4、项目根目录 /config
如果当前项目是继承/耦合 关系maven项目的话,项目根目录=父maven项目的根目录 /config。如果是项目只有一个模块那么项目根目录 那就是当前模块的根目录 /config
优先级由上到下,由低到高
profile配置文件加载
多环境配置配置文件
profile文件命名规则:application-名称.yml
- 在applicaction.yml中进行激活
spring:profiles:active: dev
配置文件绑定类属性
- 定义实体bean
@Data
@ToString
public class User {private String username;private Integer age;private Date birthday;private List<String> hobbies;private Map<Integer,String> girlFriend;private Address address;
}@Data
public class Address {private Integer id;private String detailAddress;
}
- 定义yml文件
user:username: zzqage: 25
通过@Value的方式进行属性注入
@Data
@ToString
@Component
public class User {@Value("${user.username}")private String username;@Value("${user.age}")private Integer age;private Date birthday;private List<String> hobbies;private Map<Integer,String> girlFriend;private Address address;
}
- 测试
@SpringBootTest(classes = MyApplication.class)
public class Test {@Autowiredprivate User user;@org.junit.jupiter.api.Testpublic void test(){System.out.println(user);}
}
通过@ConfigurationProperties的方式进行属性注入
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "user")
public class User {private String username;private Integer age;private Date birthday;private List<String> hobbies;private Map<Integer,String> girlFriend;private Address address;
}
- 测试
@SpringBootTest(classes = MyApplication.class)
public class Test {@Autowiredprivate User user;@org.junit.jupiter.api.Testpublic void test(){System.out.println(user);}
}
@ConfigurationProperties:常用于bean属性和yml配置文件的绑定。prefix:可以指定配置文件中某一个节点,该节点中的子节点将自动和属性进行绑定
此外@ConfigurationProperties还支持松散绑定
@value和@ConfigurationProperties区别,最大的一个区别就是@ConfigurationProperties可以匹配多个,@value需要一个一个去匹配,比较麻烦
- 我们目前写yml文件没有提示,如果需要提示需要引入spring-boot-configuration-processor依赖
这个依赖会生成METE-INFO 元数据 用于提供idea自动提示配置文件的
<!--这个依赖会生成METE-INFO 元数据 用于提供idea自动提示配置文件的--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><!--依赖不会传播 如果这是个父模块,子模块不会继承这个依赖--><optional>true</optional></dependency>
- 如果想要自动提示还需要修改idea相关配置
生成的元数据-用于自动提示
- 补充完剩余的属性
- list类型
hobbies:- 唱歌- 跳舞- 打篮球hobbies: [唱歌、跳舞、打篮球]
- map的类型
girl-friend:18: 范冰冰19: 刘亦菲girl-friend: {18:范冰冰,19:刘亦菲}
- 嵌套对象
address:id: 5detailAddress: 6666
- 完整配置
user:username: zzqage: 123birthday: 2020/01/01hobbies:- 唱歌- 跳舞- 打篮球girl-friend: {18:范冰冰,19:刘亦菲}address:id: 6detailAddress: 6666
配置文件占位符
可以通过${}来引用其他的配置项,这就是配置文件占位符,${}还可以引用springboot内置的一些属性,比如random.uuid
user:username: zzqage: 123birthday: 2020/01/01hobbies:- 唱歌- 跳舞- 打篮球girl-friend: {18:范冰冰,19:刘亦菲}address:id: 6detailAddress: ${user.username}的家
id: ${random.int}
user:username: zzqage: 123birthday: 2020/01/01hobbies:- 唱歌- 跳舞- 打篮球girl-friend: {18:范冰冰,19:刘亦菲}address:id: ${random.int}detailAddress: ${user.username}的家
配置文件-属性注入:数据校验
只支持@ConfigurationProperties,在使用的时候只需要加@Validated(支持对jsr-303数据校验)
- jsr-303数据校验
要支持对jsr-303数据校验还需要加入一个数据校验的场景启动器
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
- 测试 -实体bean要测试的属性加上注解
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "user")
@Validated
public class User {private String username;private Integer age;private Date birthday;private List<String> hobbies;private Map<Integer,String> girlFriend;@NotNullprivate Address address;
}
- 配置文件配置-去掉address不让能够注入
user:username: zzqage: 123birthday: 2020/01/01hobbies:- 唱歌- 跳舞- 打篮球girl-friend: {18:范冰冰,19:刘亦菲}
结果
通过@PropertySource可以引入外部properties配置文件
- properties配置
user.username=zzq
user.age=123
user.birthday=2020/02/06
user.hobbies=[跳舞、唱歌]
user.girlFriend.18=范冰冰
user.girlFriend.19=刘亦菲
user.address.id=5
user.address.detailAddress=上海
- 实体bean配置-@PropertySource(“classpath:data/user.properties”)
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "user")
@Validated
@PropertySource("classpath:data/user.properties")
public class User {private String username;private Integer age;private Date birthday;private List<String> hobbies;private Map<Integer,String> girlFriend;private Address address;
}
Springboot自动配置底层原理
springboot配置读取原理
@SpringBootConfiguration:Spring Boot的配置类:标注在某个类上,表示这是一个Spring Boot的配置类
@Configuration:配置类上来标注这个注解,配置类也是容器的一个组件
@EnableAutoConfiguration:开启自动配置功能:以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,会帮我们自动去加载配置类,这样自动配置才能生效
@ComponentScan:扫描包,如果没有指定扫描包spring底层会自动扫描当前配置类所在的包和子包, TypeExcludeFilter:springboot对外提供的扩展类,可以供我们去按照我们的自定义的方式进行排除。AutoConfigurationExcludeFilter:排除所有实现了AutoConfigurationExcludeFilter 接口 配置类并且自动配置类
这些注解里面最主要的就是@EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}
- @AutoConfigurationPackage
这个注解的作用:将当前配置类所在包保存在BasePackages的Bean中。供Sspring内部使用
@Import({AutoConfigurationPackages.Registrar.class}) //保存扫描路径,提供给spring-data-jpa
public @interface AutoConfigurationPackage {
就是注册了一个保存当前配置类所在包的一个bean
@Import({AutoConfigurationImportSelector.class}详细解读
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
AutoConfigurationImportSelector是一个实现了DeferredImportSelector的selector类
- 复习ImportSelector
作用:返回类路径的完整限定名,并且将这些类注册成bean
- DeferredImportSelector:变种的延迟ImportSelector
DeferredImportSelector工作原理
- 具体使用
如果getImportGroup为空则执行importselectors,否则执行getImportGroup返回类的selectImports
public class MyImportSelector implements DeferredImportSelector {@Overridepublic Class<? extends Group> getImportGroup() {return AutoConfigurationGroupdd.class;}@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.springboot.controller.bean.User"};}private static class AutoConfigurationGroupdd implements DeferredImportSelector.Group{AnnotationMetadata metadata;@Overridepublic void process(AnnotationMetadata metadata, DeferredImportSelector selector) {}@Overridepublic Iterable<Entry> selectImports() {List<Entry> list =new ArrayList<>();list.add(new Entry(this.metadata,"com.springboot.controller.bean.Person"));return list;}}
}
process获取配置方法解读
process方法可以获取META-INF/spring.factories里面的内容并把内容给selectorimports方法让这个方法去把spring.factories里面的配置注册成bean
//获取spring.factories里面的配置类@Overridepublic void process(AnnotationMetadata metadata, DeferredImportSelector selector) {}//将配置类注册成bean@Overridepublic Iterable<Entry> selectImports() {}
getAutoConfigurationEntry—>getCandidateConfigurations
getCandidateConfigurations再通过spring加载器来获取配置
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
自动配置类原理
以HttpEncodingAutoConfiguration为例
@Configuration(proxyBeanMethods = false
)
@EnableConfigurationProperties({ServerProperties.class})
@ConditionalOnWebApplication(type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class})
@ConditionalOnProperty(prefix = "server.servlet.encoding",value = {"enabled"},matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {private final Encoding properties;public HttpEncodingAutoConfiguration(ServerProperties properties) {this.properties = properties.getServlet().getEncoding();}@Bean@ConditionalOnMissingBeanpublic CharacterEncodingFilter characterEncodingFilter() {CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();filter.setEncoding(this.properties.getCharset().name());filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));return filter;}
}
- @Configuration( proxyBeanMethods = false)详解
标记了@Configuration Spring底层会给配置创建cglib动态代理,目的:给标记了@bean的方法在创建bean的时候,会执行动态代理类(这个动态代理类会先从ioc容器里面拿如果拿不到在调用bean方法进行创建,以满足单例模式)
所以 proxyBeanMethods = false,就不让它去产生代理类 - @EnableConfigurationProperties({ServerProperties.class})详解
启用可以在配置类设置的属性 对应的类(在配置文件中可以写哪些属性就由这个配置类中的属性决定)
启用配置类的属性:里面导入了一个属性配置类