Spring Boot 入门笔记:自动配置、组件与容器
整理 Spring Boot 入门和自动配置相关内容,突出配置生效条件与容器中的组件关系,说明源码阅读应绑定实际版本。
阅读自动配置源码时,先找入口和条件,再看最终创建的对象。仅看到某个自动配置类存在,不能证明它在当前应用生效;依赖、配置属性和已注册的 Bean 都可能改变结果。
一. 基础入门
1.相关资料
原文档地址 https://www.yuque.com/atguigu/springboot
2.springboot优点
-
Create stand-alone Spring applications
创建独立Spring应用
-
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
内嵌web服务器
-
Provide opinionated 'starter' dependencies to simplify your build configuration
自动starter依赖,简化构建配置
-
Automatically configure Spring and 3rd party libraries whenever possible
自动配置Spring以及第三方功能
-
Provide production-ready features such as metrics, health checks, and externalized configuration
提供生产级别的监控、健康检查及外部化配置
-
Absolutely no code generation and no requirement for XML configuration
无代码生成、无需编写XML
springboot是整合spring技术栈得一站式框架
springboot是简化spring技术栈快速开发脚手架
二.时代背景
1.微服务
-
微服务是一种架构风格
-
一个应用拆分为一组小型服务
-
每个服务运行在自己的进程内,也就是可独立部署和升级
-
服务之间使用轻量级HTTP交互
-
服务围绕业务功能拆分
-
可以由全自动部署机制独立部署
-
去中心化,服务自治。服务可以使用不同的语言、不同的存储技术
2. 分布式
1. 分布式困难
-
远程调用
-
服务发现
-
负载均衡
-
服务容错
-
配置管理
-
服务监控
-
链路追踪
-
日志管理
-
任务调度
2. 分布式解决
- SpringBoot + SpringCloud
3. 云原生
三. helloword入门
1. maven配置
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
2. 简化部署--打包配置
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
3. 自动配置原理
1.自动配置tomcat
自动引入了tomcat依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.5.1</version>
<scope>compile</scope>
</dependency>
自动配置tomcat
自动配置springMVC
引入springMVC的全套组件
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
<scope>compile</scope>
</dependency>
自动配置好springMVC的常用组件
自动配置web
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.8</version>
<scope>compile</scope>
</dependency>
默认包结构
主程序所在的包及其下面的所有子包里面的组件都会被扫描
其他的包可以在启动类上添加scanBasePackages
@SpringBootApplication(scanBasePackages = {""})
添加包扫码结构
@ComponentScan("com.wu")
各种配置拥有默认值
默认的配置,最终都是映射到某一个类上
配置文件的值,最终会绑定在每个类上,这个类会在容器中创建对象
按需加载所有自动配置项
非常多的starter
引入了哪些场景,该场景的配置才会开启
springboot所有的自动配置功能都在autoconfigure场景下
四. 核心注解
1.@Configuration
告诉springboot这是一个配置类,等于以前的配置文件
package com.wu.springboot.config;
import com.wu.springboot.pojo.Pet;
import com.wu.springboot.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 1.在配置类里面使用@Bean注解在方法上,给容器添加组件。这些组件都是单例模式。当组件有对象参数传递时,会在当前容器中查询是否存在当前类型的组件。然后使用查询到的组件作为参数传递
* 2. 配置类本身也是组件
* 3. proxyBeanMethods 代理实例方法
* full(proxyBeanMethods = true) 全代理 springboot在启动的时候,会检查每个组件的实例是否存在
* lite(proxyBeanMethods = false)轻量代理 springboot 在启动的时候,不会检查每个组件的实例(跳过检查。
* 在其他地方使用对象调用方法时,生成的实例也不是单例
* 组件依赖
* 如果只是在容器中注册组件,其他地方对其没有依赖。修改为false,能提升springboot的启动速度
*/
@Configuration(proxyBeanMethods = true) //告诉springboot,这是一个配置类,相当于之前的配置文件
public class MyConfigruation {
@Bean //给容器添加组件,以方法名作为组件的id。返回类型就是组件类型, 返回的值就是组件在容器中的实例
public User userO1(){
return new User("张三",20);
}
@Bean("tom01") //可以不使用方法名作为组件的id。在Bean注解内自定义组件id
public Pet tom(){
return new Pet("tom",2);
}
}
package com.wu.springboot;
import com.wu.springboot.config.MyConfigruation;
import com.wu.springboot.pojo.Pet;
import com.wu.springboot.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
//ComponentScan已经在SpringBootApplication有了。默认扫描的是当前类所在的包
@ComponentScan("com.wu")
@SpringBootApplication
public class SpringbootMain {
public static void main(String[] args) {
//返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(SpringbootMain.class, args);
//配置类也是一个实例
MyConfigruation myConfigruation = run.getBean("myConfigruation", MyConfigruation.class);
System.out.println(myConfigruation);
//从容器中获取user和pet的实例,对比调用对象方法生成实例
User userO1 = run.getBean("userO1", User.class);
Pet tom01 = run.getBean("tom01", Pet.class);
Pet tom = myConfigruation.tom();
User user = myConfigruation.userO1();
System.out.println(tom01==tom); //1配置配代理方法为true时@Configuration(proxyBeanMethods = true) 为true proxyBeanMethods = false-->false
System.out.println(userO1==user);//1配置配代理方法为true时@Configuration(proxyBeanMethods = true) 为true proxyBeanMethods = false-->false
}
}
2. @import注解
导入注解
* 4.Import注解
* 在容器中自动创建对应类型的组件。默认组件名字就是全类名
* 如@Import({DBHelper.class, JNDIConnectionSource.class})
* 会自动创建DBHelper和JNDIConnectionSource的实例
打印容器内的实例
for (String name :run.getBeanDefinitionNames()){
System.out.println(name);
}
输出中包含
ch.qos.logback.core.db.DBHelper
ch.qos.logback.core.db.JNDIConnectionSource
3.@Conditional 注解
条件装配,满足对应的条件,则进行组件注入
@ConditionalOnBean(name = "tom01")//条件装配,当存在实例tom01的时候,才装配该组件
@ConditionalOnMissingBean(name = "tomm") //条件装配,当容器中不存在tomm实例时,才装配该组件
用在配置类上面,则作用于整个配置类下面的组件
如果用在方法上,只作用于当前方法
注意:实例在生成的时候有先后顺序!!!
4. @ImportResource 导入原生的资源配置文件
@ImportResource("classpath:beans.xml")//导入原生的资源配置文件
5. 配置绑定
组件注入时,使用配置文件中对应的属性值
yml中配置
#自定义配置
car.carName: BYD
car.price: 10000
实例配置
@Data
@Component//将该类注册到IOC容器中。只有容器中的组件,才能使用springboot提供的相关功能
@ConfigurationProperties(prefix="car") //使用配置文件中,前缀和car匹配的值.
public class Car {
private String carName;
private String price;
}
使用(自动注入使用):
@Resource
private Car car;
另一种配置方式,在配置文件中使用
@EnableConfigurationProperties(Car.class) //开启car类的属性配置功能。将car组件添加到容器中
五. 自动配置原理
SpringBootApplication包含注解分析
1.@SpringBootConfiguration
主要注解Configuration 代表该类是一个配置文件
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
2. @ComponentScan
组件扫描
3. @EnableAutoConfiguration
自动配置包,指定了默认的包规则
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
1. @AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
@Import 导入对应的组件
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));
}
}
register()将该包下的所有组件导入到IOC容器中
new PackageImports(metadata).getPackageNames()获取到的包名时Main方法所的包。侧面印证了springboot在启动的时候,只有Main方法所在包及子包下的组件才会生效的原因
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
beanDefinition.addBasePackages(packageNames);
}
else {
registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
}
}
2. @Import(AutoConfigurationImportSelector.class)
先获取所有的组件配置信息,再通过各种条件注入动态的注入组件
@Import(AutoConfigurationImportSelector.class)//利用该方法给容器导入组件
引用流程链
- selectImports方法 查询导入的配置
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
-
getAutoConfigurationEntry() 获取自动配置的组件
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); } -
getCandidateConfigurations(annotationMetadata, attributes) 获取所有配置组件信息
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;
}
-
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader()); 使用工厂加载器加载public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); } -
loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { Map<String, List<String>> result = cache.get(classLoader); if (result != null) { return result; } result = new HashMap<>(); try { Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } // Replace all lists with unmodifiable lists containing unique elements result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } return result; } -
Enumeration
urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); 从配置文件中获取对应的加载信息 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; -
spring.factories的所有自动配置
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\ ...........
六.最佳实践
引入场景依赖。springboot列举了所有第三方配置的依赖
https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
查看自动配置了哪些
配置文件中使用debug=true
是否需要配置项
参照文档修改配置项https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties
自己分析xxxproperties绑定了哪些内容
自定义加入或替换组件
自定义器 xxxCustomizer
七.开发小技巧
1. lombok
2. dev-tools
热更新
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
3. spring initailizr
- 本文标签: Spring Boot Java
- 本文链接: https://www.tianyajuanke.top/article/36
- 版权声明: 本文由吴沛芙原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权