Skip to content

Commit 1678de7

Browse files
committed
Merge pull request #17020 from nosan
* gh-17020: Polish "Allow depended on beans to be identified by type" Allow depended on beans to be identified by type Closes gh-17020
2 parents 852f4a2 + 80650f4 commit 1678de7

File tree

2 files changed

+223
-14
lines changed

2 files changed

+223
-14
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AbstractDependsOnBeanFactoryPostProcessor.java

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818

1919
import java.util.Arrays;
2020
import java.util.HashSet;
21+
import java.util.LinkedHashSet;
2122
import java.util.Set;
23+
import java.util.function.Function;
24+
import java.util.stream.Collectors;
2225

2326
import org.springframework.beans.factory.BeanFactory;
2427
import org.springframework.beans.factory.BeanFactoryUtils;
@@ -32,13 +35,14 @@
3235

3336
/**
3437
* Abstract base class for a {@link BeanFactoryPostProcessor} that can be used to
35-
* dynamically declare that all beans of a specific type should depend on one or more
36-
* specific beans.
38+
* dynamically declare that all beans of a specific type should depend on specific other
39+
* beans identified by name or type.
3740
*
3841
* @author Marcel Overdijk
3942
* @author Dave Syer
4043
* @author Phillip Webb
4144
* @author Andy Wilkinson
45+
* @author Dmytro Nosan
4246
* @since 1.3.0
4347
* @see BeanDefinition#setDependsOn(String[])
4448
*/
@@ -48,13 +52,29 @@ public abstract class AbstractDependsOnBeanFactoryPostProcessor implements BeanF
4852

4953
private final Class<? extends FactoryBean<?>> factoryBeanClass;
5054

51-
private final String[] dependsOn;
55+
private final Function<ListableBeanFactory, Set<String>> dependsOn;
5256

5357
protected AbstractDependsOnBeanFactoryPostProcessor(Class<?> beanClass,
5458
Class<? extends FactoryBean<?>> factoryBeanClass, String... dependsOn) {
5559
this.beanClass = beanClass;
5660
this.factoryBeanClass = factoryBeanClass;
57-
this.dependsOn = dependsOn;
61+
this.dependsOn = (beanFactory) -> new HashSet<>(Arrays.asList(dependsOn));
62+
}
63+
64+
/**
65+
* Create an instance with target bean and factory bean classes and dependency types.
66+
* @param beanClass target bean class
67+
* @param factoryBeanClass target factory bean class
68+
* @param dependencyTypes dependency types
69+
* @since 2.1.7
70+
*/
71+
protected AbstractDependsOnBeanFactoryPostProcessor(Class<?> beanClass,
72+
Class<? extends FactoryBean<?>> factoryBeanClass, Class<?>... dependencyTypes) {
73+
this.beanClass = beanClass;
74+
this.factoryBeanClass = factoryBeanClass;
75+
this.dependsOn = (beanFactory) -> Arrays.stream(dependencyTypes)
76+
.flatMap((dependencyType) -> getBeanNames(beanFactory, dependencyType).stream())
77+
.collect(Collectors.toSet());
5878
}
5979

6080
/**
@@ -67,31 +87,42 @@ protected AbstractDependsOnBeanFactoryPostProcessor(Class<?> beanClass, String..
6787
this(beanClass, null, dependsOn);
6888
}
6989

90+
/**
91+
* Create an instance with target bean class and dependency types.
92+
* @param beanClass target bean class
93+
* @param dependencyTypes dependency types
94+
* @since 2.1.7
95+
*/
96+
protected AbstractDependsOnBeanFactoryPostProcessor(Class<?> beanClass, Class<?>... dependencyTypes) {
97+
this(beanClass, null, dependencyTypes);
98+
}
99+
70100
@Override
71101
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
72102
for (String beanName : getBeanNames(beanFactory)) {
73103
BeanDefinition definition = getBeanDefinition(beanName, beanFactory);
74104
String[] dependencies = definition.getDependsOn();
75-
for (String bean : this.dependsOn) {
76-
dependencies = StringUtils.addStringToArray(dependencies, bean);
105+
for (String dependencyName : this.dependsOn.apply(beanFactory)) {
106+
dependencies = StringUtils.addStringToArray(dependencies, dependencyName);
77107
}
78108
definition.setDependsOn(dependencies);
79109
}
80110
}
81111

82-
private Iterable<String> getBeanNames(ListableBeanFactory beanFactory) {
83-
Set<String> names = new HashSet<>();
84-
names.addAll(Arrays
85-
.asList(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, this.beanClass, true, false)));
112+
private Set<String> getBeanNames(ListableBeanFactory beanFactory) {
113+
Set<String> names = getBeanNames(beanFactory, this.beanClass);
86114
if (this.factoryBeanClass != null) {
87-
for (String factoryBeanName : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory,
88-
this.factoryBeanClass, true, false)) {
89-
names.add(BeanFactoryUtils.transformedBeanName(factoryBeanName));
90-
}
115+
names.addAll(getBeanNames(beanFactory, this.factoryBeanClass));
91116
}
92117
return names;
93118
}
94119

120+
private static Set<String> getBeanNames(ListableBeanFactory beanFactory, Class<?> beanClass) {
121+
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, beanClass, true, false);
122+
return Arrays.stream(names).map(BeanFactoryUtils::transformedBeanName)
123+
.collect(Collectors.toCollection(LinkedHashSet::new));
124+
}
125+
95126
private static BeanDefinition getBeanDefinition(String beanName, ConfigurableListableBeanFactory beanFactory) {
96127
try {
97128
return beanFactory.getBeanDefinition(beanName);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Copyright 2012-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure;
18+
19+
import org.junit.Test;
20+
21+
import org.springframework.beans.factory.BeanFactory;
22+
import org.springframework.beans.factory.FactoryBean;
23+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
24+
import org.springframework.beans.factory.config.BeanDefinition;
25+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
26+
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
27+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
28+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
29+
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.annotation.Configuration;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
34+
/**
35+
* Tests for {@link AbstractDependsOnBeanFactoryPostProcessor}.
36+
*
37+
* @author Dmytro Nosan
38+
*/
39+
public class AbstractDependsOnBeanFactoryPostProcessorTests {
40+
41+
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
42+
.withUserConfiguration(FooBarConfiguration.class);
43+
44+
@Test
45+
public void fooBeansShouldDependOnBarBeanNames() {
46+
this.contextRunner
47+
.withUserConfiguration(FooDependsOnBarNamePostProcessor.class, FooBarFactoryBeanConfiguration.class)
48+
.run(this::assertThatFooDependsOnBar);
49+
}
50+
51+
@Test
52+
public void fooBeansShouldDependOnBarBeanTypes() {
53+
this.contextRunner
54+
.withUserConfiguration(FooDependsOnBarTypePostProcessor.class, FooBarFactoryBeanConfiguration.class)
55+
.run(this::assertThatFooDependsOnBar);
56+
}
57+
58+
@Test
59+
public void fooBeansShouldDependOnBarBeanNamesParentContext() {
60+
try (AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext(
61+
FooBarFactoryBeanConfiguration.class)) {
62+
this.contextRunner.withUserConfiguration(FooDependsOnBarNamePostProcessor.class).withParent(parentContext)
63+
.run(this::assertThatFooDependsOnBar);
64+
}
65+
}
66+
67+
@Test
68+
public void fooBeansShouldDependOnBarBeanTypesParentContext() {
69+
try (AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext(
70+
FooBarFactoryBeanConfiguration.class)) {
71+
this.contextRunner.withUserConfiguration(FooDependsOnBarTypePostProcessor.class).withParent(parentContext)
72+
.run(this::assertThatFooDependsOnBar);
73+
}
74+
}
75+
76+
private void assertThatFooDependsOnBar(AssertableApplicationContext context) {
77+
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
78+
assertThat(getBeanDefinition("foo", beanFactory).getDependsOn()).containsExactly("bar", "barFactoryBean");
79+
assertThat(getBeanDefinition("fooFactoryBean", beanFactory).getDependsOn()).containsExactly("bar",
80+
"barFactoryBean");
81+
}
82+
83+
private BeanDefinition getBeanDefinition(String beanName, ConfigurableListableBeanFactory beanFactory) {
84+
try {
85+
return beanFactory.getBeanDefinition(beanName);
86+
}
87+
catch (NoSuchBeanDefinitionException ex) {
88+
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
89+
if (parentBeanFactory instanceof ConfigurableListableBeanFactory) {
90+
return getBeanDefinition(beanName, (ConfigurableListableBeanFactory) parentBeanFactory);
91+
}
92+
throw ex;
93+
}
94+
}
95+
96+
static class Foo {
97+
98+
}
99+
100+
static class Bar {
101+
102+
}
103+
104+
@Configuration
105+
static class FooBarFactoryBeanConfiguration {
106+
107+
@Bean
108+
public FooFactoryBean fooFactoryBean() {
109+
return new FooFactoryBean();
110+
}
111+
112+
@Bean
113+
public BarFactoryBean barFactoryBean() {
114+
return new BarFactoryBean();
115+
}
116+
117+
}
118+
119+
@Configuration
120+
static class FooBarConfiguration {
121+
122+
@Bean
123+
public Bar bar() {
124+
return new Bar();
125+
}
126+
127+
@Bean
128+
public Foo foo() {
129+
return new Foo();
130+
}
131+
132+
}
133+
134+
static class FooDependsOnBarTypePostProcessor extends AbstractDependsOnBeanFactoryPostProcessor {
135+
136+
protected FooDependsOnBarTypePostProcessor() {
137+
super(Foo.class, FooFactoryBean.class, Bar.class, BarFactoryBean.class);
138+
}
139+
140+
}
141+
142+
static class FooDependsOnBarNamePostProcessor extends AbstractDependsOnBeanFactoryPostProcessor {
143+
144+
protected FooDependsOnBarNamePostProcessor() {
145+
super(Foo.class, FooFactoryBean.class, "bar", "barFactoryBean");
146+
}
147+
148+
}
149+
150+
static class FooFactoryBean implements FactoryBean<Foo> {
151+
152+
@Override
153+
public Foo getObject() {
154+
return new Foo();
155+
}
156+
157+
@Override
158+
public Class<?> getObjectType() {
159+
return Foo.class;
160+
}
161+
162+
}
163+
164+
static class BarFactoryBean implements FactoryBean<Bar> {
165+
166+
@Override
167+
public Bar getObject() {
168+
return new Bar();
169+
}
170+
171+
@Override
172+
public Class<?> getObjectType() {
173+
return Bar.class;
174+
}
175+
176+
}
177+
178+
}

0 commit comments

Comments
 (0)