This blog mainly tells about springboot how to simplify a lot of configuration to us, and then follow the source code of their own packaging a Starter, first of all we need from two places, the first is the start of springboot dependence, the second is springboot automatic assembly;

Start relying on

We need to introduce the spring-boot-starter-web dependency when creating a Springboot project;

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
Copy the code

So this dependency, if we click on it, we can see that this startup dependency actually integrates common Web dependencies, such as Spring-Web, Spring-WebMVC

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <version>2.1.4. The RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-json</artifactId>
  <version>2.1.4. The RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <version>2.1.4. The RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.0.16. The Final</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.1.6. RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.1.6. RELEASE</version>
  <scope>compile</scope>
</dependency>
Copy the code

The startup dependency of Spring Boot is simply another encapsulation of commonly used dependencies, which is convenient for us to introduce and simplify the configuration of POP.xml. But more importantly, it gives the management of dependencies to Spring Boot. We do not need to pay attention to whether different versions of different dependencies conflict. Spring Boot has helped us consider the good, we can use it!

Before using Spring Boot’s bootstrap dependency, we need to add the configuration in pom.xml:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4. The RELEASE</version>
    <relativePath/> <! -- lookup parent from repository -->
</parent>
Copy the code

That is to let pom. XML inherit from Spring Boot pom. XML, and Spring Boot pom. XML defines the commonly used framework dependency and the corresponding version number, we do not need to worry about version conflicts;

Automatically.

First of all, we know that SpringBoot Boot requires a Boot Boot class, which in addition to being the entry point to the application, also plays an important role in configuring Spring Boot.

@SpringBootApplication
public class Application {

    public static void main(String[] args) { SpringApplication.run(Application.class, args); }}Copy the code

If you look at the @SpringBootApplication annotation, if you click on this annotation, you can see that it plays the role of multiple annotations, which also shows the hierarchical nature of annotations;

@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 {};
    
    / /...
}
Copy the code

The @SpringBootConfiguration annotation and the @ComponentScan annotation, the former is the @Configuration annotation, is to declare this class as a Configuration class, and the latter is to enable automatic scanning components.

The @enableAutoConfiguration annotation is used to enable Spring Boot autoconfiguration.

@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

We focus on analysis the @ Import ({AutoConfigurationImportSelector. Class}) this annotation, we know that @ is the role of the Import components added to the Spring container, And in here, that is, AutoConfigurationImportSelector this component will be added to the Spring container. Namely AutoConfigurationImportSelector statement into a Bean;

We focus on analysis the @ Import annotations AutoConfigurationImportSelector in class;

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
		if(! isEnabled(annotationMetadata)) {return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		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);
	}


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;
	}
Copy the code

In getAutoConfigurationEntry approach scans all the jar package under the ClassPath spring. Factories file, Fetch all the values in the Spring. factories file with key as EnableAutoConfiguration, and then these values are the fully qualified names of the class that spring Boot uses to load the class (reflection). Add these auto-configuration classes to the Spring container.

The jar package named spring-boot-autoconfigure-2.1.4.release.jar was loaded with the spring-.Factories file. The jar package was loaded with the EnableAutoConfiguration key

This jar package has auto-configuration classes named after xxxAutoConfiguration. These auto-configuration classes include auto-configuration classes for common frameworks such as AOP, Mongo, Redis, and Web. It can basically meet the needs of our daily development. For example, we need to use AOP in the program, directly introduce the corresponding dependency can!

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>
Copy the code

Let’s take a relatively simple configuration class for analysis and see how it plays the role of configuration; We take HttpEncodingAutoConfiguration; Part of the code is as follows:

// Declare this class as a configuration class
@Configuration 
// Enable ConfigurationProperties and bind the configuration file to httpProperties.class
@EnableConfigurationProperties({HttpProperties.class})
// Auto-configuration classes only work in Web applications
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
/ / only exist CharacterEncodingFilter. Class cases automatic configuration class to take effect
@ConditionalOnClass({CharacterEncodingFilter.class})
// Check whether a configuration exists in the configuration file. Spring.http. encoding takes effect only if it is enabled, or if it is not.
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
    private final Encoding properties;

    public HttpEncodingAutoConfiguration(HttpProperties properties) {
        this.properties = properties.getEncoding();
    }

    // Add the character encoding filter component to the Spring container
    @Bean
    // A config or bean declaration using the annotation is instantiated into the spring container only if the specified class does not already exist
    @ConditionalOnMissingBean
    public CharacterEncodingFilter characterEncodingFilter(a) {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
        return filter;
    }
    
    @Bean
public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer(a) {
    return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
}
Copy the code

Configuration: This annotation declares this class to be a Configuration class (just as we normally write Configuration classes, with this annotation on the class).

EnableConfigurationProperties: ConfigurationProperties: bind the configuration file to the HttpProperties. Class class and associate the values of the configuration file with the variables of HttpProperties. You can go to httpProperties.class,

@ConfigurationProperties(
    prefix = "spring.http"
)

public static final Charset DEFAULT_CHARSET;
private Charset charset;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
private Map<Locale, Charset> mapping;
Copy the code

The value of the application. Properties file prefixed with spring. HTTP is associated with the httpProperties.class variable. The properties we can set are Charset, Force, forceRequest, forceResponse, and Mapping. The ConfigurationProperties annotation also injects the HttpProperties class into the Spring container as a bean object, because in general, For example, the default springBoot package scan path is xxxxxxapplication.java and all its subpackages, but some third-party JAR beans are obviously not scannable. This annotation comes in handy. Of course, you might say, I’ll just use @ComponentScan. The difference between these two annotations is: @ ComponentScan premise is you want bean bean existing in the container, and @ EnableConfigurationProperties want let container automatically discover your class and register as a bean. This means that in addition to using the configuration information provided by Spring Boot by default, we can also specify the configuration information through the configuration file.

  • ConditionalOnWebApplication:The purpose of this annotation is to automatically configure classes only for Web applications.
  • ConditionalOnClass:Only in existenceCharacterEncodingFilterThis class is the case for auto-configuration classes to take effect.
  • ConditionalOnProperty:It checks whether a configuration exists in the configuration file. Spring.http. encoding takes effect only if it is enabled, or if it is not.
  • @ConditionalOnMissingBean:The Config or bean declaration that uses the annotation is instantiated into the container only if the specified class does not exist in the Spring container

ConditionalXXXX (ConditionalXXXX) ConditionalXXXX (ConditionalXXXX) ConditionalXXXX (ConditionalXXXX) ConditionalXXXX (ConditionalXXXX) ConditionalXXXX (ConditionalXXXX) ConditionalXXXX

The characterEncodingFilter method, which follows, creates a characterEncodingFilter object, which is the characterEncodingFilter, sets the properties, and then returns the object, using the @bean annotation, and adds the returned object to the Spring container. Now that the character encoding filter component is configured, normally we would need to do the following configuration in web.xml:

 <filter>
       <filter-name>springUtf8Encoding</filter-name>
       <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
       <init-param>
           <param-name>encoding</param-name>
           <param-value>utf-8</param-value>
       </init-param>
       <init-param>
           <param-name>forceEncoding</param-name>
           <param-value>true</param-value>
       </init-param> 
    </filter>
    <filter-mapping>
       <filter-name>springUtf8Encoding</filter-name>
       <url-pattern>/ *</url-pattern>
   </filter-mapping>
Copy the code

Now that we have analyzed the principle, let’s package ourselves a spring-boot-starter-AOP similar to the one above

Encapsulating a Starter

1. Development specification of SpringBoot Starter

  • 1, naming usespring-boot-starter-xxx, includingxxxIs our specific package name if integratedSpring CloudUse thespring-cloud-starter-xxx
  • 2, usually need to prepare twojarFiles, one of which contains no code and is only responsible for importing the relevant JAR files, and the other contains the core code

For example, the starter integrated with NacOS and Spring Cloud is shown below:

For more on the Starter specifications, check out the documentation on the official website

The Starter development step

Let’s create a project named Happy-spring-boot-starter and introduce dependencies:

	<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
    	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
   </dependencies>
    <dependencyManagement>
        <! -- We are based on Springboot application -->
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.1.4. The RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
Copy the code

Since we need the annotations provided by Springboot and the automatic configuration provided by Springboot, we have to introduce two dependencies, spring-boot-autoconfigure and spring-boot-dependencies.

3. Create automatic configuration classes

In general, we might want to pre-inject some of our beans when SpringBoot starts, in which case we create our own auto-configuration class, typically xxxxAutoConfiguration. Here is similar to the above HttpEncodingAutoConfiguration, below we imitate HttpEncodingAutoConfiguration create a OkayStarterAutoConfiguration configuration classes;

@Configuration
@EnableConfigurationProperties(OkayProperties.class)
@ConditionalOnClass(Okay.class)
@ConditionalOnWebApplication
public class OkayStarterAutoConfiguration {

    
    @Bean
    @ConditionalOnMissingBean
    /** * This okay bean takes effect only when the okay. Config. enable=true configuration exists */
    @ConditionalOnProperty(prefix = "okay.config", name = "enable", havingValue = "true")
    public Okay defaultStudent(OkayProperties okayProperties) {
        Okay okay = new Okay();
        okay.setPlatform(okayProperties.getPlatform());
        okay.setChannel(okayProperties.getChannel());
        okay.setEnable(okayProperties.getEnable());
        returnokay; }}Copy the code

The meaning of each note here has been explained above, so I won’t go into too much explanation here;

Create a new OkayProperties that states what the user of the starter can configure.

@ConfigurationProperties(prefix = "okay.config")
public class OkayProperties {

    private String platform;

    private String channel;

    private Boolean enable;

    public String getPlatform(a) {
        return platform;
    }

    public void setPlatform(String platform) {
        this.platform = platform;
    }

    public String getChannel(a) {
        return channel;
    }

    public void setChannel(String channel) {
        this.channel = channel;
    }

    public Boolean getEnable(a) {
        return enable;
    }

    public void setEnable(Boolean enable) {
        this.enable = enable;
    }

    @Override
    public String toString(a) {
        return "OkayProperties{" +
                "platform='" + platform + '\' ' +
                ", channel='" + channel + '\' ' +
                ", enable=" + enable +
                '} '; }}Copy the code

Create a new meta-INF directory under the Resources directory and create a Spring.factories file

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  cn.haoxiaoyong.okay.starter.config.OkayStarterAutoConfiguration
Copy the code

Here is not and above we explain the source code is basically consistent!

Use our own Starter

Create a new SpringBoot project to introduce our own Maven dependencies:

	<dependency>
        <groupId>cn.haoxiaoyong.okay</groupId>
        <artifactId>okay-spring-boot-starter</artifactId>
        <version>Hundreds - SNAPSHO</version>
    </dependency>
Copy the code

And configured in the configuration file appliaction.yml

You see more intelligent will automatically prompt!

okay:
  config:
    platform: pdd
    channel: ws
    enable: true
Copy the code
@RestController
@Slf4j
public class OkController {

    @Autowired
    Okay okay;

    @RequestMapping("okay")
    public String testOkay(a) {
        log.info(okay.getChannel() + "" + okay.getPlatform() + "" + okay.getEnable());

        return okay.getChannel() + "" + okay.getPlatform() + ""+ okay.getEnable(); }}Copy the code

Okay: localhost:8082/okay

This example is just to show the logic, this one uses a custom Starter and makes a simple graph bed

Example Address: github.com/haoxiaoyong…