SpringBoot source code analysis

Posted by geetakhurana on Thu, 27 Jan 2022 15:24:31 +0100

SpringBoot source code analysis (2) automatic assembly demo

preface

The Spring Framework focuses on how to make the management of beans easier and how to let developers manage some configurations generated by beans as little as possible; When we reference beans in other modules, we can automatically create beans and hand them over to spring's Ioc container for management.
spring3.x generates the Enable module driven annotation. When calling other module beans, you can generate beans through the Enable annotation or import annotation. But in spring 4 In X, the emergence of conditional conditional annotation really realizes automatic assembly.
Before reading the source code of automatic assembly, let's demonstrate a case of automatic assembly. This article explains how to define a conditional Redis automatic assembly template.

1, Create Maven service of RedissonTemplate

Maven project version:

<groupId>com.my</groupId>
<artifactId>RedissonTemplate</artifactId>
<version>1.0-SNAPSHOT</version>

Create redissonautoconfiguration with @ Configuration annotation class:

package com.my.redission;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@EnableConfigurationProperties(RedissonProperties.class)
@Configuration
//@ConditionalOnClass(RedissonProperties.class)
public class RedissonAutoConfiguration {

    /**
     * RedissonClient:Operate the core objects of Redis
     *     Inject the object into the container
     *     Need to connect to Redis service, host port
     * @return
     */
    @Bean
    public RedissonClient redissonClient(RedissonProperties redissonProperties){
        Config config = new Config();
        String prefix = "redis://";
        if(redissonProperties.getSsl()){
            prefix = "rediss://";
        }
        config.useSingleServer()
                .setAddress(prefix+redissonProperties.getHost()+":"+redissonProperties.getPort())
                .setTimeout(redissonProperties.getTimeout());
        return Redisson.create(config);
    }

}

We can see that the modified class carries the annotation EnableConfigurationProperties(RedissonProperties.class), which is automatically generated from application yml/application. Get the relevant configuration parameters about Redis from the properties file.
RedissonProperties.clas

package com.my.redission;

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

@Data
@ConfigurationProperties(prefix = "my.redisson")
public class RedissonProperties {

    private String host = "localhost";
    private Integer port = 6379;
    private Integer timeout = 1000;
    private Boolean ssl = false;

}

Lombok.com is used here Data annotation, we don't need to get and set the member variables of RedissonProperties. But don't forget to add Lombok Data dependency.

<dependency>
  <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <version>1.18.12</version>
 </dependency>

At this moment, we have completed the programming. Next, we will configure the automatic assembly of our service.
We need to create a META-INF folder under resource, and add three files under META-INF folder: spring factories,additional-spring-configuration-metadata.json and spring autoconfigure metadata properties.

First, let's talk about additional spring configuration metadata JSON, which is used to configure how to start from application yml/application. Get the information of member variables in RedissonProperties in the properties file, that is, Redis configuration parameters.
additional-spring-configuration-metadata.json

{
  "properties": [{
    "name": "my.redisson.host",
    "type": "java.lang.String",
    "description": "Redis Service address of",
    "defaultValue": "localhost"
  },{
    "name": "my.redisson.port",
    "type": "java.lang.Integer",
    "description": "Redis Service port for",
    "defaultValue": 6379
  },{
    "name": "my.redisson.timeout",
    "type": "java.lang.Integer",
    "description": "Redis Service port for",
    "defaultValue": 1000
  },{
    "name": "my.redisson.ssl",
    "type": "java.lang.Boolean",
    "description": "Redis Service port for",
    "defaultValue": false
  }]
}

spring. The full path of redisonautoconfiguration class is configured in the factories file, which allows Spring Boot to load redisonautoconfiguration class through automatic assembly. The principle of loading is from classpath / meta-inf / spring. Net via SpringFactoriesLoader In the factories file, load the corresponding classes into the spring IoC container according to the key. The data format in this file: key is the full path of the customized configuration class EnableAutoConfiguration, and value is the full path of the configuration class; EnableAutoConfiguration is the Enable of automatic assembly.
spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.my.redission.RedissonAutoConfiguration

Conditional filtering, or conditional, can be used in the automatic assembly of Spring Boot.
The AutoConfigurationImportSelector method will first scan spring autoconfiguration metadata Properties file to obtain the class to be assembled; Finally, scan spring The class corresponding to factories will be filtered in combination with the previous metadata. Why does automatic assembly need filtering conditions? In most cases, beans annotated with @ configuration need to rely on classes of other frameworks. When the dependent class does not exist, the bean cannot be injected into Ioc. At the same time, it can also reduce the number of invalid @ configuration classes, so as to reduce the startup time of SpringBoot. Spring Boot also adds the full classpath of @ configuration of its own two-party dependency framework and friend's dependency framework to the classpath / meta-inf / spring under Spring Boot and Spring Boot autoconfigure frameworks In the factories file, when the conditions are met, spring. Com will be loaded automatically Beans in the factories file.
Like the demo in this article, RedissonTemplate needs to rely on org. Org when creating RedissonAutoConfiguration class redisson. api. Redissonclient class. Then we can add this condition only if there is org. Org in the classpath of the project redisson. api. RedissonAutoConfiguration can only be loaded into Ioc by automatic assembly of Spring Boot when redissonclient class.
spring-autoconfigure-metadata.properties

com.my.redission.RedissonAutoConfiguration=
com.my.redission.RedissonAutoConfiguration.ConditionalOnClass=org.redisson.api.RedissonClient

ConditionalOnClass means that the following classes need to exist, and there are many Conditional conditional annotations. Like: ConditionalOnBean, ConditionalOnResource, etc.

If we don't use spring autoconfigure metadata Properties file configuration, you can also add @ ConditionalOnClass(RedissonProperties.class) annotation on RedissonAutoConfiguration class.

So far, our RedissonTemplate configuration is completed.

2, Create test service

First, we create a spring boot project to introduce the dependency of RedissonTemplate.

<dependency>
    <groupId>com.my</groupId>
    <artifactId>RedissonTemplate</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

Then in application yml/application. Configure Redis startup parameters in the properties file:

my:
  redisson:
    host: 127.0.0.1
    port: 6379
    timeout: 2000

To write test cases, we provide a get interface through the Controller and access the data in Redis through the get interface in the browser. If we can connect to Redis and access Redis data normally, and represent, RedissonTemplate is automatically assembled into Ioc.
First, we exclude the automatic scanning of @ ComponentScan annotations. As can be seen from the figure, we have not configured the @ ComponentScan scanning path, so this path is the current class path and its subclasses.

Full path of our startup class: com example. demo1. Demo1Application
The full path of our RedissonTemplate: com my. redission. RedissonAutoConfiguration

Write our Controller layer code:

package com.example.demo1.action;

import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class RedissionAction {
    @Autowired
    private RedissonClient redissonClient;

    @GetMapping("/query")
    public String query(){
        return "key Number of="+redissonClient.getKeys().count()+"";
    }
}

As can be seen from 1, the conditions for automatically assembling RedissonTemplate are

Let's see if org. Org exists in our libraries Redismission framework and whether RedisClient exists in the framework.


It can be seen that our conditions meet the automatic assembly RedissonTemplate. Next, we run the Spring Boot test project. You can see the log information of Redis connection printed in the startup log.

At the same time, let's visit our controller interface to see the number of key s returned in redis.

Next, let's test the scenarios that do not meet the conditions.
We will spring - autoconfigure - metadata The condition of the properties file is changed to: rg redisson. api. Redissonclienttets class. Of course, this class does not exist.

com.my.redission.RedissonAutoConfiguration=
com.my.redission.RedissonAutoConfiguration.ConditionalOnClass=org.redisson.api.RedissonClientTets

At this point, when we run the test project again, we will see the error message of Spring Boot startup, showing field redissonclient in com example. demo1. action. RedissionAction required a bean of type ‘org.redisson.api.RedissonClient’ that could not be found.
This means that the Bean RedissonClient cannot be found.

3, Summary

So far, we have completed the project of how to configure an automatic assembly. In the next chapter, we will interpret the logic of automatic assembly from the perspective of source code.

Topics: Java Spring Boot