springboot自动配置

目录

 

写在前面

关于@import注解

验证猜想1

验证猜想2

实现一个自动配置

项目的pom文件


写在前面

spring的自动配置,帮我们预置了一些常用对象,如servlet,Filter等。

通过自动配置,将这些对象加入到IOC容器中。实现自自动配置的核心是:

  1. 核心注解@import

  2. 核心接口 DeferredImportSelector(继承了ImportSelector接口)

  3. 核心类AutoConfigurationImportSelector(实现了DeferredImportSelector接口)

  4. 核心文件META-INF/spring.factories

  5. 核心代码

    // 读取所有calsspath下的META-INF/spring.factories文件
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                    getBeanClassLoader());
            return configurations;
        }

    声明一点:实现了DeferredImportSelector接口的类,一般都会执行ImportSelector#selectImports方法,但是AutoConfigurationImportSelector却没有执行selectImports方法。

关于@import注解

import可以把bean加入到IOC容器中。写个demo看下现象。

  • import导入普通类

@SpringBootApplication
@Import(value = {Dept.class})
public class Main {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Main.class);
//        System.out.println(context.getBean(User.class));
        System.out.println(Dept.class);
    }
}
public class Dept {
}

输出结果

class com.demo.model.Dept

 

  • import导入实现了ImportSelector接口的类

@SpringBootApplication
@Import(value = {Dept.class, MyDeferredImportSelector.class})
public class Main {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Main.class);
        System.out.println(Dog.class);
    }
}
package com.demo.importSelecotr;
​
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.type.AnnotationMetadata;
​
public class MyDeferredImportSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.demo.model.Dog"};
    }
}
package com.demo.model;
​
public class Dog {
}

输出结果

class com.demo.model.Dog

 

  • import导入实现了ImportSelector接口的类2 ( ImportSelector的selectImports方法导入的是配置类)

​
// 排除需要扫描的类UserAutoConfig。 使用import导入
@SpringBootApplication(exclude={com.demo.autoConfig.UserAutoConfig.class})
@Import(value = {Dept.class, MyDeferredImportSelector.class, MyDeferredImportSelector2.class})
public class Main {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Main.class);
        System.out.println(context.getBean("defaultUser"));
    }
}
public class MyDeferredImportSelector2 implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // UserAutoConfig 是个配置类,该类有@Configuration注解。(springboot自动配置类均有该注解)
        return new String[]{"com.demo.autoConfig.UserAutoConfig"};
    }
}
@Configuration
// DefaultUserConfig 属性类,和application.yml中配置对应
@EnableConfigurationProperties(DefaultUserProperties.class)
@ConditionalOnClass(User.class)
public class UserAutoConfig {
    @Bean(name = "defaultUser")
    @ConditionalOnMissingBean(User.class)
    public User initDefaultUser(DefaultUserProperties config) {
        User user = new User();
        user.setName(config.getConfigName());
        user.setPassword(config.getConfigPassword());
        return user;
    }
}
​
@Data
@NoArgsConstructor
@ConfigurationProperties(prefix="user")
public class DefaultUserProperties {
    private String configName;
    private String configPassword;
}

application.yml

user:
  configName: admin
  configPassword: 111111

输出结果

User(name=admin, password=111111)

 

小结

我们通过import注解导入了实现DeferredImportSelector接口的类。依赖于ImportSelector#selectImports方法可以将bean加入到IOC容器中。

猜想1:如果在ImportSelector接口的selectImports方法中,读取我们的配置文件META-INF/spring.factories是否就可以实现自动配置?

 

验证猜想1

参考spirngboot定义的spring.factories文件,定义自己的spring.factories。

参考AutoConfigurationImportSelector的getCandidateConfigurations方法,使用SpringFactoriesLoader.loadFactoryNames方法读取META-INF\spring.factories。

// 排除需要扫描的类UserAutoConfig。 使用import导入
@SpringBootApplication(exclude={com.demo.autoConfig.UserAutoConfig.class})
@Import(value = {MyDeferredImportSelector3.class})
public class Main {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Main.class);
        System.out.println(context.getBean("defaultUser"));
    }
}
public class MyDeferredImportSelector3 implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        List<String> configurations =                  
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,ClassLoader.getSystemClassLoader());
        boolean contains = configurations.contains("com.demo.autoConfig.UserAutoConfig");
        if (contains) {
            System.out.println("MyDeferredImportSelector3 --->com.demo.autoConfig.UserAutoConfig");
            return new String[]{"com.demo.autoConfig.UserAutoConfig"};
        }
        return new String[0];
    }
}

resources\META-INF\spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.demo.autoConfig.UserAutoConfig

输入结果

MyDeferredImportSelector3 --->com.demo.autoConfig.UserAutoConfig
......
​
User(name=admin, password=111111)

小结:通过读取文件中配置信息,也可以将bean加入到IOC容器中。

猜想2:springboot实现自动配置是调用了DeferredImportSelector接口的selectImports方法吗?(不是)

 

验证猜想2

打断点调试发现没有调用AutoConfigurationImportSelector#selectImports方法,很是诧异

class: org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

源码调试

通过打断点调试 发现getCandidateConfigurations调用栈为

org.springframework.boot.autoconfigure.
            AutoConfigurationImportSelector#getCandidateConfigurations
    org.springframework.boot.autoconfigure.
                 AutoConfigurationImportSelector#getAutoConfigurationEntry
        org.springframework.boot.autoconfigure.
                    AutoConfigurationImportSelector.AutoConfigurationGroup#process
            org.springframework.context.annotation.ConfigurationClassParser.
                         DeferredImportSelectorGrouping#getImports
                org.springframework.context.annotation.ConfigurationClassParser.
                             DeferredImportSelectorGroupingHandler#processGroupImports
class :org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler
​
public void processGroupImports() {
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        Predicate<String> exclusionFilter = grouping.getCandidateFilter();
        grouping.getImports().forEach(entry -> {
        ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
        try {
            // 该 entry包含了spring.factories中的配置类信息
            // 也包含从一般的DeferredImportSelector.selectImports返回的calss信息
            // 将entry交给processImports处理,进一步注册到BeanDefinitionMap中,为bean创建做准备。
            processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                exclusionFilter, false);
                    }
                    catch (BeanDefinitionStoreException ex) {}
                    catch (Throwable ex) {}
                });
            }
        }
​

 

class: org.springframework.context.annotation.**ConfigurationClassParser.DeferredImportSelectorGrouping#getImports
​
/**
 * Return the imports defined by the group.
 */
public Iterable<Group.Entry> getImports() {
   for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
      //  group做自己的处理
      this.group.process(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getImportSelector());
   }
   // 处理完后,返回统一数据类型 
   return this.group.selectImports();
}

this.group.process(),是通过group对象调用process方法的。group接口实现类:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process

org.springframework.context.annotation.ConfigurationClassParser.DefaultDeferredImportSelectorGroup#process

AutoConfigurationImportSelector 所数组为AutoConfigurationGroup

一般的DeferredImportSelector类所属组为DefaultDeferredImportSelectorGroup

 

AutoConfigurationImportSelector 走的是AutoConfigurationGroup#process,该方法中并没有调用selectImports方法。

class: org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process
filed: Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
​
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    // 获取spring.factories中配置key=org.springframework.boot.autoconfigure.EnableAutoConfiguration=\的值
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
         .getAutoConfigurationEntry(annotationMetadata);
   this.autoConfigurationEntries.add(autoConfigurationEntry);
    // 将读取的配置信息加入到entries中
   for (String importClassName : autoConfigurationEntry.getConfigurations()) {
      this.entries.putIfAbsent(importClassName, annotationMetadata);
   }
}
​
​
@Override
public Iterable<Entry> selectImports() {
    .......
        return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
        .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
        .collect(Collectors.toList());
}

 

一般的DeferredImportSelector类走的是DefaultDeferredImportSelectorGroup#process,该方法中并有调用了selectImports方法。

class: org.springframework.context.annotation.ConfigurationClassParser.DefaultDeferredImportSelectorGroup
filed : private final List<Entry> imports = new ArrayList<>();
​
@Override
public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
    // 调用了selectImports方法
   for (String importClassName : selector.selectImports(metadata)) {
      this.imports.add(new Entry(metadata, importClassName));
   }
}
​
​
@Override
public Iterable<Entry> selectImports() {
    return this.imports;
}

小结:

  1. 实现了DeferredImportSelector接口的类,并不一定会执行ImportSelector#selectImports方法。

  2. 如果DeferredImportSelector属于默认组则一定执行;否则需要看group的具体实现。

     

实现一个自动配置

使用springBoot自动配置的机制,实现一个自动配置。最后可以打成jar,如果需要可以直接依赖。

创建步骤:

  1. calsspath下创建 META-INF\spring.factories文件

  2. 写配置类xxxxAutoConfiguration @Configuration

  3. 写属性类xxxxxProperties @EnableConfigurationProperties

 

项目的pom文件

<?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>
​
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
​
</project>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值