SpringBoot源码分析

·  阅读 142

自动配置

使用 Spring Boot 开发较之以前的基于 xml 配置式的开发,要简捷方便快速的多。而这完全得益于 Spring Boot 的自动配置。下面就通过源码阅读方式来分析自动配置的运行原理。

打开启动类的@SpringBootApplication 注解源码。

@SpringBootApplication
public class Springboot01DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(Springboot01DemoApplication.class, args);
    }
}
复制代码

查看@SpringBootApplication注解的源码,我们发现@SpringBootApplication 注解其实就是一个组合注解,如下:

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

}
复制代码

元注解

  • @Target(ElementType.TYPE):表示当前注解仅能注解在类上。

  • @Retention(RetentionPolicy.RUNTIME):表示当前注解的生存时间为源码、字节码,及 JVM 运行期间。

  • @Documented:表示当前注解的注释信息将显示在 javaAPI 文档中

  • @Inherited:表示当前注解所标注的类的子类将会继承该类上的注解

@SpringBootConfiguration

该注解与@Configuration 注解功能完全相同。即标注该类是Spring配置类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
 
   @AliasFor(annotation = Configuration.class)
   boolean proxyBeanMethods() default true;
}
复制代码

@ComponentScan

用于指定当前应用所要扫描的包。

image.png

@EnableAutoConfiguration

该注解用于完成自动配置,是 Spring Boot 的核心注解。后面要详细解析。这里主要是要对@EnableXxx 注解进行说明。@EnableXxx 注解一般用于开启某一项功能,是为了避免简化配置代码的引入。其是组合注解,一般情况下@EnableXxx 注解中都会组合一个@Import 注解,而该@Import 注解用于导入指定的类,而该被导入的类一般为配置类。其导入配置类的方式常见的有三种:

  • A、直接引入配置类,@Import 中指定的类一般为 Configuration 结尾,且该类上会注解@Configuration,表示当前类为 JavaConfig 类。例如用于开启定时任务的@EnableScheduling 注解的@Import。

  • B、 根据条件选择配置类,@Import 中指定的类一般以 ConfigurationSelector 结尾,且该类实现了 ImportSelector接口,表示当前类会根据条件选择不同的配置类导入。例如,用于开启缓存功能的@EnableCaching 的@Import。

  • C、 动态注册 Bean,@Import 中指定的类一般以 Registrar 结尾,且该类实现了 ImportBeanDefinitionRegistrar接口,用于表示在代码运行时若使用了到该配置类,则系统会自动将其导入。例如,用于开启 AspectJ 自动代理功能的@EnableAspectJAutoProxy 注解的@Import。

查看@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 {};
}
复制代码

该注解用于完成自动配置,是 Spring Boot 的核心注解,是一个组合注解。所谓自动配置是指,将用户自定义的类及框架本身用到的类进行装配。其中最重要的注解有两个:

  • @AutoConfigurationPackage:用于导入并装配用户自定义类,即自动扫描包中的类

  • @Import:用于导入并装配框架本身的类

@Import

image.png 由于AutoConfigurationImportSelector类间接实现了ImportSelector,该接口的方法selectImports能够将返回的类的全限定类名集合中的所有类都注册到Spring容器中。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

 
   @Override
   public String[] selectImports(AnnotationMetadata annotationMetadata) {
      if (!isEnabled(annotationMetadata)) {
         return NO_IMPORTS;
      }
      AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
            .loadMetadata(this.beanClassLoader);
            
      //重点getAutoConfigurationEntry方法
      AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
            annotationMetadata);
      return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
   }
}
复制代码

接着查看getAutoConfigurationEntry方法,如下:

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   //重点getCandidateConfigurations方法
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   configurations = removeDuplicates(configurations);
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   configurations = filter(configurations, autoConfigurationMetadata);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}
复制代码

接着getCandidateConfigurations方法,如下:

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;
}
复制代码

接着查看SpringFactoriesLoaderloadFactoryNames方法

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
   String factoryTypeName = factoryType.getName();
   return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}


private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
   MultiValueMap<String, String> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   /**
    * 加载spring.factories文件,封装成Map返回
    */
   try {
      //FACTORIES_RESOURCE_LOCATION的值为:META-INF/spring.factories
      Enumeration<URL> urls = (classLoader != null ?
            classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      result = new LinkedMultiValueMap<>();
      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();
            for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
               result.add(factoryTypeName, factoryImplementationName.trim());
            }
         }
      }
      cache.put(classLoader, result);
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}
复制代码

image.png

image.png

spring.factories文件的内容如下:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# 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.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
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,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
复制代码

综上AutoConfigurationImportSelector类的作用就是将META-INF/spring.factories中默认的全限定类名对应的类注册到Spring容器中。

@AutoConfigurationPackage

该注解的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}
复制代码

接着重点查看@Import(AutoConfigurationPackages.Registrar.class)中的AutoConfigurationPackages.Registrar

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   /**
    * 该方法能够手动往bdMap中添加BeanDefinition
    */
   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      //metadata封装了我们的@SpringBootApplication信息
      register(registry, new PackageImport(metadata).getPackageName());
   }
}
复制代码

Registrar实现了ImportBeanDefinitionRegistrar接口,该接口的registerBeanDefinitions方法能够往Spring的容器BeanDefinitionMap中注册自定义的BeanDefinition对象。又由于metadata封装了我们的@SpringBootApplication信息,调用getPackageName()返回带有@SpringBootApplication注解类的包名,所以@AutoConfigurationPackage注解的作用就是扫描启动类(即带有@SpringBootApplication注解的类)所在的包及其子包下的类,封装成BeanDefinition对象,注册到Spring容器BeanDefinitionMap中。

image.png

image.png

自定义Starter

自定义starter

1.首先创建工程zdy-spring-boot-starter,引入依赖如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
        <version>2.2.2.RELEASE</version>
    </dependency>
</dependencies>
复制代码

2.编写javaBean,如下:

/**
 * @EnableConfigurationProperties注解就是开启@ConfigurationProperties注解
 */
@EnableConfigurationProperties(SimpleBean.class)//或者直接使用@Component注解
@ConfigurationProperties(prefix = "simplebean")
public class SimpleBean {

    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "SimpleBean{" +
                "id=" + id +
                ", name='" + name + ''' +
                '}';
    }
}
复制代码

3.编写配置类MyAutoConfifiguration,如下:

@Configuration
@ConditionalOnClass(SimpleBean.class)//当类路径下即classpath下有指定的类(即SimpleBean)的情况就会自动配置
public class MyAutoConfiguration {
    
    static {
        System.out.println("MyAutoConfiguration init....");
    }
    
    @Bean
    public SimpleBean simpleBean() {
        return new SimpleBean();
    }
}
复制代码

4.在resources下创建/META-INF/spring.factories

image.png

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lagou.config.MyAutoConfiguration
复制代码

使用starter

1.引入上面自定义starter工程的依赖,如下:

<dependency>
    <groupId>com.lagou</groupId>
        <artifactId>zdy-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
复制代码

2.在application.properties中配置属性值

simplebean.id=1
simplebean.name=自定义starter
复制代码

3.编写测试方法

@RunWith(SpringRunner.class)  //测试启动器,并加载spring boot测试注解
@SpringBootTest
        //标记该类为spring boot单元测试类,并加载项目的applicationContext上下文环境
class Springboot01DemoApplicationTests {
//测试自定义starter

    @Autowired
    private SimpleBean simpleBean;

    @Test
    public void zdyStarterTest(){
        System.out.println(simpleBean);
    }
}
复制代码

启动源码

大家都知道SpringBoot项目启动主要是依靠启动类中的run方法,例如下面的Springboot01DemoApplication

@SpringBootApplication
public class Springboot01DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(Springboot01DemoApplication.class, args);
    }
}
复制代码

接着查看run方法,如下:

/**
 * 调用静态类,参数primarySource对应的就是SpringbootDemoApplication.class以及main方法中的args
 */
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
   return run(new Class<?>[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   //SpringApplication的启动由两部分组成:
   //1. 实例化SpringApplication对象
   //2. run(args):调用run方法
   return new SpringApplication(primarySources).run(args);
}
复制代码

从以上代码可知,run方法主要分为两个步骤,一个是SpringApplication实例化,另外一个是调用SpringApplicationrun方法。

SpringApplication实例化

首先来查看上面代码中的new SpringApplication(primarySources)primarySources参数就是run方法传进来的项目启动类Springboot01DemoApplication

public SpringApplication(Class<?>... primarySources) {
   this(null, primarySources);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

   this.sources = new LinkedHashSet();
   this.bannerMode = Mode.CONSOLE;
   this.logStartupInfo = true;
   this.addCommandLineProperties = true;
   this.addConversionService = true;
   this.headless = true;
   this.registerShutdownHook = true;
   this.additionalProfiles = new HashSet();
   this.isCustomEnvironment = false;
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");

   //项目启动类 SpringbootDemoApplication.class设置为属性存储起来
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

   //设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
   this.webApplicationType = WebApplicationType.deduceFromClasspath();

   // 设置初始化器(Initializer),最后会调用这些初始化器
   //所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,在Spring上下文被刷新之前进行初始化的操作
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

   // 设置监听器(Listener)
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

   // 初始化 mainApplicationClass 属性:用于推断并设置项目main()方法启动的主程序启动类
   this.mainApplicationClass = deduceMainApplicationClass();
}
复制代码

从上述源码可以看出,SpringApplication的初始化过程主要包括4部分,具体说明如下。

  • (1)this.webApplicationType = WebApplicationType.deduceFromClasspath()用于判断当前webApplicationType应用的类型。deduceFromClasspath()方法用于查看Classpath类路径下是否存在某个特征类,从而判断当前webApplicationType类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
/**
 * @return 从 classpath 上,判断 Web 应用类型。
 */
static WebApplicationType deduceFromClasspath() {
       // WebApplicationType.REACTIVE 类型  通过类加载器判断REACTIVE相关的Class是否存在
   if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) // 判断REACTIVE相关的Class是否存在
         && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
         && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
      return WebApplicationType.REACTIVE;
   }
   // WebApplicationType.NONE 类型
   for (String className : SERVLET_INDICATOR_CLASSES) {
      if (!ClassUtils.isPresent(className, null)) { // 不存在 Servlet 的类
         return WebApplicationType.NONE;
      }
   }
   /**
    * 最终会调用改行代码
    */
   // WebApplicationType.SERVLET 类型。可以这样的判断的原因是,引入 Spring MVC 时,是内嵌的 Web 应用,会引入 Servlet 类,即DispatcherServlet
   return WebApplicationType.SERVLET;
}
复制代码
  • (2)this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))用于SpringApplication应用的初始化器设置。在初始化器设置过程中,会使用Spring类加载器SpringFactoriesLoader从META-INF/spring.factories类路径下的META-INF下的spring.factores文件中获取所有可用的应用初始化器类ApplicationContextInitializer。
/**
     * 获得指定类类对应的对象们。
     *
     * @param type 指定类
     * @param <T> 泛型
     * @return 对象们
    * // // 这里的入参type是:org.springframework.context.ApplicationContextInitializer.class
r
    * //            ApplicationListener
     */
   private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
      return getSpringFactoriesInstances(type, new Class<?>[] {});
   }

   private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
         Class<?>[] parameterTypes, Object... args) {
      ClassLoader classLoader = getClassLoader();
      // Use names and ensure unique to protect against duplicates
        // 加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组
      Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
      //org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
      //org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
      // 根据names来进行实例化
      List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
      // 对实例进行排序
      AnnotationAwareOrderComparator.sort(instances);
      return instances;
   }
复制代码

接着查看SpringFactoriesLoader.loadFactoryNames(type, classLoader),如下:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    
}

/**
 * 从META-INF/spring.factories中加载类型为org.springframework.context.ApplicationContextInitializer的bean
 */
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
   MultiValueMap<String, String> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   try {
      //FACTORIES_RESOURCE_LOCATION为META-INF/spring.factories
      Enumeration<URL> urls = (classLoader != null ?
            classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      result = new LinkedMultiValueMap<>();
      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();
            for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
               result.add(factoryTypeName, factoryImplementationName.trim());
            }
         }
      }
      cache.put(classLoader, result);
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}
复制代码

image.png

从META-INF/spring.factories类路径下的META-INF下的spring.factores文件中获取所有可用的应用初始化器类ApplicationContextInitializer,一共有两个,如下:

org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
复制代码
  • (3)this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class))用于SpringApplication应用的监听器设置。监听器设置的过程与上一步初始化器设置的过程基本一样,也是使用SpringFactoriesLoader从META-INF/spring.factories类路径下的META-INF下的spring.factores文件中获取所有可用的监听器类ApplicationListener。

代码跟第二步的类似,从META-INF/spring.factories类路径下的META-INF下的spring.factores文件中获取所有可用的监听器类ApplicationListener。,如下图所示

image.png

  • (4)this.mainApplicationClass = this.deduceMainApplicationClass()用于推断并设置项目main()方法启动的主程序启动类

run方法

接着查看run方法,如下:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   //SpringApplication的启动由两部分组成:
   //1. 实例化SpringApplication对象
   //2. run(args):调用run方法
   return new SpringApplication(primarySources).run(args);
}

/**
 * 一共9个步骤
 */
public ConfigurableApplicationContext run(String... args) {
    // 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   // 初始化应用上下文和异常报告集合
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   // 配置 headless 属性
   configureHeadlessProperty();

   /**
    * 重点在下面额九个步骤
    */

   //   (1)获取并启动监听器
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
       // 创建  ApplicationArguments 对象 初始化默认应用参数类
      // args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

      //(2)项目运行环境Environment的预配置
      // 创建并配置当前SpringBoot应用将要使用的Environment
      // 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

      configureIgnoreBeanInfo(environment);
      // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
      Banner printedBanner = printBanner(environment);

      // (3)创建Spring容器
      context = createApplicationContext();
      // 获得异常报告器 SpringBootExceptionReporter 数组
      //这一步的逻辑和实例化初始化器和监听器的一样,
      // 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);


      // (4)Spring容器前置处理
      //这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);

      // (5):刷新容器
      refreshContext(context);

      // (6):Spring容器后置处理
      //扩展接口,设计模式中的模板方法,默认为空实现。
      // 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
      afterRefresh(context, applicationArguments);
      // 停止 StopWatch 统计时长
      stopWatch.stop();
      // 打印 Spring Boot 启动的时长日志。
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      // (7)发出结束执行的事件通知
      listeners.started(context);

      // (8):执行Runners
      //用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
      //Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
      //Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
      callRunners(context, applicationArguments);
   } catch (Throwable ex) {
       // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

       //   (9)发布应用上下文就绪事件
   //表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
   // 这样整个Spring Boot项目就正式启动完成了。
   try {
      listeners.running(context);
   } catch (Throwable ex) {
           // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
           handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
    //返回容器
   return context;
}
复制代码

从上述源码可以看出,项目初始化启动过程大致包括以下部分:

  • 第一步:获取并启动监听器

this.getRunListeners(args)和listeners.starting()方法主要用于获取SpringApplication实例初始化过程中初始化的SpringApplicationRunListener监听器并运行。

  • 第二步:根据SpringApplicationRunListeners以及参数来准备环境

this.prepareEnvironment(listeners, applicationArguments)方法主要用于对项目运行环境进行预设置,同时通过this.configureIgnoreBeanInfo(environment)方法排除一些不需要的运行环境

  • 第三步:创建Spring容器根据webApplicationType进行判断, 确定容器类型,如果该类型为SERVLET类型,会通过反射装载

对应的字节码,也就是AnnotationConfigServletWebServerApplicationContext,接着使用之前初始化设置的context(应用上下文环境)、environment(项目运行环境)、listeners(运行监听器)、applicationArguments(项目参数)和printedBanner(项目图标信息)进行应用上下文的组装配置,并刷新配置

  • 第四步:Spring容器前置处理

这一步主要是在容器刷新之前的准备动作。设置容器环境,包括各种变量等等,其中包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础

  • 第五步:刷新容器

开启刷新spring容器,通过refresh方法对整个IOC容器的初始化(包括bean资源的定位,解析,注册等等),同时向JVM运行时注册一个关机钩子,在JVM关机时会关闭这个上下文,除非当时它已经关闭

  • 第六步:Spring容器后置处理

扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理。

  • 第七步:发出结束执行的事件

获取EventPublishingRunListener监听器,并执行其started方法,并且将创建的Spring容器传进去了,创建一个ApplicationStartedEvent事件,并执行ConfigurableApplicationContext 的publishEvent方法,也就是说这里是在Spring容器中发布事件,并不是在SpringApplication中发布事件,和前面的starting是不同的,前面的starting是直接向SpringApplication中的监听器发布启动事件。

  • 第八步:执行Runners

用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序。其中,Spring Boot提供的执行器接口有ApplicationRunner 和CommandLineRunner两种,在使用时只需要自定义一个执行器类实现其中一个接口并重写对应的run()方法接口,然后Spring Boot项目启动后会立即执行这些特定程序

下面,通过一个Spring Boot执行流程图,让大家更清晰的知道Spring Boot的整体执行流程和主要启动阶段:

image.png

分类:
后端
标签:
分类:
后端
标签: