Spring提供了三种方式将Bean注入到spring容器中
- @Import()
- ImportBeanDefinitionRegistrar
- ImportSelector
这也是一种将bean注册到spring容器中
- @Configuration
— @Bean
但是这种底层原来@Component注解来实现,这个有一个annotation继承的问题,每个版本都有相应的改变。有的版本实现了两,后面的版本做了增强的实现做了递归的实现,可以实现注解继承。
实现思路
通过读取配置文件中的属性到环境变量中,然后通过环境变量与spring容器提供的接口完成自动映射.
实现步骤
- 新建文件
usr.id = m
usr.name = lisi
usr.age = 10
- 定义一个注解EnableConfigurationBeanBinding
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigurationBeanBindingRegistrar.class)
public @interface EnableConfigurationBeanBinding {
/**
* The default value for {@link #multiple()}
*
* @since 1.0.6
*/
boolean DEFAULT_MULTIPLE = false;
/**
* The default value for {@link #ignoreUnknownFields()}
*
* @since 1.0.6
*/
boolean DEFAULT_IGNORE_UNKNOWN_FIELDS = true;
/**
* The default value for {@link #ignoreInvalidFields()}
*
* @since 1.0.6
*/
boolean DEFAULT_IGNORE_INVALID_FIELDS = true;
/**
* The name prefix of the properties that are valid to bind to the type of configuration.
*
* @return the name prefix of the properties to bind
*/
String prefix();
/**
* @return The binding type of configuration.
*/
Class<?> type();
/**
* It indicates whether {@link #prefix()} binding to multiple Spring Beans.
*
* @return the default value is <code>false</code>
* @see #DEFAULT_MULTIPLE
*/
boolean multiple() default DEFAULT_MULTIPLE;
/**
* Set whether to ignore unknown fields, that is, whether to ignore bind
* parameters that do not have corresponding fields in the target object.
* <p>Default is "true". Turn this off to enforce that all bind parameters
* must have a matching field in the target object.
*
* @return the default value is <code>true</code>
* @see #DEFAULT_IGNORE_UNKNOWN_FIELDS
*/
boolean ignoreUnknownFields() default DEFAULT_IGNORE_UNKNOWN_FIELDS;
/**
* Set whether to ignore invalid fields, that is, whether to ignore bind
* parameters that have corresponding fields in the target object which are
* not accessible (for example because of null values in the nested path).
* <p>Default is "true".
*
* @return the default value is <code>true</code>
* @see #DEFAULT_IGNORE_INVALID_FIELDS
*/
boolean ignoreInvalidFields() default DEFAULT_IGNORE_INVALID_FIELDS;
}
- 重点看这个类@Import(ConfigurationBeanBindingRegistrar.class)
public class ConfigurationBeanBindingRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
final static Class ENABLE_CONFIGURATION_BINDING_CLASS = EnableConfigurationBeanBinding.class;
private final static String ENABLE_CONFIGURATION_BINDING_CLASS_NAME = ENABLE_CONFIGURATION_BINDING_CLASS.getName();
private final Log log = LogFactory.getLog(getClass());
private ConfigurableEnvironment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(ENABLE_CONFIGURATION_BINDING_CLASS_NAME);
registerConfigurationBeanDefinitions(attributes, registry);
}
public void registerConfigurationBeanDefinitions(Map<String, Object> attributes, BeanDefinitionRegistry registry) {
String prefix = getRequiredAttribute(attributes, "prefix");
// 如果该变量使用了环境变量中的属性,通过该方法可以替换环境变量
prefix = environment.resolvePlaceholders(prefix);
// 获取需要替换的class对象
Class<?> configClass = getRequiredAttribute(attributes, "type");
boolean multiple = getAttribute(attributes, "multiple", valueOf(DEFAULT_MULTIPLE));
boolean ignoreUnknownFields = getAttribute(attributes, "ignoreUnknownFields", valueOf(DEFAULT_IGNORE_UNKNOWN_FIELDS));
boolean ignoreInvalidFields = getAttribute(attributes, "ignoreInvalidFields", valueOf(DEFAULT_IGNORE_INVALID_FIELDS));
// 注册bean
registerConfigurationBeans(prefix, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, registry);
}
private void registerConfigurationBeans(String prefix, Class<?> configClass, boolean multiple,
boolean ignoreUnknownFields, boolean ignoreInvalidFields,
BeanDefinitionRegistry registry) {
// 通过前置获取到配置文件中的属性值
Map<String, Object> configurationProperties = PropertySourcesUtils.getSubProperties(environment.getPropertySources(), environment, prefix);
if (CollectionUtils.isEmpty(configurationProperties)) {
if (log.isDebugEnabled()) {
log.debug("There is no property for binding to configuration class [" + configClass.getName()
+ "] within prefix [" + prefix + "]");
}
return;
}
// 获取到bean的名字
Set<String> beanNames = multiple ? resolveMultipleBeanNames(configurationProperties) :
singleton(resolveSingleBeanName(configurationProperties, configClass, registry));
for (String beanName : beanNames) {
registerConfigurationBean(beanName, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields,
configurationProperties, registry);
}
registerConfigurationBindingBeanPostProcessor(registry);
}
private void registerConfigurationBean(String beanName, Class<?> configClass, boolean multiple,
boolean ignoreUnknownFields, boolean ignoreInvalidFields,
Map<String, Object> configurationProperties,
BeanDefinitionRegistry registry) {
// 构建bean的定义
BeanDefinitionBuilder builder = rootBeanDefinition(configClass);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
setSource(beanDefinition);
Map<String, Object> subProperties = resolveSubProperties(multiple, beanName, configurationProperties);
// init 属性值
initBeanMetadataAttributes(beanDefinition, subProperties, ignoreUnknownFields, ignoreInvalidFields);
// 注册到IOC容器中
registry.registerBeanDefinition(beanName, beanDefinition);
if (log.isInfoEnabled()) {
log.info("The configuration bean definition [name : " + beanName + ", content : " + beanDefinition
+ "] has been registered.");
}
}
private Map<String, Object> resolveSubProperties(boolean multiple, String beanName,
Map<String, Object> configurationProperties) {
if (!multiple) {
return configurationProperties;
}
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addLast(new MapPropertySource("_", configurationProperties));
return getSubProperties(propertySources, environment, normalizePrefix(beanName));
}
private void setSource(AbstractBeanDefinition beanDefinition) {
beanDefinition.setSource(ENABLE_CONFIGURATION_BINDING_CLASS);
}
private void registerConfigurationBindingBeanPostProcessor(BeanDefinitionRegistry registry) {
registerInfrastructureBean(registry, ConfigurationBeanBindingPostProcessor.BEAN_NAME,
ConfigurationBeanBindingPostProcessor.class);
}
@Override
public void setEnvironment(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
this.environment = (ConfigurableEnvironment) environment;
}
private Set<String> resolveMultipleBeanNames(Map<String, Object> properties) {
Set<String> beanNames = new LinkedHashSet<String>();
for (String propertyName : properties.keySet()) {
int index = propertyName.indexOf(".");
if (index > 0) {
String beanName = propertyName.substring(0, index);
beanNames.add(beanName);
}
}
return beanNames;
}
private String resolveSingleBeanName(Map<String, Object> properties, Class<?> configClass,
BeanDefinitionRegistry registry) {
String beanName = (String) properties.get("id");
if (!StringUtils.hasText(beanName)) {
BeanDefinitionBuilder builder = rootBeanDefinition(configClass);
beanName = BeanDefinitionReaderUtils.generateBeanName(builder.getRawBeanDefinition(), registry);
}
return beanName;
}
}
分析:
- ImportBeanDefinitionRegistrar
// 注册bea的定义
void registerBeanDefinitions(AnnotationMetadata var1, BeanDefinitionRegistry var2); - EnvironmentAware
// 设置环境变量
void setEnvironment(Environment var1);
将配置导入到环境变量中@Import
如果有多个类同时想绑定到Spring ioc中
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigurationBeanBindingsRegister.class)
public @interface EnableConfigurationBeanBindings {
/**
* @return the array of {@link EnableConfigurationBeanBinding EnableConfigurationBeanBindings}
*/
EnableConfigurationBeanBinding[] value();
}
在项目我中,做公共组件开发,我们经常使用spring-context提供的一些提供,去扩展spring容器.