Description
Describe the bug
The order of injection and execution of user-defined OpenApiCustomiser
beans appears to be controllable via the standard Spring means (annotate with @Order
or implement org.springframework.core.Ordered
). However, this is not true of GlobalOpenApiCustomizer
beans which appear to be injected/executed in the order they happened to be defined in the backend configuration. It is particularly confusing when there are multiple beans that implement both interfaces - the order of execution changes depending on whether the api-docs URL used is grouped vs non-grouped.
To Reproduce
Steps to reproduce the behavior:
- Using spring-boot 2.7.6
- Using springdoc-openapi 1.6.14.
- Run the below Spring Boot app
- Open
http://localhost:8080/v3/api-docs
in a browser - Observe the console output and notice that customizers are executed in order 1, 2, 3.
- Open
http://localhost:8080/v3/api-docs/mygroup
in a browser - Observe the console output and notice that customizers are executed in order 3, 2, 1.
package order;
import io.swagger.v3.oas.models.OpenAPI;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@Import({
OrderDemo.Customizer3.class,
OrderDemo.Customizer2.class,
OrderDemo.Customizer1.class,
})
public class OrderDemo {
@Order(3)
public static class Customizer3 implements OpenApiCustomiser, GlobalOpenApiCustomizer {
public void customise(OpenAPI openApi) {
System.out.println("-> 3");
}
}
@Order(2)
public static class Customizer2 implements OpenApiCustomiser, GlobalOpenApiCustomizer {
public void customise(OpenAPI openApi) {
System.out.println("-> 2");
}
}
@Order(1)
public static class Customizer1 implements OpenApiCustomiser, GlobalOpenApiCustomizer {
public void customise(OpenAPI openApi) {
System.out.println("-> 1");
}
}
@RestController
public static class MyController {
@GetMapping("/test")
public String testingMethod() {
return "foo";
}
}
public static void main(String[] args) {
// Ensure at least one group is configured
System.setProperty("springdoc.group-configs[0].group", "mygroup");
System.setProperty("springdoc.group-configs[0].paths-to-match", "/test");
SpringApplication.run(OrderDemo.class, args);
}
}
Expected behavior
- The ordering behavior should be the same for
GlobalOpenApiCustomizer
andOpenApiCustomiser
beans. - Ideally ordering would be controllable via standard Spring means (e.g.
@Order
).
Additional Context
I believe this is due to the fact that MultipleOpenApiResource.afterPropertiesSet()
(only used for group-configs?) obtains its list of GlobalOpenApiCustomizer
s via ApplicationContext.getBeansOfType(Class)
, which is documented to return beans in the "order of the backend configuration", whereas the OpenApiWebResource
(used for non-grouped) is autowired and therefore is controllable via standard means (e.g @Order
).