Skip to content

Commit a378db1

Browse files
committed
add multiple beans registrar
1 parent 45cf133 commit a378db1

File tree

7 files changed

+319
-3
lines changed

7 files changed

+319
-3
lines changed

04fx/demo/pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@
2727
<artifactId>spring-boot-starter-test</artifactId>
2828
<scope>test</scope>
2929
</dependency>
30-
</dependencies>
30+
31+
<dependency>
32+
<groupId>org.projectlombok</groupId>
33+
<artifactId>lombok</artifactId>
34+
<version>1.18.22</version>
35+
<scope>compile</scope>
36+
</dependency>
37+
</dependencies>
3138

3239
<build>
3340
<plugins>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.kimmking.javacourse.demo;
2+
3+
import lombok.Data;
4+
5+
/**
6+
* Description for this class.
7+
*
8+
* @Author : kimmking(kimmking@apache.org)
9+
* @create 2022/11/29 16:05
10+
*/
11+
@Data
12+
public class DemoConfig {
13+
14+
private String demoName;
15+
private String demoDesc;
16+
17+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package io.kimmking.javacourse.demo;
2+
3+
import org.springframework.beans.MutablePropertyValues;
4+
import org.springframework.beans.factory.support.*;
5+
import org.springframework.context.EnvironmentAware;
6+
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
7+
import org.springframework.core.annotation.AnnotationAttributes;
8+
import org.springframework.core.env.ConfigurableEnvironment;
9+
import org.springframework.core.env.Environment;
10+
import org.springframework.core.type.AnnotationMetadata;
11+
import org.springframework.util.CollectionUtils;
12+
import org.springframework.util.StringUtils;
13+
14+
import java.util.*;
15+
16+
import static io.kimmking.javacourse.demo.PropertySourcesUtils.getSubProperties;
17+
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
18+
19+
/**
20+
* This class is cloned and modified from https://github.com/apache/dubbo/blob/58d1259cb9d89528594e43db5d8667179005dcfc/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/DubboConfigBindingsRegistrar.java.
21+
*
22+
* @Author : kimmking(kimmking@apache.org)
23+
* @create 2022/11/29 16:09
24+
*/
25+
public class DemoConfigBindingsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
26+
27+
private ConfigurableEnvironment environment;
28+
29+
@Override
30+
public void setEnvironment(Environment _environment) {
31+
this.environment = (ConfigurableEnvironment) _environment;
32+
}
33+
34+
@Override
35+
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
36+
registerBeanDefinitions(importingClassMetadata, registry);
37+
}
38+
39+
@Override
40+
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
41+
42+
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
43+
importingClassMetadata.getAnnotationAttributes(EnableDemoConfigBindings.class.getName()));
44+
String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));
45+
Class<? extends DemoConfig> configClass = attributes.getClass("type");
46+
boolean multiple = attributes.getBoolean("multiple");
47+
registerDubboConfigBeans(prefix, configClass, multiple, registry);
48+
}
49+
50+
private void registerDubboConfigBeans(String prefix,
51+
Class<? extends DemoConfig> configClass,
52+
boolean multiple,
53+
BeanDefinitionRegistry registry) {
54+
Map<String, Object> properties = getSubProperties(environment.getPropertySources(), prefix);
55+
if (CollectionUtils.isEmpty(properties)) {
56+
System.out.println("There is no property for binding to demo config class [" + configClass.getName()
57+
+ "] within prefix [" + prefix + "]");
58+
return;
59+
}
60+
Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) :
61+
Collections.singleton(resolveSingleBeanName(properties, configClass, registry));
62+
Map<String, Map<String, Object>> groupProperties = getGroupProperties(properties, beanNames);
63+
for (String beanName : beanNames) {
64+
registerDemoConfigBean(beanName, configClass, registry, groupProperties.get(beanName));
65+
//registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry);
66+
}
67+
//registerDubboConfigBeanCustomizers(registry);
68+
}
69+
70+
private Map<String, Map<String, Object>> getGroupProperties(Map<String, Object> properties, Set<String> beanNames) {
71+
Map<String, Map<String, Object>> map = new HashMap<>();
72+
for (String propertyName : properties.keySet()) {
73+
int index = propertyName.indexOf(".");
74+
if (index > 0) {
75+
String beanName = propertyName.substring(0, index);
76+
String beanPropertyName = propertyName.substring(index + 1);
77+
if (beanNames.contains(beanName)) {
78+
Map<String, Object> group = map.get(beanName);
79+
if (group == null) {
80+
group = new HashMap<>();
81+
map.put(beanName, group);
82+
}
83+
group.put(beanPropertyName, properties.get(propertyName));
84+
}
85+
}
86+
}
87+
return map;
88+
}
89+
90+
private void registerDemoConfigBean(String beanName, Class<? extends DemoConfig> configClass,
91+
BeanDefinitionRegistry registry, Map<String, Object> properties) {
92+
BeanDefinitionBuilder builder = rootBeanDefinition(configClass);
93+
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
94+
// Convert Map to MutablePropertyValues
95+
MutablePropertyValues propertyValues = new MutablePropertyValues(properties);
96+
beanDefinition.setPropertyValues(propertyValues);
97+
registry.registerBeanDefinition(beanName, beanDefinition);
98+
System.out.println("The demo config bean definition [name : " + beanName + ", class : " + configClass.getName() +
99+
"] has been registered.");
100+
}
101+
102+
private Set<String> resolveMultipleBeanNames(Map<String, Object> properties) {
103+
Set<String> beanNames = new LinkedHashSet<String>();
104+
for (String propertyName : properties.keySet()) {
105+
int index = propertyName.indexOf(".");
106+
if (index > 0) {
107+
String beanName = propertyName.substring(0, index);
108+
beanNames.add(beanName);
109+
}
110+
}
111+
return beanNames;
112+
}
113+
114+
private String resolveSingleBeanName(Map<String, Object> properties, Class<? extends DemoConfig> configClass,
115+
BeanDefinitionRegistry registry) {
116+
String beanName = (String) properties.get("demoName");
117+
if (!StringUtils.hasText(beanName)) {
118+
BeanDefinitionBuilder builder = rootBeanDefinition(configClass);
119+
beanName = BeanDefinitionReaderUtils.generateBeanName(builder.getRawBeanDefinition(), registry);
120+
}
121+
return beanName;
122+
}
123+
124+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.kimmking.javacourse.demo;
2+
3+
import org.springframework.context.annotation.Import;
4+
5+
import java.lang.annotation.*;
6+
7+
/**
8+
* Description for this class.
9+
*
10+
* @Author : kimmking(kimmking@apache.org)
11+
* @create 2022/11/29 16:10
12+
*/
13+
@Target({ElementType.TYPE})
14+
@Retention(RetentionPolicy.RUNTIME)
15+
@Documented
16+
@Import(DemoConfigBindingsRegistrar.class)
17+
public @interface EnableDemoConfigBindings {
18+
/**
19+
* The name prefix of the properties that are valid to bind.
20+
*
21+
* @return the name prefix of the properties to bind
22+
*/
23+
String prefix();
24+
25+
/**
26+
* @return The binding type.
27+
*/
28+
Class<? extends DemoConfig> type();
29+
30+
/**
31+
* It indicates whether {@link #prefix()} binding to multiple Spring Beans.
32+
*
33+
* @return the default value is <code>true</code>
34+
*/
35+
boolean multiple() default true;
36+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package io.kimmking.javacourse.demo;
2+
3+
4+
import org.springframework.core.env.*;
5+
6+
import java.util.Collections;
7+
import java.util.LinkedHashMap;
8+
import java.util.Map;
9+
import java.util.Properties;
10+
11+
/**
12+
* {@link PropertySources} Utilities
13+
* <p>
14+
* The source code is cloned from https://github.com/alibaba/spring-context-support/blob/1.0.2/src/main/java/com/alibaba/spring/util/PropertySourcesUtils.java
15+
*
16+
* @since 2.6.6
17+
*/
18+
public abstract class PropertySourcesUtils {
19+
20+
/**
21+
* Get Sub {@link Properties}
22+
*
23+
* @param propertySources {@link PropertySource} Iterable
24+
* @param prefix the prefix of property name
25+
* @return Map
26+
* @see Properties
27+
*/
28+
public static Map<String, Object> getSubProperties(Iterable<PropertySource<?>> propertySources, String prefix) {
29+
30+
// Non-Extension AbstractEnvironment
31+
AbstractEnvironment environment = new AbstractEnvironment() {
32+
};
33+
34+
MutablePropertySources mutablePropertySources = environment.getPropertySources();
35+
36+
for (PropertySource<?> source : propertySources) {
37+
mutablePropertySources.addLast(source);
38+
}
39+
40+
return getSubProperties(environment, prefix);
41+
42+
}
43+
44+
/**
45+
* Get Sub {@link Properties}
46+
*
47+
* @param environment {@link ConfigurableEnvironment}
48+
* @param prefix the prefix of property name
49+
* @return Map
50+
* @see Properties
51+
*/
52+
public static Map<String, Object> getSubProperties(ConfigurableEnvironment environment, String prefix) {
53+
54+
Map<String, Object> subProperties = new LinkedHashMap<String, Object>();
55+
56+
MutablePropertySources propertySources = environment.getPropertySources();
57+
58+
String normalizedPrefix = normalizePrefix(prefix);
59+
60+
for (PropertySource<?> source : propertySources) {
61+
if (source instanceof EnumerablePropertySource) {
62+
for (String name : ((EnumerablePropertySource<?>) source).getPropertyNames()) {
63+
if (!subProperties.containsKey(name) && name.startsWith(normalizedPrefix)) {
64+
String subName = name.substring(normalizedPrefix.length());
65+
if (!subProperties.containsKey(subName)) { // take first one
66+
Object value = source.getProperty(name);
67+
if (value instanceof String) {
68+
// Resolve placeholder
69+
value = environment.resolvePlaceholders((String) value);
70+
}
71+
subProperties.put(subName, value);
72+
}
73+
}
74+
}
75+
}
76+
}
77+
78+
return Collections.unmodifiableMap(subProperties);
79+
80+
}
81+
82+
/**
83+
* Normalize the prefix
84+
*
85+
* @param prefix the prefix
86+
* @return the prefix
87+
*/
88+
public static String normalizePrefix(String prefix) {
89+
return prefix.endsWith(".") ? prefix : prefix + ".";
90+
}
91+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
11

2+
demo.config.a1.demoName = d1
3+
demo.config.a1.demoDesc = demo1
4+
5+
demo.config.a2.demoName = d2
6+
demo.config.a2.demoDesc = demo2
7+
8+
demo.config.a3.demoName = d3
9+
demo.config.a3.demoDesc = demo3

04fx/demo/src/test/java/io/kimmking/javacourse/demo/DemoApplicationTests.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,45 @@
22

33
import org.junit.jupiter.api.Test;
44
import org.springframework.boot.test.context.SpringBootTest;
5+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
6+
import org.springframework.context.annotation.PropertySource;
7+
8+
import java.util.Arrays;
9+
10+
import static org.junit.jupiter.api.Assertions.*;
511

612
@SpringBootTest
713
class DemoApplicationTests {
8-
914
@Test
10-
void contextLoads() {
15+
void testDemoConfig() {
16+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
17+
context.register(TestConfig.class);
18+
context.refresh();
19+
20+
System.out.println(Arrays.toString(context.getBeanNamesForType(DemoConfig.class)));
21+
assertEquals(3, context.getBeansOfType(DemoConfig.class).size());
22+
23+
DemoConfig demoConfig = context.getBean("a1", DemoConfig.class);
24+
System.out.println("a1=" + demoConfig.toString());
25+
assertEquals("d1", demoConfig.getDemoName());
26+
assertEquals("demo1", demoConfig.getDemoDesc());
27+
28+
demoConfig = context.getBean("a2", DemoConfig.class);
29+
System.out.println("a2=" + demoConfig.toString());
30+
assertEquals("d2", demoConfig.getDemoName());
31+
assertEquals("demo2", demoConfig.getDemoDesc());
32+
33+
demoConfig = context.getBean("a3", DemoConfig.class);
34+
System.out.println("a3=" + demoConfig.toString());
35+
assertEquals("d3", demoConfig.getDemoName());
36+
assertEquals("demo3", demoConfig.getDemoDesc());
37+
38+
}
39+
40+
@EnableDemoConfigBindings(prefix = "demo.config", type = DemoConfig.class)
41+
@PropertySource("application.properties")
42+
private static class TestConfig {
43+
1144
}
1245

1346
}

0 commit comments

Comments
 (0)