SpringBoot的核心就是自动配置,自动配置是基于条件判断配置Bean

自动配置的源码在spring-boot-autoconfigure-2.2.13.RELEASE

SpringBoot自动配置的原理及实现_ide

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;
}

打开,如下图可以看到所有需要配置的类全路径都在文件中,每行一个配置,多个类名逗号分隔,而\表示忽略换行

SpringBoot自动配置的原理及实现_后端_02

样例讲解

以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

项目目录

SpringBoot自动配置的原理及实现_spring_03

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/> <!&ndash; lookup parent from repository &ndash;>
</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自动配置的原理及实现_ide_04

总结:

一、什么是springBoot自动配置

SSM项目创建一次,需要写大量的配置文件,而springboot只需要写少量的配置,其他的都交给自动配置,按条件创建bean

二、springboot自动配置是怎么运行的

  1. 当SpringBoot项目启动时,会先去扫描所有jar包下的META-INF/spring.factories配置文件

2.通过读取spring.factories配置文件得到所有自动配置类的全限定类名,并将这些自动配置类全部注入到IOC容器

3.那么多类是全部都注入进去?当然不是,在这些配置类中都有条件注解,这些条件注解会去判断是否要导入

4.经过以上步骤,就实现了自动配置的过程,当然详细的实现细节肯定不会这么简单