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
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.经过以上步骤,就实现了自动配置的过程,当然详细的实现细节肯定不会这么简单