Custom Start Dependencies in Spring Boot

Keywords: Programming Java Spring Apache SpringBoot

1. Add automatic configuration dependencies to pom files:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

2. If a jar exists to configure it, the dependency needs to be added. Add httpclient here:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

3. Write default configuration classes:

package com.springboot.autoconfig;

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

//Configuration in application.properties will override the default values below
@ConfigurationProperties(prefix = "spring.httpclient")
public class HttpClientProperties {
    private Integer connectTimeOut = 1000;

    private Integer socketTimeOut = 10000;

    private String agent = "agent";

    private Integer maxConnPerRoute = 10;

    private Integer maxConnTotaol = 50;

    public Integer getConnectTimeOut() {
        return connectTimeOut;
    }

    public void setConnectTimeOut(Integer connectTimeOut) {
        this.connectTimeOut = connectTimeOut;
    }

    public Integer getSocketTimeOut() {
        return socketTimeOut;
    }

    public void setSocketTimeOut(Integer socketTimeOut) {
        this.socketTimeOut = socketTimeOut;
    }

    public String getAgent() {
        return agent;
    }

    public void setAgent(String agent) {
        this.agent = agent;
    }

    public Integer getMaxConnPerRoute() {
        return maxConnPerRoute;
    }

    public void setMaxConnPerRoute(Integer maxConnPerRoute) {
        this.maxConnPerRoute = maxConnPerRoute;
    }

    public Integer getMaxConnTotaol() {
        return maxConnTotaol;
    }

    public void setMaxConnTotaol(Integer maxConnTotaol) {
        this.maxConnTotaol = maxConnTotaol;
    }
}

4. Write configuration automatic configuration class. This class will read the default configuration class written before and create an instance of the default configuration without configuration:

package com.springboot.autoconfig;

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration//Note that this class is a Java configuration class
@ConditionalOnClass({HttpClient.class})//Automatic configuration occurs when HttpClient exists
@EnableConfigurationProperties(HttpClientProperties.class)//Read the default configuration
public class HttpClientAutoConfiguration {

    private final HttpClientProperties properties;

    //Configuration in application.properties will be used to create a good configuration
    public HttpClientAutoConfiguration(HttpClientProperties properties){
        this.properties = properties;
    }

    @Bean//Declare as bean to inject automatically
    @ConditionalOnMissingBean(HttpClient.class)//When there is no created HttpClient instance, this method is used to create it.
    public HttpClient httpClient(){
        //Building RequConfig
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(properties.getConnectTimeOut())//Set connection timeout, default 1 second
                .setSocketTimeout(properties.getSocketTimeOut()).build();//Set the read timeout time by default of 10 seconds
        HttpClient client = HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig) //Setting request Config
                .setUserAgent(properties.getAgent())//Setting up User-Agent
                .setMaxConnPerRoute(properties.getMaxConnPerRoute())//Set the maximum number of connections for a remote IP
                .setMaxConnTotal(properties.getMaxConnTotaol())//Set the total number of connections
                .build();
        return client;
    }

    @Bean
    @ConditionalOnBean(name="httpClient",value=HttpClient.class)//Existing HttpClient instance httpClient
    @ConditionalOnProperty(name = "spring.httpclient.testConfig", havingValue = "true")//Read Configuration Sets this Property to true
    public HttpClient httpClient1(HttpClient httpClient) throws IOException {
        Long contentLength=httpClient.execute(new HttpGet("https://www.taobao.com")).getEntity().getContentLength();
        if(contentLength>10){
            System.out.println("test success..");
        }else{
            System.out.println("test fail..");
        }
        return httpClient;
    }
}

@ Conditional OnMissing Bean: If the user does not customize a bean, the bean will be built

@ Conditional OnProperty: If the user does not open a property, it will not be built. Only open can build

@ Conditional OnClass: A class is built only if it exists.

 

5. Write test classes:

package com.springboot;

import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SelfStarterApplicationTests {

    /**
     * Check if the automatic configuration HttpClient AutoConfiguration we wrote is valid
     */
    @Autowired
    private HttpClient httpClient;

    @Autowired
    private HttpClient httpClient1;

    /**
     * httpclient bean Definition
     * There are three ways to make the automatic configuration of HttpClient AutoConfiguration work
     * 1.By making the package where the automatic configuration is located a subpackage annotated with the @SpringBootApplication startup class
     * 2.By defining the META-INF/spring.factories file, add the mapping relationship between Enable AutoConfiguration and automatic configuration
     * 3.By adding the annotation EnableHttpClient to the startup class, EnableHttpClient wants @Import(HttpClientAutoConfiguration.class)
     * @return
     */
    @Test
    public void testHttclient() throws IOException {
        //Visit Baidu and Export Baidu Page
        System.out.println(EntityUtils.toString(httpClient.execute(new HttpGet("http://www.baidu.com")).getEntity()));
    }

    @Test
    public void testHttclient1() throws IOException {
        //Visit Baidu and Export Baidu Page
        System.out.println(EntityUtils.toString(httpClient1.execute(new HttpGet("http://www.baidu.com")).getEntity()));
    }
}

 

6. At this point, since the current package is under the application entry class, the annotation @Configuration will be scanned automatically directly:

Running test class methods can successfully return results.

 

If the current package is not under the application entry class, that is, normally, when it is published to the outside as a third-party dependency. At this point, it will not be possible to scan the annotations automatically. There are two solutions:

Scenario 1. Create custom annotations:

package com.springboot.autoconfig;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.annotation.Import;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(HttpClientAutoConfiguration.class)
public @interface EnableHttpClient {
}

In the annotation, the HttpClientAutoConfiguration configuration is introduced using @Import(HttpClientAutoConfiguration.class). At this point, just add custom annotations to the application entry:

package com.springboot;

import com.springboot.autoconfig.EnableHttpClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableHttpClient
@SpringBootApplication
public class SelfStarterApplication {
    public static void main(String[] args) {
        SpringApplication.run(SelfStarterApplication.class, args);
    }
}

Scenario 2. Create spring.factories and define scanning classes:

    spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.springboot.autoconfig.HttpClientAutoConfiguration

The files are placed under META-INF in the resources directory:

    

 

Discrimination of the use of the two:

  1. When using scheme 2, after adding dependencies, it will automatically scan the configuration.
  2. For a while, after adding dependencies, you need to add custom annotations to the application entry to turn on the automatic scanning configuration.

Both schemes are used in practice.

 

7. Follow-up actions: Use mvn command to type the project into Jar package, add local warehouse, and then introduce its dependencies into other projects and complete relevant automatic configuration:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.springboot</groupId> <artifactId>self-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> 

 

Relevant procedures: https://gitee.com/LangWangHuangShiFu/self-starter

Posted by kwilder on Wed, 20 Mar 2019 08:30:40 -0700