起步依赖
<!--springboot创建必须导入的其父类的依赖管理包-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
</parent>
按住Ctrl点击pom.xml中的spring-boot-starter-parent,跳转到了spring-boot-starter-parent的pom.xml,按住Ctrl点击pom.xml中的spring-boot-starter-dependencies,跳转到了spring-boot-starter-dependencies的pom.xml,从上面的spring-boot-starter-dependencies的pom.xml中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了(不会出现版本冲突的问题)。所以起步依赖的作用就是进行依赖的传递。
当我们导入依赖:spring-boot-starter-web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
按住Ctrl点击pom.xml中的spring-boot-starter-web,跳转到了spring-boot-starter-web的pom.xml,spring-boot-starter-web就是将web开发要使用的spring-web、spring-webmvc等坐标进行了“打包”,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用,同时加载tomcat,只要启动main方法,就相当于起到tomcat进行开发;同时加载json,支持springmvc的数据请求和响应。
自动配置原理解析
@SpringBootApplication
public class SpringbootDemo02Application {
public static void main(String[] args) {
ApplicationContext act = SpringApplication.run(SpringbootDemo02Application.class, args);
for (String name : act.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
按住Ctrl点击查看启动类MySpringBootApplication上的注解注解@SpringBootApplication的源封装spring注解的复合注解有:
@ComponentScan
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan 解析
1.component是组件,scan是扫描,所以这个注解的含义就是用来扫描组件的
2.componentScan 扫描当前包及其子包下被 @Component,@Controller,@Service,@Repository
注解标记的类并纳入到spring容器中进行管理,所以这个注解会自动注入所有在主程序所在包下的组件。默认把当前启动类所在的包作为扫描包的起点
相当与以前的ssm扫描包:
<context:component-scan base-package="com.xxx"></context:component-scan>
@SpringBootConfiguration解释
按住Ctrl点击查看注解 @EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
从注解可以看出,这个就是标志该类是配置类@Configuration
@EnableAutoConfiguration的理解是最为重点
按住Ctrl点击查看注解 @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 :这个注解是自动导入我们配置的包
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
重点理解import导入的自动选择配置类:
AutoConfigurationImportSelector.class
其流程点击进去可以看到:
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());
}
}
其有一个方法selectImports(),是选择要导入的组件,其方法内:getAutoConfigurationEntry方法是获取自动配置的实体方法
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);
}
}
getAutoConfigurationEntry方法中有getCandidateConfigurations获取候选配置的方法,具体看看是获取那些配置.点击可以看到,获取的资源配置会遍历存到 Properties properties = PropertiesLoaderUtils.loadProperties(resource)文件中,所以当我们用配置文件时,会出现提示的原因;
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;
}
方法内部有loadFactoryNames的方法,是获取工厂配置文件的名字,点击可以看到
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, 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();
该方法是加载我们自己配置的资源或者系统里面的资源,都是META-INF/spring.factories路径下配置的资源
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
从下面这里也可以知道,配置文件的资源都是从META-INF/spring.factories路径加载的
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.");
loadFactoryNames方法的参数this.getSpringFactoriesLoaderFactoryClass() 点击可以看到该方法是加载EnableAutoConfiguration.class,也就是含有这个注解的类都会被导入,也就是我们的启动类(@SpringBootApplication 含有该注解),所以我们的启动类会的所以配置文件会加载进去.
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
再看看系统导入的配置文件META-INF/spring.factories
里面都是XXXAutoConfiguration配置文件,所以启动的时候,这些配置文件也会被加载进去
# 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,\
但是这些文件到会加载进去,为什么都没有生效呢?点击一个配置文件可以看到有@ConditionalOnClass或者@ConditionalOnXXX 这些注解,这些都是这些配置文件生效的条件:从@Configuration可以看出是一个配置文件,@Bean就是将该类的方法导入到IOC容器中.所以当我们在pom.xml导入我们需要的依赖时,响应下系统文件就会满足@ConditionalOnXXX 条件,里面组件我们就可以直接拿来使用,这就是自动装配的好处,一般系统配置组件都有默认设置;
@Configuration
@ConditionalOnClass({RabbitTemplate.class, Channel.class})
@EnableConfigurationProperties({RabbitProperties.class})
@Import({RabbitAnnotationDrivenConfiguration.class})
public class RabbitAutoConfiguration {
public RabbitAutoConfiguration() {
}
@Configuration
@ConditionalOnClass({RabbitMessagingTemplate.class})
@ConditionalOnMissingBean({RabbitMessagingTemplate.class})
@Import({RabbitAutoConfiguration.RabbitTemplateConfiguration.class})
protected static class MessagingTemplateConfiguration {
protected MessagingTemplateConfiguration() {
}
@Bean
@ConditionalOnSingleCandidate(RabbitTemplate.class)
public RabbitMessagingTemplate rabbitMessagingTemplate(RabbitTemplate rabbitTemplate) {
return new RabbitMessagingTemplate(rabbitTemplate);
}
配置文件application.yaml的理解
其实我们导入的依赖就是spring.factories配置文件生效的配置组件,当我们配置application.yaml配置时,其实就是自定义我们的配置文件的属性:从注解
@EnableConfigurationProperties({RedisProperties.class})
@ConditionalOnProperty(
prefix = "spring.rabbitmq.listener",
name = {"type"},
havingValue = "simple",
matchIfMissing = true
)
这些注解可以知道,我们在配置文件输入的就是在自定义我们的配置类.
且注解 @ConfigurationProperties (prefix=“配置文件中的key的前缀”)可以将配置文件中的配置自动与实体进行映射
@Configuration
@ConditionalOnClass({RabbitTemplate.class, Channel.class})
@EnableConfigurationProperties({RabbitProperties.class})
@Import({RabbitAnnotationDrivenConfiguration.class})
public class RabbitAutoConfiguration {
public RabbitAutoConfiguration() {
}
@Bean(
name = {"rabbitListenerContainerFactory"}
)
@ConditionalOnMissingBean(
name = {"rabbitListenerContainerFactory"}
)
@ConditionalOnProperty(
prefix = "spring.rabbitmq.listener",
name = {"type"},
havingValue = "simple",
matchIfMissing = true
)
public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
return factory;
}
SpringApplication的理解
主要的作用:
推断项目类型是普通类型还是web类型,要是web类型就继续保持运行状态
查找加载所有的初始化器,设置到初始化的属性中
查找监听器,设置到监听器属性中
推断并设置main方法定义的类,找到运行的类
@SpringBootApplication//springboot的入口
public class Application {
public static void main(String[] args) {
System.out.println("hahaha " );
SpringApplication.run(Application.class,args);
}
}
总结:
导入父类,可以定义好我们的依赖版本以及管理.当我们导入我们想要的依赖时,系统会根据我们的导入的依赖自动选择相应的组件.当我们要对相应的组件进行设置时,配置application.yaml配置文件即可,如果不配置,系统的组件会使用默认的设置.当然,我们也可以自定义组件,通过使用用@bean和@Configuration 注解可以自定义组件.