0%

springboot部分功能浅析

springboot部分功能浅析

1.绑定器

org.springframework.boot.context.properties.bind.Binderspringbootenvironment对象中获取配置信息的绑定器,可以很方便的就获取到对应的配置值信息。

1.创建配置类(必须要提供set方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Data
@ConfigurationProperties(prefix = "spring.dynamic")
public class DtpProperties {

private boolean enabledBanner;

private Apollo apollo;

@Data
public static class Apollo {

private String namespace;
}
}

2.配置文件中的配置项

1
2
spring.dynamic.enabledBanner=true
spring.dynamic.apollo.namespace=aaa

2.通过Binder获取配置信息

1
Binder.get(environment).bind("spring.dynamic", Bindable.ofInstance(dtpProperties));

2.@Import引入

通过@Import引入将某些配置类或者扩展点接口添加到容器中。

示例:

1.创建一个注解EnableMyConfig,该注解使用@Import标记,并添加MyConfigurationSelector.class,这个类实现了ImportSelector顶层接口。

1
2
3
4
5
6
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyConfigurationSelector.class)
public @interface EnableMyConfig {
}

2.实现DeferredImportSelector#selectImports接口,那么在Spring加载到这个引入类MyConfigurationSelector时就会将MyBeanDefinitionRegistrar类添加到Spring容器中,作用等同于为MyBeanDefinitionRegistrar添加@Component等自动注入注解。

1
2
3
4
5
6
7
8
9
10
11
12
public class MyConfigurationSelector implements DeferredImportSelector, Ordered {

@Override
public String[] selectImports(AnnotationMetadata metadata) {
return new String[]{MyBeanDefinitionRegistrar.class.getName()};
}

@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
}

3.这个引入类一般以注册类居多,如果是配置类一般通过@Component或者@Configuration等注解就可自动注入,我这里的示例是一个注册类。实现于ImportBeanDefinitionRegistrar接口,MyBeanDefinitionRegistrar这个spring生命周期中会执行到registerBeanDefinitions,即为MyProperties类创建BeanDefinition对象,并将这个对象注入到容器中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Slf4j
public class MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

private Environment environment;

@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// BeanDefinition对象构建器
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(DtpProperties.class);

// 注入属性键和属性值
builder.addPropertyValue("enabledBanner", true);

//这里手动注册bean对象
registry.registerBeanDefinition("myProperties", builder.getBeanDefinition());
}

}

注意:@EnableMyConfig注解需要标记到某个类上,且这个类在扫描范围之内才会生效,我曾经深深陷入于这个注解已经是在扫描范围之内,为什么需要标记到某个类上,扫描到这个注解后这个注解不是被@Import标记吗,难道不能直接获取到@Import中的值。实际上还是需要通过标记的这个类上拥有@Component等自动注入的注解。

整个过程大致为:由Spring容器MapperScan入口refresh方法 ⇒执行容器中实现BeanDefinitionRegistryPostProcessorbean对象⇒ConfigurationClassPostProcessor是对BeanDefinitionRegistryPostProcessor的实现,主要处理逻辑在这个类中。⇒之后会执行实现ImportBeanDefinitionRegistrar#registerBeanDefinitions,也就是这时会执行MyBeanDefinitionRegistrar这个自定义类,在这个自定义类中又注册了新的BeanDefinition⇒之后Spring容器会创建这个Bean对象。

综上:从Spring容器的角度来说,MyBeanDefinitionRegistrar是在ConfigurationClassPostProcessor执行时加载的,MyProperties是在MyBeanDefinitionRegistrar加载之后加载。

​ 目前在Spring的生态中一般都是通过@EnableXX注解标记@Import注解来引入某些配置的方式,进行动态的引入某些配置类。比如EnableScheduling,只有将这个注解标记在扫描配置类之上,才会通过@Import引入配置SchedulingConfiguration类。

1
2
3
4
5
6
7
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {

}

@Import也可以配合ImportSelector实现类,进行动态创建类。比如MapperScan。在MapperScannerRegistrar中动态的创建 MapperScannerConfigurer对象并注册到容器中。

1
2
3
4
5
6
7
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
}

3.配置类加载

EnableConfigurationProperties注解配合ConfigurationProperties注解进行使用。

假设说创建了一个配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Data
@ConfigurationProperties(prefix = "spring.dynamic")
public class MyProperties {

private boolean enabledBanner;

private Apollo apollo;

@Data
public static class Apollo {

private String namespace;
}
}

如果这个类没有标记@Configuration注解,那么这个容器中不会有这个类的Bean对象。

所以将此配置类添加到容器中有两种方式,一种是直接添加注入容器注解@Configuration。第二种是通过@EnableConfigurationProperties注解指向这个配置类。

1
2
3
4
5
6
7
8
9
10
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(MyProperties.class)
public class BaseBeanConfiguration {

@Bean
public MyRegistry myRegistry() {
return new MyRegistry();
}

}

当加载DtpBaseBeanConfiguration这个类时,会读取到EnableConfigurationProperties注解,这里又是一个@Import的应用,引入了EnableConfigurationPropertiesRegistrar类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerInfrastructureBeans(registry);
registerMethodValidationExcludeFilter(registry);
ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
// 获取到指向的配置类(这里是MyProperties),这里会将MyProperties转换为
// BeanDefinition对象注册到容器中, 等到根据BeanDefinition创建具体的bean
// 对象的时候,会读取到ConfigurationProperties注解进行配置属性值的绑定
getTypes(metadata).forEach(beanRegistrar::register);
}

}

注意:这个EnableConfigurationProperties注解的作用仅仅是将某个配置类转换为BeanDefinition注册到Spring容器中,结合@ConditionXXX可以减少无用bean对象的创建。

-------------本文结束感谢您的阅读-------------