Skip to content

Commit f140ed9

Browse files
committed
Interact only with bean definition during bean registration phase
- register bean definition rather than bean instance - do not require GroupedOpenApi beans instances anymore for SpringdocActuatorBeanFactoryConfigurer bean registrations This will prevent any premature bean instantiation, violating the container and causing unintended side-effects (see BeanFactoryPostProcessor documentation)
1 parent 76b1abb commit f140ed9

File tree

3 files changed

+130
-75
lines changed

3 files changed

+130
-75
lines changed

springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,15 +428,14 @@ static class SpringDocActuatorConfiguration {
428428
/**
429429
* Springdoc bean factory post processor 3 bean factory post processor.
430430
*
431-
* @param groupedOpenApis the grouped open apis
432431
* @return the bean factory post processor
433432
*/
434433
@Bean
435434
@Lazy(false)
436435
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
437436
@Conditional(MultipleOpenApiSupportCondition.class)
438-
static SpringdocActuatorBeanFactoryConfigurer springdocBeanFactoryPostProcessor3(List<GroupedOpenApi> groupedOpenApis) {
439-
return new SpringdocActuatorBeanFactoryConfigurer(groupedOpenApis);
437+
static SpringdocActuatorBeanFactoryConfigurer springdocBeanFactoryPostProcessor3() {
438+
return new SpringdocActuatorBeanFactoryConfigurer();
440439
}
441440

442441
/**

springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocActuatorBeanFactoryConfigurer.java

Lines changed: 84 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
*
33
* *
44
* * *
5-
* * * * Copyright 2019-2020 the original author or authors.
5+
* * * * Copyright 2019-2022 the original author or authors.
66
* * * *
77
* * * * Licensed under the Apache License, Version 2.0 (the "License");
88
* * * * you may not use this file except in compliance with the License.
@@ -23,17 +23,20 @@
2323

2424
package org.springdoc.core;
2525

26-
import java.util.ArrayList;
27-
import java.util.List;
28-
2926
import org.springdoc.core.customizers.ActuatorOpenApiCustomizer;
3027
import org.springdoc.core.customizers.ActuatorOperationCustomizer;
31-
28+
import org.springframework.beans.BeansException;
3229
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
30+
import org.springframework.beans.factory.config.RuntimeBeanReference;
31+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
32+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
33+
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
3334
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
3435
import org.springframework.boot.context.properties.bind.BindResult;
3536
import org.springframework.boot.context.properties.bind.Binder;
36-
import org.springframework.util.CollectionUtils;
37+
import org.springframework.context.ApplicationContext;
38+
import org.springframework.context.ApplicationContextAware;
39+
import org.springframework.util.ObjectUtils;
3740

3841
import static org.springdoc.core.Constants.ACTUATOR_DEFAULT_GROUP;
3942
import static org.springdoc.core.Constants.ALL_PATTERN;
@@ -44,58 +47,95 @@
4447
/**
4548
* The type Springdoc bean factory configurer.
4649
* @author bnasslahsen
50+
* @author christophejan
4751
*/
48-
public class SpringdocActuatorBeanFactoryConfigurer extends SpringdocBeanFactoryConfigurer{
52+
public class SpringdocActuatorBeanFactoryConfigurer implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {
4953

5054
/**
51-
* The Grouped open apis.
55+
* The ApplicationContext.
5256
*/
53-
private List<GroupedOpenApi> groupedOpenApis;
57+
protected ApplicationContext applicationContext;
5458

55-
/**
56-
* Instantiates a new Springdoc actuator bean factory configurer.
57-
*
58-
* @param groupedOpenApis the grouped open apis
59-
*/
60-
public SpringdocActuatorBeanFactoryConfigurer(List<GroupedOpenApi> groupedOpenApis) {
61-
this.groupedOpenApis = groupedOpenApis;
59+
@Override
60+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
61+
this.applicationContext = applicationContext;
6262
}
6363

6464
@Override
65-
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
66-
final BindResult<WebEndpointProperties> result = Binder.get(environment)
65+
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
66+
final BindResult<WebEndpointProperties> result = Binder.get(applicationContext.getEnvironment())
6767
.bind(MANAGEMENT_ENDPOINTS_WEB, WebEndpointProperties.class);
6868
if (result.isBound()) {
6969
WebEndpointProperties webEndpointProperties = result.get();
7070

71-
List<GroupedOpenApi> newGroups = new ArrayList<>();
72-
73-
ActuatorOpenApiCustomizer actuatorOpenApiCustomiser = new ActuatorOpenApiCustomizer(webEndpointProperties);
74-
beanFactory.registerSingleton("actuatorOpenApiCustomiser", actuatorOpenApiCustomiser);
75-
ActuatorOperationCustomizer actuatorCustomizer = new ActuatorOperationCustomizer();
76-
beanFactory.registerSingleton("actuatorCustomizer", actuatorCustomizer);
77-
78-
GroupedOpenApi actuatorGroup = GroupedOpenApi.builder().group(ACTUATOR_DEFAULT_GROUP)
79-
.pathsToMatch(webEndpointProperties.getBasePath() + ALL_PATTERN)
80-
.pathsToExclude(webEndpointProperties.getBasePath() + HEALTH_PATTERN)
81-
.addOperationCustomizer(actuatorCustomizer)
82-
.addOpenApiCustomiser(actuatorOpenApiCustomiser)
83-
.build();
84-
// Add the actuator group
85-
newGroups.add(actuatorGroup);
86-
87-
if (CollectionUtils.isEmpty(groupedOpenApis)) {
88-
GroupedOpenApi defaultGroup = GroupedOpenApi.builder().group(DEFAULT_GROUP_NAME)
89-
.pathsToMatch(ALL_PATTERN)
90-
.pathsToExclude(webEndpointProperties.getBasePath() + ALL_PATTERN)
91-
.build();
92-
// Register the default group
93-
newGroups.add(defaultGroup);
94-
}
71+
boolean addDefaultGroup = ObjectUtils.isEmpty(applicationContext
72+
.getBeanNamesForType(GroupedOpenApi.class, true, false));
73+
74+
registry.registerBeanDefinition("actuatorOpenApiCustomiser", BeanDefinitionBuilder
75+
.genericBeanDefinition(ActuatorOpenApiCustomizer.class)
76+
.addConstructorArgValue(webEndpointProperties)
77+
.getBeanDefinition());
78+
79+
registry.registerBeanDefinition("actuatorCustomizer", BeanDefinitionBuilder
80+
.genericBeanDefinition(ActuatorOperationCustomizer.class)
81+
.getBeanDefinition());
9582

96-
newGroups.forEach(elt -> beanFactory.registerSingleton(elt.getGroup(), elt));
83+
// register the actuator group bean definition
84+
registry.registerBeanDefinition(ACTUATOR_DEFAULT_GROUP, BeanDefinitionBuilder
85+
.genericBeanDefinition(SpringdocActuatorBeanFactoryConfigurer.class)
86+
.setFactoryMethod("actuatorGroupFactoryMethod")
87+
.addConstructorArgValue(webEndpointProperties.getBasePath())
88+
.addConstructorArgValue(new RuntimeBeanReference(ActuatorOpenApiCustomizer.class))
89+
.addConstructorArgValue(new RuntimeBeanReference(ActuatorOperationCustomizer.class))
90+
.getBeanDefinition());
91+
92+
if (addDefaultGroup) {
93+
// register the default group bean definition
94+
registry.registerBeanDefinition(DEFAULT_GROUP_NAME, BeanDefinitionBuilder
95+
.genericBeanDefinition(SpringdocActuatorBeanFactoryConfigurer.class)
96+
.setFactoryMethod("defaultGroupFactoryMethod")
97+
.addConstructorArgValue(webEndpointProperties.getBasePath())
98+
.getBeanDefinition());
99+
}
97100
}
98-
initBeanFactoryPostProcessor(beanFactory);
101+
}
102+
103+
@Override
104+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
105+
SpringdocBeanFactoryConfigurer.initBeanFactoryPostProcessor(beanFactory);
106+
}
107+
108+
/**
109+
* Actuator {@link GroupedOpenApi} factory method.
110+
*
111+
* @param actuatorBasePath the actuator base path
112+
* @param actuatorOpenApiCustomiser the {@link ActuatorOpenApiCustomizer}
113+
* @param actuatorOperationCustomizer the {@link ActuatorOperationCustomizer}
114+
*
115+
* @return the actuator {@link GroupedOpenApi}
116+
*/
117+
public static GroupedOpenApi actuatorGroupFactoryMethod(String actuatorBasePath,
118+
ActuatorOpenApiCustomizer actuatorOpenApiCustomiser, ActuatorOperationCustomizer actuatorOperationCustomizer) {
119+
return GroupedOpenApi.builder().group(ACTUATOR_DEFAULT_GROUP)
120+
.pathsToMatch(actuatorBasePath + ALL_PATTERN)
121+
.pathsToExclude(actuatorBasePath + HEALTH_PATTERN)
122+
.addOpenApiCustomiser(actuatorOpenApiCustomiser)
123+
.addOperationCustomizer(actuatorOperationCustomizer)
124+
.build();
125+
}
126+
127+
/**
128+
* Default {@link GroupedOpenApi} factory method.
129+
*
130+
* @param actuatorBasePath the actuator base path
131+
*
132+
* @return the default {@link GroupedOpenApi}
133+
*/
134+
public static GroupedOpenApi defaultGroupFactoryMethod(String actuatorBasePath) {
135+
return GroupedOpenApi.builder().group(DEFAULT_GROUP_NAME)
136+
.pathsToMatch(ALL_PATTERN)
137+
.pathsToExclude(actuatorBasePath + ALL_PATTERN)
138+
.build();
99139
}
100140

101141
}

springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
*
33
* *
44
* * *
5-
* * * * Copyright 2019-2020 the original author or authors.
5+
* * * * Copyright 2019-2022 the original author or authors.
66
* * * *
77
* * * * Licensed under the Apache License, Version 2.0 (the "License");
88
* * * * you may not use this file except in compliance with the License.
@@ -23,18 +23,18 @@
2323

2424
package org.springdoc.core;
2525

26-
import java.util.List;
27-
import java.util.stream.Collectors;
28-
2926
import io.swagger.v3.oas.models.OpenAPI;
3027

31-
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
28+
import org.springdoc.core.SpringDocConfigProperties.GroupConfig;
29+
import org.springframework.beans.BeansException;
3230
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
31+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
32+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
33+
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
3334
import org.springframework.boot.context.properties.bind.BindResult;
3435
import org.springframework.boot.context.properties.bind.Binder;
35-
import org.springframework.context.EnvironmentAware;
36-
import org.springframework.core.env.Environment;
37-
import org.springframework.lang.Nullable;
36+
import org.springframework.context.ApplicationContext;
37+
import org.springframework.context.ApplicationContextAware;
3838
import org.springframework.util.CollectionUtils;
3939

4040
import static org.springdoc.core.Constants.SPRINGDOC_PREFIX;
@@ -43,41 +43,57 @@
4343
/**
4444
* The type Springdoc bean factory configurer.
4545
* @author bnasslahsen
46+
* @author christophejan
4647
*/
47-
public class SpringdocBeanFactoryConfigurer implements EnvironmentAware, BeanFactoryPostProcessor {
48+
public class SpringdocBeanFactoryConfigurer implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {
4849

4950
/**
50-
* The Environment.
51+
* The ApplicationContext.
5152
*/
52-
@Nullable
53-
protected Environment environment;
53+
protected ApplicationContext applicationContext;
5454

5555
@Override
56-
public void setEnvironment(Environment environment) {
57-
this.environment = environment;
56+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
57+
this.applicationContext = applicationContext;
5858
}
5959

6060
@Override
61-
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
62-
final BindResult<SpringDocConfigProperties> result = Binder.get(environment)
61+
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
62+
final BindResult<SpringDocConfigProperties> result = Binder.get(applicationContext.getEnvironment())
6363
.bind(SPRINGDOC_PREFIX, SpringDocConfigProperties.class);
6464
if (result.isBound()) {
65-
SpringDocConfigProperties springDocGroupConfig = result.get();
66-
List<GroupedOpenApi> groupedOpenApis = springDocGroupConfig.getGroupConfigs().stream()
67-
.map(elt -> {
68-
GroupedOpenApi.Builder builder = GroupedOpenApi.builder();
69-
if (!CollectionUtils.isEmpty(elt.getPackagesToScan()))
70-
builder.packagesToScan(elt.getPackagesToScan().toArray(new String[0]));
71-
if (!CollectionUtils.isEmpty(elt.getPathsToMatch()))
72-
builder.pathsToMatch(elt.getPathsToMatch().toArray(new String[0]));
73-
return builder.group(elt.getGroup()).build();
74-
})
75-
.collect(Collectors.toList());
76-
groupedOpenApis.forEach(elt -> beanFactory.registerSingleton(elt.getGroup(), elt));
65+
result.get().getGroupConfigs().stream().forEach(groupConfig ->
66+
// register bean definitions
67+
registry.registerBeanDefinition(groupConfig.getGroup(), BeanDefinitionBuilder
68+
.genericBeanDefinition(SpringdocBeanFactoryConfigurer.class)
69+
.setFactoryMethod("groupedOpenApisFactoryMethod")
70+
.addConstructorArgValue(groupConfig)
71+
.getBeanDefinition())
72+
);
7773
}
74+
}
75+
76+
@Override
77+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
7878
initBeanFactoryPostProcessor(beanFactory);
7979
}
8080

81+
/**
82+
* {@link GroupedOpenApi} factory method from {@link GroupConfig}.
83+
*
84+
* @param groupConfig the {@link GroupConfig}
85+
*
86+
* @return the {@link GroupedOpenApi}
87+
*/
88+
public static GroupedOpenApi groupedOpenApisFactoryMethod(GroupConfig groupConfig) {
89+
GroupedOpenApi.Builder builder = GroupedOpenApi.builder();
90+
if (!CollectionUtils.isEmpty(groupConfig.getPackagesToScan()))
91+
builder.packagesToScan(groupConfig.getPackagesToScan().toArray(new String[0]));
92+
if (!CollectionUtils.isEmpty(groupConfig.getPathsToMatch()))
93+
builder.pathsToMatch(groupConfig.getPathsToMatch().toArray(new String[0]));
94+
return builder.group(groupConfig.getGroup()).build();
95+
}
96+
8197
/**
8298
* Init bean factory post processor.
8399
*

0 commit comments

Comments
 (0)