SpringBoot的核心就是自动配置,自动配置是基于条件判断配置Bean
自动配置的源码在spring-boot-autoconfigure-2.2.13.RELEASE
SpringBoot运行原理
先看@SpringBootApplication
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.boot.autoconfigure; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.core.annotation.AliasFor; @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 { @AliasFor( annotation = EnableAutoConfiguration.class ) Class<?>[] exclude() default {}; @AliasFor( annotation = EnableAutoConfiguration.class ) String[] excludeName() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackages" ) String[] scanBasePackages() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackageClasses" ) Class<?>[] scanBasePackageClasses() default {}; @AliasFor( annotation = Configuration.class ) boolean proxyBeanMethods() default true; }
主要关注的几个注解如下
@SpringBootConfiguration:标记当前类为配置类
@EnableAutoConfiguration:开启自动配置
@ComponentScan:扫描主类所在的同级包以及下级包里的Bean
关键是@EnableAutoConfiguration
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.boot.autoconfigure; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; @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 {}; }
最关键的要属@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器:通过@Import(AutoConfigurationImportSelector.class)导入的配置功能,
AutoConfigurationImportSelector中的方法getCandidateConfigurations,得到待配置的class的类名集合,这个集合就是所有需要进行自动配置的类,而是否配置的关键在于META-INF/spring.factories文件中是否存在该配置信息
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; }
打开,如下图可以看到所有需要配置的类全路径都在文件中,每行一个配置,多个类名逗号分隔,而\表示忽略换行
样例讲解
以SpringApplicationAdminJmxAutoConfiguration类来看其主要构成部分
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.boot.autoconfigure.admin; import java.util.Iterator; import javax.management.MalformedObjectNameException; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.jmx.export.MBeanExporter; @Configuration( proxyBeanMethods = false ) @AutoConfigureAfter({JmxAutoConfiguration.class}) @ConditionalOnProperty( prefix = "spring.application.admin", value = {"enabled"}, havingValue = "true", matchIfMissing = false ) public class SpringApplicationAdminJmxAutoConfiguration { private static final String JMX_NAME_PROPERTY = "spring.application.admin.jmx-name"; private static final String DEFAULT_JMX_NAME = "org.springframework.boot:type=Admin,name=SpringApplication"; public SpringApplicationAdminJmxAutoConfiguration() { } @Bean @ConditionalOnMissingBean public SpringApplicationAdminMXBeanRegistrar springApplicationAdminRegistrar(ObjectProvider<MBeanExporter> mbeanExporters, Environment environment) throws MalformedObjectNameException { String jmxName = environment.getProperty("spring.application.admin.jmx-name", "org.springframework.boot:type=Admin,name=SpringApplication"); if (mbeanExporters != null) { Iterator var4 = mbeanExporters.iterator(); while(var4.hasNext()) { MBeanExporter mbeanExporter = (MBeanExporter)var4.next(); mbeanExporter.addExcludedBean(jmxName); } } return new SpringApplicationAdminMXBeanRegistrar(jmxName); } }
都能看到各种各样的条件判断注解,满足条件时就加载这个Bean并实例化
手动实现自动配置的demo
项目目录
1.XiaoServer文件
package com.xiao.configure; /** * 需要被实例化的服务类 * @author xiaoss * @date 2021年11月03日 14:35 */ public class XiaoServer { private String name; public String sayServerName(){ return "I'm " + name + "! "; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
2.XiaoServerProperties文件
package com.xiao.configure; import org.springframework.boot.context.properties.ConfigurationProperties; /** * 配置信息属性类 * @author xiaoss * @date 2021年11月03日 14:55 */ @ConfigurationProperties(prefix = "xiao") public class XiaoServerProperties { private static final String NAME = "xiao_server0"; private String name = NAME; public String getName() { return name; } public void setName(String name) { this.name = name; } }
3.XiaoServiceAutoConfiguration文件
package com.xiao.configure; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 自动配置类 * @author xiaoss * @date 2021年11月03日 14:58 * 根据条件判断是否要自动配置,创建Bean */ @Configuration @EnableConfigurationProperties(XiaoServerProperties.class) @ConditionalOnClass(XiaoServer.class)//判断XiaoServer这个类在类路径中是否存在 @ConditionalOnProperty(prefix = "xiao",value = "enabled",matchIfMissing = true) public class XiaoServiceAutoConfiguration { @Autowired private XiaoServerProperties mistraServiceProperties; @Bean(name = "xiaoServer") @ConditionalOnMissingBean(XiaoServer.class)//当容器中没有这个Bean时(XiaoServer)就自动配置这个Bean,Bean的参数来自于XiaoServerProperties public XiaoServer mistraService(){ XiaoServer mistraService = new XiaoServer(); mistraService.setName(mistraServiceProperties.getName()); return mistraService; } }
4.SpringbootAutoconfigureApplication文件
package com.xiao; import com.xiao.configure.XiaoServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Date; @SpringBootApplication @RestController public class SpringbootAutoconfigureApplication { @Autowired private XiaoServer xiaoService; public static void main(String[] args) { SpringApplication.run(SpringbootAutoconfigureApplication.class, args); } @RequestMapping("/") public Object index(){ return "helll demo "+xiaoService.getName()+" "+new Date(); } }
5.spring.factories
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.xiao.configure.XiaoServiceAutoConfiguration
6.application.properties
xiao.name=test service
7.pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!--<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.6</version> <relativePath/> <!– lookup parent from repository –> </parent>--> <groupId>com.xiao</groupId> <artifactId>springboot-autoconfigure</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot-autoconfigure</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.5.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.5.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>2.2.13.RELEASE</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
运行结果:
总结:
一、什么是springBoot自动配置
SSM项目创建一次,需要写大量的配置文件,而springboot只需要写少量的配置,其他的都交给自动配置,按条件创建bean
二、springboot自动配置是怎么运行的
- 当SpringBoot项目启动时,会先去扫描所有jar包下的META-INF/spring.factories配置文件
2.通过读取spring.factories配置文件得到所有自动配置类的全限定类名,并将这些自动配置类全部注入到IOC容器
3.那么多类是全部都注入进去?当然不是,在这些配置类中都有条件注解,这些条件注解会去判断是否要导入
4.经过以上步骤,就实现了自动配置的过程,当然详细的实现细节肯定不会这么简单