Manually build the starter component

As mentioned in previous posts, try to use starter related dependencies. Because these related dependencies will configure related classes according to the configuration file. For example, redis is integrated in Spring. In rapid development, Spring boot starter data redis dependency is recommended. It will configure redisTemplate and other related classes by default according to the configuration file.
Then, let's introduce how to build the starter component.
First of all, for the customized starter (third party), the official recommended name is XX Spring boot starter, and only the official name of Spring starter is Spring boot starter XXX.

First, let's look at the dependency part. pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.apex</groupId>
    <artifactId>myservice-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
		
        <!-- custom springboot This dependency must be introduced as an entry -->
        <!--Here are the notes we need,Spring Will be based on EnableAutoConfigurationImportSelector Inject related classes-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.3.0.RELEASE</version>
        </dependency>

        <!-- Automatic prompt function when reading configuration file -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <version>2.3.0.RELEASE</version>
            <optional>true</optional>
        </dependency>
   
    </dependencies>
	<!--No, because when I packed idea Suggest I use UTF-8 code-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

Write a YmlProperties class here to read the yml configuration file.

package com.apex.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @author ZJX
 * @date 2022/2/20
 * @description
 */
 //Get the attribute prefixed with myservice in the configuration file (application.properties or application.yml)
@ConfigurationProperties(prefix = "myservice")
public class YmlProperties {
    private String url;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

Next, write the Service layer.

package com.apex.service;

import com.apex.config.YmlProperties;
import org.springframework.stereotype.Service;

/**
 * @author ZJX
 * @date 2022/2/20
 * @description
 */
public class YmlService {
    private YmlProperties ymlProperties;
    public YmlService(YmlProperties ymlProperties){
        this.ymlProperties = ymlProperties;
    }
    public String getUrl(){
        if(ymlProperties!=null){
            return ymlProperties.getUrl();
        }else{
            return null;
        }
    }
    public YmlProperties getYmlProperties() {
        return ymlProperties;
    }

    public void setYmlProperties(YmlProperties ymlProperties) {
        this.ymlProperties = ymlProperties;
    }
}

Next, write a configuration class for automatic configuration.

package com.apex;

import com.apex.config.YmlProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.apex.service.YmlService;

/**
 * @author ZJX
 * @date 2022/2/20
 * @description
 */
 //This class is a configuration class
@Configuration
//Describes the class that automatically reads configuration files
@EnableConfigurationProperties(YmlProperties.class)
//The annotation configuration file needs to be configured with specific attributes to take effect. Here, myservice needs to be configured It takes effect only when enabled = true. matchIfMissing indicates that if it is not configured, it is true by default, that is, it takes effect by default
@ConditionalOnProperty(prefix = "myservice",name = "enabled",matchIfMissing = true)
public class MyAutoConfiguration {
    @Autowired
    YmlProperties ymlProperties;
    @Bean
    public YmlService ymlService (){
        return new YmlService(ymlProperties);
    }

}

The next step is to configure spring Factories file (under META-INF under resources, no files or directories need to be created by yourself).

#Configure automatically loaded classes
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.apex.MyAutoConfiguration

The above configuration is because @ SpringBootApplication contains @ EnableAutoConfiguration.

In @ EnableAutoConfiguration, AutoConfigurationImportSelector is imported through @ Import.

Find the selectImports method, and you can see that it calls the getAutoConfigurationEntry method.

In the getAutoConfigurationEntry method, the configuration information is obtained through getCandidateConfigurations.

You can see that this method continues to call the loadFactoryNames method. The following assertion information also explains that it actually obtains the META-INF/factories file. But we can continue to look for specific files from the method.

In the loadFactoryNames method, if the current class loading object exists, load it in the specified path under the current class loading package. If it does not exist, find it in the current system class loading path. And for factories_ RESOURCE_ For location, it points to meta-inf / spring and factories files (that is, META-INF/spring.factories under each jar package will be scanned).



Finally, the component is introduced into other modules.

       <dependencies>
           <dependency>
               <groupId>com.apex</groupId>
               <artifactId>myservice-spring-boot-starter</artifactId>
               <version>0.0.1-SNAPSHOT</version>
           </dependency>
       </dependencies>

Controller layer

@Controller
public class FirstController {
	//Automatic injection
    @Autowired
    YmlService ymlService;
    @GetMapping("/hello")
     @ResponseBody
    public String sayHello(){
     return ymlService.getUrl();
 }

}

Application of this module YML file.

myservice:
  url: 123

test

Although the project runs successfully, there is a problem, that is, the idea will report that the bean cannot be found. That's because YmlService is configured under other modules. Idea can find this class, but it doesn't know whether this class is controlled and reversed by the Spring container, but it is.

In order to eliminate the popularity, we can use spring. Com under the customized starter module Add the Service class under factories.

This can clearly tell "idea" that the YmlService class will be automatically scanned by Spring and injected into the Spring container. Then the explosion disappeared.

Finally, let me show you my project structure.

Keywords: Java Spring Spring Boot

Added by undertow on Sun, 20 Feb 2022 18:18:20 +0200