Automatic configuration
Development using Spring Boot is much simpler, easier and faster than previous xmL-based configuration development. This is all thanks to Spring Boot’s automatic configuration. The following is to analyze the operation principle of automatic configuration by reading the source code.
Open the @SpringBootApplication annotation source for the bootstrap class.
@SpringBootApplication
public class Springboot01DemoApplication {
public static void main(String[] args) { SpringApplication.run(Springboot01DemoApplication.class, args); }}Copy the code
Looking at the source of the @SpringBootApplication annotation, we can see that the @SpringBootApplication annotation is actually a composite annotation as follows:
@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(a) default true;
}
Copy the code
Yuan notes
-
@target (elementtype.type) : indicates that the current annotation can only be annotated on a class.
-
@Retention(retentionPolicy.runtime) : Indicates that the lifetime of the current annotation is source code, bytecode, and JVM RUNTIME.
-
@documented: annotation information that represents the current annotation will be displayed in the javaAPI document
-
Inherited: indicates that a child class of the annotated class inherits the annotations in this class
@SpringBootConfiguration
This annotation is exactly the same as the @Configuration annotation. The annotation class is a Spring configuration class
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods(a) default true;
}
Copy the code
@ComponentScan
Use to specify the packets to be scanned for the current application.
@EnableAutoConfiguration
This annotation is used to complete automatic configuration and is the core annotation of Spring Boot. Details will be explained later. The @enablexxx annotation is mainly explained here. The @enablexxx annotation is usually used to enable a function to avoid the introduction of simplified configuration code. The @enablexxx annotation is usually combined with an @import annotation. The @import annotation is used to Import the specified class, which is usually a configuration class. There are three common ways to import configuration classes:
-
A. Import the Configuration class directly. The class specified in @import usually ends with Configuration, and @Configuration is annotated on the class to indicate that the current class is JavaConfig. For example, the @import annotation for the @enablesCheduling annotation to start a scheduled task.
-
B. Select a configuration class based on the condition. The class specified in @import usually ends with ConfigurationSelector and implements the ImportSelector interface, indicating that the current class will Import different configuration classes based on the condition. For example, @import for @enablecaching, which is used to EnableCaching.
-
C, dynamically register beans, @ Import in the specified class usually end with the Registrar, and the class implements the ImportBeanDefinitionRegistrar interface, is used to represent the code if runtime USES to the configuration class, then the system will automatically Import it. For example, the @import of the @enableAspectJAutoProxy annotation used to enable AspectJ automatic proxying.
View the @enableAutoConfiguration annotation as follows:
@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 {};
}
Copy the code
This annotation is used to complete automatic configuration, is the core annotation of Spring Boot, and is a composite annotation. Automatic configuration refers to the assembly of user-defined classes and classes used by the framework itself. The most important notes are two:
-
@autoConfigurationPackage: Used to import and assemble user-defined classes that are automatically scanned in packages
-
@import: Class used to Import and assemble the framework itself
@Import
Due to theAutoConfigurationImportSelector
Class is implemented indirectlyImportSelector
, the interface methodselectImports
The ability to register all classes in the fully qualified set of class names of returned classes into the Spring container.
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);
/ / key getAutoConfigurationEntry method
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
returnStringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }}Copy the code
Then check getAutoConfigurationEntry method, as follows:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if(! isEnabled(annotationMetadata)) {return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
/ / key getCandidateConfigurations method
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);
}
Copy the code
Then getCandidateConfigurations method, as follows:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
/ / the key
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;
}
Copy the code
Then look at the loadFactoryNames method of the SpringFactoriesLoader
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;
}
/** * Load the spring.factories file, wrap it as a Map and return */
try {
// The FACTORIES_RESOURCE_LOCATION value is: meta-INF /spring.factoriesEnumeration<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); }}Copy the code
The content of the spring.factories file looks like this:
# 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
Copy the code
In conclusion AutoConfigurationImportSelector class is the role of the meta-inf/spring. Factories in default the fully qualified class name of the corresponding class registered in the spring container.
@AutoConfigurationPackage
This annotation is defined as follows:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
Copy the code
Then the key view @ Import (AutoConfigurationPackages. The Registrar. The class) in the AutoConfigurationPackages. The Registrar class
static class Registrar implements ImportBeanDefinitionRegistrar.DeterminableImports {
/** * This method can manually add BeanDefinition */ to bdMap
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Metadata encapsulates our @SpringBootApplication information
register(registry, newPackageImport(metadata).getPackageName()); }}Copy the code
The Registrar has realized ImportBeanDefinitionRegistrar interface, The registerBeanDefinitions method of this interface registers custom BeanDefinition objects in Spring’s container BeanDefinitionMap. And since metadata encapsulates our @SpringBootApplication information, getPackageName() returns the package name with the @SpringBootApplication annotation class, So what the @AutoConfigurationPackage annotation does is scan the package that starts the class (that is, the class with the @SpringBootApplication annotation) and its subpackages, encapsulating them as BeanDefinition objects, Register with the Spring container BeanDefinitionMap.
Custom Starter
Custom starter
1. Create project zdy-spring-boot-starter and introduce the following dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.2.2. RELEASE</version>
</dependency>
</dependencies>
Copy the code
2. Write a javaBean as follows:
/ * * *@EnableConfigurationPropertiesAnnotation is opening@ConfigurationPropertiesNote * /
@EnableConfigurationProperties(SimpleBean.class)// Or just use the @component annotation
@ConfigurationProperties(prefix = "simplebean")
public class SimpleBean {
private int id;
private String name;
public int getId(a) {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName(a) {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString(a) {
return "SimpleBean{" +
"id=" + id +
", name='" + name + ' ''+'}'; }}Copy the code
Write configuration class MyAutoConfifiguration as follows:
@Configuration
@ConditionalOnClass(SimpleBean.class)// This is automatically configured when there is a specified class in the classpath (i.e. SimpleBean)
public class MyAutoConfiguration {
static {
System.out.println("MyAutoConfiguration init....");
}
@Bean
public SimpleBean simpleBean(a) {
return newSimpleBean(); }}Copy the code
Create/meta-INF /spring.factories under Resources
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lagou.config.MyAutoConfiguration
Copy the code
Use the starter
1. Import the above custom starter project dependencies as follows:
<dependency>
<groupId>com.lagou</groupId>
<artifactId>zdy-spring-boot-starter</artifactId>
<version>1.0 the SNAPSHOT</version>
</dependency>
Copy the code
2. Configure the property value in application.properties
simplebean.id=1
simplebean.name=Custom starter
Copy the code
3. Write test methods
@RunWith(SpringRunner.class) // Test the launcher and load the Spring Boot test annotations
@SpringBootTest
// Mark the class as a Spring Boot unit test class and load the project's applicationContext
class Springboot01DemoApplicationTests {
// Test the custom starter
@Autowired
private SimpleBean simpleBean;
@Test
public void zdyStarterTest(a){ System.out.println(simpleBean); }}Copy the code
Start the source
Everyone know SpringBoot project start mainly rely on start run method in the class, such as Springboot01DemoApplication next class
@SpringBootApplication
public class Springboot01DemoApplication {
public static void main(String[] args) { SpringApplication.run(Springboot01DemoApplication.class, args); }}Copy the code
Then look at the run method as follows:
/ * * * call static class, parameters corresponding is SpringbootDemoApplication primarySource. Class, as well as the main method of the args * /
public static ConfigurableApplicationContext run(Class
primarySource, String... args) {
return run(newClass<? >[] { primarySource }, args); }public static ConfigurableApplicationContext run(Class
[] primarySources, String[] args) {
// The SpringApplication startup consists of two parts:
//1. Instantiate the SpringApplication object
//2. run(args) : call the run method
return new SpringApplication(primarySources).run(args);
}
Copy the code
As can be seen from the above code, the run method is mainly divided into two steps, one is the instantiation of SpringApplication, and the other is the run method that calls SpringApplication.
SpringApplication instantiation
First of all to see in the code above new SpringApplication (primarySources), primarySources parameter is run method incoming class Springboot01DemoApplication of project startup.
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");
/ / project startup SpringbootDemoApplication. Class is set to the attribute stored
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// Set the application type to SERVLET application (traditional MVC application before Spring 5) or REACTIVE application (WebFlux interactive application since Spring 5)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// Set initializers, which will eventually be called
/ / the so-called initializer is org. Springframework. Context. ApplicationContextInitializer implementation class, initialized before the Spring context is refresh operation
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// Set the Listener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// Initializes the mainApplicationClass property: used to infer and set the main program launcher class started by the project main() method
this.mainApplicationClass = deduceMainApplicationClass();
}
Copy the code
As can be seen from the above source code, the initialization process of SpringApplication mainly includes four parts, as described below.
- (1) this. WebApplicationType = webApplicationType. DeduceFromClasspath () is used to determine the current webApplicationType type of application. DeduceFromClasspath () is used to see if a characteristic class exists in the Classpath Classpath. To determine whether the current webApplicationType is a SERVLET application (traditional MVC application before Spring 5) or a REACTIVE application (WebFlux interactive application since Spring 5).
/ * * *@returnDetermine the Web application type from the CLASspath. * /
static WebApplicationType deduceFromClasspath(a) {
/ / WebApplicationType. REACTIVE type Through the Class loader judgment REACTIVE related Class exists
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) // Check whether classes related to REACTIVE exist
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
/ / WebApplicationType. NONE type
for (String className : SERVLET_INDICATOR_CLASSES) {
if(! ClassUtils.isPresent(className,null)) { // There is no class for Servlet
returnWebApplicationType.NONE; }}/** * finally calls the line change code */
/ / WebApplicationType. SERVLET type. The reason for this is that when Spring MVC is introduced, it is an embedded Web application that introduces the Servlet class, DispatcherServlet
return WebApplicationType.SERVLET;
}
Copy the code
- (2) enclosing setInitializers (enclosing getSpringFactoriesInstances (ApplicationContextInitializer. Class)) for SpringApplication initializer Settings. During initializer setup, The Spring classloader SpringFactoriesLoader is used to fetch all available application initializers ApplicationCo from the spring.factores file in the meta-INF/Spring. factories class path NtextInitializer.
/** * returns the objects corresponding to the specified class. * *@paramType Specifies the class *@param< T > generic *@returnSubjects * / / / / the into the participation type: org. Springframework. Context. ApplicationContextInitializer. Class r * / / ApplicationListener * /
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, newClass<? > [] {}); }private <T> Collection<T> getSpringFactoriesInstances(Class
type, Class
[] parameterTypes, Object... args)
{
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// Load an array of class names in 'meta-INF /spring.factories' of the specified type
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
//org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
// Instantiate according to names
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// Sort the instances
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
Copy the code
Then see SpringFactoriesLoader. LoadFactoryNames (type, this), as follows:
public static List<String> loadFactoryNames(Class<? > factoryType,@Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
/ * * * from the meta-inf/spring. The type of load in the factories for 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 for the meta-inf/spring. The factoriesEnumeration<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); }}Copy the code
From the meta-inf/spring. Factories under the class path of the spring under the meta-inf. Factores file to get all of the available application ApplicationContextInitializer initializer class, a total of two, is as follows:
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
Copy the code
- (3) enclosing setListeners (enclosing getSpringFactoriesInstances (ApplicationListener. Class)) listener for SpringApplication application Settings. The listener setup is basically the same as the initializer setup in the previous step, Also use the SpringFactoriesLoader to get all the available listener classes applicationListeners from the spring.factores file in the meta-INF/Spring. factories class path.
The code is similar to step 2, and gets all available listener classes ApplicationListener from the spring.factores file in the meta-INF /spring.factories class path. , as shown in the figure below
- (4) enclosing mainApplicationClass = this. DeduceMainApplicationClass () is used to infer and set up the project main () method to start the main program start class
Run method
Then look at the run method as follows:
public static ConfigurableApplicationContext run(Class
[] primarySources, String[] args) {
// The SpringApplication startup consists of two parts:
//1. Instantiate the SpringApplication object
//2. run(args) : call the run method
return new SpringApplication(primarySources).run(args);
}
/** * there are 9 steps */
public ConfigurableApplicationContext run(String... args) {
// Create a StopWatch object and start it. StopWatch collects statistics about the run startup duration.
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// Initializes the application context and exception report collection
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// Configure the headless attribute
configureHeadlessProperty();
/** * focus on the next denomination of nine steps */
// (1) Get and start listener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// create an ApplicationArguments object to initialize the default ApplicationArguments class
// Args is the command line argument that starts the Spring application and can be accessed in the Spring application. Such as: -- server. The port = 9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// (2) Pre-configuration of the project operation Environment
// Create and configure the Environment to be used by the current SpringBoot application
/ / and traverse call all SpringApplicationRunListener environmentPrepared () method
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// Prepare the Banner printer - is the ASCII art font printed on the console when Spring Boot is started
Banner printedBanner = printBanner(environment);
// Create a Spring container
context = createApplicationContext();
/ / get abnormality report SpringBootExceptionReporter array
// The logic of this step is the same as instantiating initializers and listeners,
/ / is by calling getSpringFactoriesInstances exception class name of the method to get the configuration and instantiate all exception handling classes.
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// (4) Spring container preprocessing
// This step is mainly a preparatory action before the container is refreshed. This includes a very critical operation: injecting the startup class into the container to set the stage for enabling automated configuration later.
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// (5) : refresh the container
refreshContext(context);
// (6) : Spring container postprocessing
// Extension interface, template method in design mode, default empty implementation.
// You can override this method if you have custom requirements. Such as printing some start-end logs, or some other post-processing
afterRefresh(context, applicationArguments);
// Stop StopWatch Statistics duration
stopWatch.stop();
// Prints the Spring Boot startup duration log.
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// (7) Issue an event notification to end execution
listeners.started(context);
// (8) : execute Runners
// Used to call the custom executor XxxRunner class in the project to execute specific programs immediately after the project is started
// The Runner Runner is used to perform some business initialization operations when the service is started. These operations are performed only once after the service is started.
Spring Boot provides two service interfaces, ApplicationRunner and CommandLineRunner
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// If an exception occurs, handle it and throw an IllegalStateException
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
// (9) Publish the application context ready event
/ / said in front of all initialization start without a problem, use run listener SpringApplicationRunListener run continuously configured application context ApplicationContext,
// The whole Spring Boot project is officially started.
try {
listeners.running(context);
} catch (Throwable ex) {
// If an exception occurs, handle it and throw an IllegalStateException
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
// Return the container
return context;
}
Copy the code
It can be seen from the above source code that the project initialization startup process roughly includes the following parts:
- Step 1: Get and start the listener
Enclosing getRunListeners (args) and listeners. Starting () method is mainly used to obtain SpringApplication instance initialization in the process of initialization SpringApplicationRunListener listener and running.
- The second step: according to SpringApplicationRunListeners and parameters to prepare the environment
The this.prepareEnvironment(Listeners, applicationArguments) method is used to pre-set the running environment of the project. At the same time, through this. ConfigureIgnoreBeanInfo (environment) method to exclude some don’t need to run environment
- Step 3: Create the Spring container. Determine the type of the container based on webApplicationType. If the type is SERVLET, it will be loaded by reflection
The corresponding bytecode, namely AnnotationConfigServletWebServerApplicationContext, Context, Environment, Listeners, applicationArguments, and printedBanner are then used to create the process Text to assemble the configuration and refresh the configuration
- Step 4: Spring container preprocessing
This step is primarily a preparatory action before the container is refreshed. Setting up the container environment, including variables, and so on, includes a very critical operation: injecting the start class into the container, which sets the stage for enabling automated configuration later
- Step 5: Refresh the container
Enable the refresh of the Spring container, initialize the entire IOC container through the refresh method (including the location, parsing, registration, and so on of bean resources), and register a shutdown hook with the JVM runtime that closes the context when the JVM shuts down, unless it is already closed
- Step 6: Spring container post-processing
Extension interface, template method in design mode, default to empty implementation. You can override this method if you have custom requirements. Such as printing some start-end logs, or some other post-processing.
- Step 7: Emit an event to end execution
Get EventPublishingRunListener listeners, and perform its started method, and will create the Spring container has handed in, create a ApplicationStartedEvent events, And perform ConfigurableApplicationContext publishEvent method, that is to say, this is in the Spring container launch event, not in SpringApplication publishing events, and in front of the starting is different, The previous starting is to publish startup events directly to listeners in the SpringApplication.
- Step 8: Execute Runners
Used to call the custom executor XxxRunner class in a project to execute specific programs immediately after the project is started. Spring Boot provides two types of executor interfaces, ApplicationRunner and CommandLineRunner. You only need to define an executor class to implement one of these interfaces and rewrite the corresponding Run () method interface. These specific programs are then executed immediately after the Spring Boot project starts
Below, through a Spring Boot execution flow chart, let us know the overall Spring Boot execution process and the main startup stage more clearly: