spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.25k stars 40.7k forks source link

IDE plugin support - information about beans #43208

Open grisha9 opened 3 hours ago

grisha9 commented 3 hours ago

It would be nice to get all the information about beans (bean definition) in a native way, without starting the application and creating bean instances (start application may have many side effects: DB modification, execute post-construct logic and etc). This can be very useful for creating plugins for IDE independent of IDE implementation.
Maybe there is already something for this?

Maybe it is worth adding some task to Spring Boot Plugins (Maven/Gradle) that like the Maven task 'mvn dependency:tree' , would print out information about beans without starting the application? Then we could just run a build system plugin task, for example for Maven: "mvn org.springframework.boot:spring-boot-maven-plugin:3.2.0:print-bean-definition" , and output information about the beans in some format to the console or file.

I made some prototype - https://github.com/grisha9/spring-bean-printer
The module bean-printer contain logic to print all the bean definition information to the console after the phase "spring.context.beans.post-process". I have custom ApplicationStartup implementation that throw exception after it phase. And custom SpringApplicationRunListener that print all bean info from ConfigurableApplicationContext in failed method.
Also in this module I rewrote the method for launching the spring boot application as shown below. I pass SpringBootApplication class name as env parameter - "org.example.spring.appClassName" and I use my own implementation for ApplicationStartup and SpringApplicationRunListener. And packed it into a jar file - bean-printer.jar

public class BeanDefinitionPrinter {    
    public static void main(String[] args) throws ClassNotFoundException {    
        Class<?> applicationClass = Class.forName(System.getenv("org.example.spring.appClassName"));    
        SpringApplication springApplication = new SpringApplication(applicationClass);     
        springApplication.setApplicationStartup(new SampleApplicationStartup());    
        SpringApplicationHook applicationHook = application -> new SampleFailedSpringApplicationRunListener();    
        SpringApplication.withHook(applicationHook, () -> springApplication.run(args));    
    }
}

The module project-sample contains simple project for test usages. In this module I added spring-boot-maven-plugin this next configuration

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <environmentVariables>
            <org.example.spring.appClassName>org.springframework.sample.Application</org.example.spring.appClassName>
        </environmentVariables>
        <additionalClasspathElements>../bean-printer/target/bean-printer-3.0.0.jar</additionalClasspathElements>
        <mainClass>org.example.bean.printer.BeanDefinitionPrinter</mainClass>
    </configuration>
</plugin>

Configuration contains additional classpath element with bean-printer.jar and main class for run it - org.example.bean.printer.BeanDefinitionPrinter and env variable to real application class name. As result it print bean definition information in console (mvn org.springframework.boot:spring-boot-maven-plugin:3.2.0:run):

BeanInfo{beanName='application', className='org.springframework.sample.Application', methodName='null', methodReturnTypeName='null', scope='singleton', primary=false}
BeanInfo{beanName='testComponentBean', className='org.springframework.sample.TestComponentBean', methodName='null', methodReturnTypeName='null', scope='singleton', primary=false}
BeanInfo{beanName='testConfig', className='org.springframework.sample.TestConfig', methodName='null', methodReturnTypeName='null', scope='singleton', primary=false}
BeanInfo{beanName='methodBean', className='org.springframework.sample.TestConfig', methodName='methodBean', methodReturnTypeName='org.springframework.sample.TestMethodBean', scope='singleton', primary=false}
...

Maybe it's worth creating a separate task for build systems for this?

wilkinsona commented 2 hours ago

Thanks for the suggestion.

This can be very useful for creating plugins for IDE independent of IDE implementation

I'm not aware of a shared framework for building IDE plugins that allows a plugin implementation to be independent of the IDE implementation. Can you please expand on this?

Given that major IDEs such as IntelliJ IDEA already have support for introspecting an application's beans and Spring Boot already has the beans endpoint that makes this information available at runtime, my feeling is that what you're proposing is likely to occupy a niche for which there's not much demand. As such, I don't think it's something that we'd want to support in Spring Boot itself.

grisha9 commented 1 hour ago

Spring Boot already has the beans endpoint that makes this information available at runtime

Yes, but it requires running the application. But this is not always possible. I worked on large projects where there was no possibility to run the application locally and development was carried out through tests. (This often requires a local environment - DB and etc.)

For example IDEA has also problem with DI context recognized: image
On this example IDEA successful recognize ConditionalBean, but it is not context and a warning about this should be shown. And the application does not starting.

Can you please expand on this?

If Spring Boot had a build tool plugin (Maven/Gradle) for getting information about beans (without run application) it would greatly simplify the creation of IDE plugins, namely support for DI. Such a build tool task is a platform-independent solution for get context beans and can be used to create a spring-boot support plugin for any IDE (IDEA or VS-CODE)