Usage of AutoConfiguration in SpringBoot

  • 2021-07-18 08:04:50
  • OfStack

In SpringBoot, we can often introduce some starter packages to integrate the use of some tools, such as spring-boot-starter-data-redis .

It is very convenient to use, so how is it realized?

Code analysis

Let's look at the notes first @SpringBootApplication Which contains 1 @EnableAutoConfiguration

Keep reading @ EnableAutoConfiguration annotations

@Import({AutoConfigurationImportSelector.class})

In this class (AutoConfigurationImportSelector), automatic configuration loading is implemented

Main code snippets:

In the String [] selectImports (AnnotationMetadata annotationMetadata) method


AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);

In the getAutoConfigurationEntry method:


List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); 

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}

Finally, META-INF/spring. factories is loaded via SpringFactoriesLoader. loadSpringFactories


Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
        LinkedMultiValueMap result = new LinkedMultiValueMap();

    while(urls.hasMoreElements()) {
          URL url = (URL)urls.nextElement();
          UrlResource resource = new UrlResource(url);
          Properties properties = PropertiesLoaderUtils.loadProperties(resource);
          Iterator var6 = properties.entrySet().iterator();

          while(var6.hasNext()) {
            Entry<?, ?> entry = (Entry)var6.next();
            String factoryClassName = ((String)entry.getKey()).trim();
            String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
            int var10 = var9.length;

            for(int var11 = 0; var11 < var10; ++var11) {
              String factoryName = var9[var11];
              result.add(factoryClassName, factoryName.trim());
            }
          }
        }

ZookeeperAutoConfiguration

Let's implement an AutoConfiguration for an ZK

First define an ZookeeperAutoConfiguration class

Then add in META-INF/spring. factories


org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.fayayo.fim.zookeeper.ZookeeperAutoConfiguration

Next, let's look at the concrete implementation:


@ConfigurationProperties(prefix = "fim.register")
@Configuration
public class URLRegistry {
  private String address;
  private int timeout;
  private int sessionTimeout;
  public String getAddress() {
    if (address == null) {
      address = URLParam.ADDRESS;
    }
    return address;
  }
  public void setAddress(String address) {
    this.address = address;
  }
  public int getTimeout() {
    if (timeout == 0) {
      timeout = URLParam.CONNECTTIMEOUT;
    }
    return timeout;
  }
  public void setTimeout(int timeout) {
    this.timeout = timeout;
  }
  public int getSessionTimeout() {
    if (sessionTimeout == 0) {
      sessionTimeout = URLParam.REGISTRYSESSIONTIMEOUT;
    }
    return sessionTimeout;
  }
  public void setSessionTimeout(int sessionTimeout) {
    this.sessionTimeout = sessionTimeout;
  }
}
@Configuration
@EnableConfigurationProperties(URLRegistry.class)
@Slf4j
public class ZookeeperAutoConfiguration {
  @Autowired
  private URLRegistry url;
  @Bean(value = "registry")
  public Registry createRegistry() {
    try {
      String address = url.getAddress();
      int timeout = url.getTimeout();
      int sessionTimeout = url.getSessionTimeout();
      log.info("init ZookeeperRegistry,address[{}],sessionTimeout[{}],timeout[{}]", address, timeout, sessionTimeout);
      ZkClient zkClient = new ZkClient(address, sessionTimeout, timeout);
      return new ZookeeperRegistry(zkClient);
    } catch (ZkException e) {
      log.error("[ZookeeperRegistry] fail to connect zookeeper, cause: " + e.getMessage());
      throw e;
    }
  }
}

ZookeeperRegistry partial implementation:


public ZookeeperRegistry(ZkClient zkClient) {
    this.zkClient = zkClient;

    log.info("zk register success!");

    String parentPath = URLParam.ZOOKEEPER_REGISTRY_NAMESPACE;
    try {
      if (!zkClient.exists(parentPath)) {
        log.info("init zookeeper registry namespace");
        zkClient.createPersistent(parentPath, true);
      }
      // Eavesdropping 
      zkClient.subscribeChildChanges(parentPath, new IZkChildListener() {
        // Add listener child node changes to parent node. 
        @Override
        public void handleChildChange(String parentPath, List<String> currentChilds) {
          log.info(String.format("[ZookeeperRegistry] service list change: path=%s, currentChilds=%s", parentPath, currentChilds.toString()));
          if(watchNotify!=null){
            watchNotify.notify(nodeChildsToUrls(currentChilds));
          }
        }
      });

      ShutDownHook.registerShutdownHook(this);

    } catch (Exception e) {
      e.printStackTrace();
      log.error("Failed to subscribe zookeeper");
    }
  }

Specific use

So how do we use our own ZookeeperAutoConfiguration

First, introduce dependencies in the projects that need to be used


   <dependency>
      <groupId>com.fayayo</groupId>
      <artifactId>fim-registry-zookeeper</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>

Then configure the parameters


 fim:
   register:
    address: 192.168.88.129:2181
    timeout: 2000

If not configured, there will be default parameters

When you use it, you only need to inject it into Bean, for example


@Autowired
  private Registry registry;
  public List<URL> getAll(){
    List<URL>list=cache.get(KEY);
    if(CollectionUtils.isEmpty(list)){
      list=registry.discover();
      cache.put(KEY,list);
    }
    return list;
  }

Complete code

https://github.com/lizu18xz/fim.git

Summarize


Related articles: