How can I get a list of instantiated beans from Spring?

前端 未结 6 727
挽巷
挽巷 2020-12-05 07:50

I have several beans in my Spring context that have state, so I\'d like to reset that state before/after unit tests.

My idea was to add a method to a helper class wh

相关标签:
6条回答
  • 2020-12-05 08:20

    I had to improve it a little

    @Resource
    AbstractApplicationContext context;
    
    @After
    public void cleanup() {
        resetAllMocks();
    }
    
    private void resetAllMocks() {
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        for (String name : context.getBeanDefinitionNames()) {
            Object bean = beanFactory.getSingleton(name);
            if (Mockito.mockingDetails(bean).isMock()) {
                Mockito.reset(bean);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-05 08:23

    For example:

     public static List<Object> getInstantiatedSigletons(ApplicationContext ctx) {
                List<Object> singletons = new ArrayList<Object>();
    
                String[] all = ctx.getBeanDefinitionNames();
    
                ConfigurableListableBeanFactory clbf = ((AbstractApplicationContext) ctx).getBeanFactory();
                for (String name : all) {
                    Object s = clbf.getSingleton(name);
                    if (s != null)
                        singletons.add(s);
                }
    
                return singletons;
    
        }
    
    0 讨论(0)
  • 2020-12-05 08:25

    I am not sure whether this will help you or not.

    You need to create your own annotation eg. MyAnnot. And place that annotation on the class which you want to get. And then using following code you might get the instantiated bean.

    ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
    scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnot.class));
    for (BeanDefinition beanDefinition : scanner.findCandidateComponents("com.xxx.yyy")){
        System.out.println(beanDefinition.getBeanClassName());
    }
    

    This way you can get all the beans having your custom annotation.

    0 讨论(0)
  • 2020-12-05 08:30

    Using the previous answers, I've updated this to use Java 8 Streams API:

    @Inject
    private ApplicationContext applicationContext;
    
    @Before
    public void resetMocks() {
        ConfigurableListableBeanFactory beanFactory = ((AbstractApplicationContext) applicationContext).getBeanFactory();
        Stream.of(applicationContext.getBeanDefinitionNames())
                .map(n -> beanFactory.getSingleton(n))
                // My ConfigurableListableBeanFactory isn't compiled for 1.8 so can't use method reference. If yours is, you can say
                // .map(ConfigurableListableBeanFactory::getSingleton)
                .filter(b -> Mockito.mockingDetails(b).isMock())
                .forEach(Mockito::reset);
    }
    
    0 讨论(0)
  • 2020-12-05 08:33

    applicationContext.getBeanDefinitionNames() does not show the beans which are registered without BeanDefinition instance.

    package io.velu.core;
    
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ComponentScan
    public class Core {
    
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Core.class);
        String[] singletonNames = context.getDefaultListableBeanFactory().getSingletonNames();
        for (String singleton : singletonNames) {
            System.out.println(singleton);
        }       
    }
    

    }


    Console Output

    environment
    systemProperties
    systemEnvironment
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    messageSource
    applicationEventMulticaster
    lifecycleProcessor
    

    As you can see in the output, environment, systemProperties, systemEnvironment beans will not be shown using context.getBeanDefinitionNames() method.

    Spring Boot

    For spring boot web applications, all the beans can be listed using the below endpoint.

    @RestController
    @RequestMapping("/list")
    class ExportController {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    @GetMapping("/beans")
    @ResponseStatus(value = HttpStatus.OK)
    String[] registeredBeans() {
        return printBeans();
    }
    
    private String[] printBeans() {
        AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
        if (autowireCapableBeanFactory instanceof SingletonBeanRegistry) {
            String[] singletonNames = ((SingletonBeanRegistry) autowireCapableBeanFactory).getSingletonNames();
            for (String singleton : singletonNames) {
                System.out.println(singleton);
            }
            return singletonNames;
        }
        return null;
    }
    

    }


    [ "autoConfigurationReport", "springApplicationArguments", "springBootBanner", "springBootLoggingSystem", "environment", "systemProperties", "systemEnvironment", "org.springframework.context.annotation.internalConfigurationAnnotationProcessor", "org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory", "org.springframework.boot.autoconfigure.condition.BeanTypeRegistry", "org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry", "propertySourcesPlaceholderConfigurer", "org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store", "preserveErrorControllerTargetClassPostProcessor", "org.springframework.context.annotation.internalAutowiredAnnotationProcessor", "org.springframework.context.annotation.internalRequiredAnnotationProcessor", "org.springframework.context.annotation.internalCommonAnnotationProcessor", "org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor", "org.springframework.scheduling.annotation.ProxyAsyncConfiguration", "org.springframework.context.annotation.internalAsyncAnnotationProcessor", "methodValidationPostProcessor", "embeddedServletContainerCustomizerBeanPostProcessor", "errorPageRegistrarBeanPostProcessor", "messageSource", "applicationEventMulticaster", "org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat", "tomcatEmbeddedServletContainerFactory", "org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration$TomcatWebSocketConfiguration", "websocketContainerCustomizer", "spring.http.encoding-org.springframework.boot.autoconfigure.web.HttpEncodingProperties", "org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration", "localeCharsetMappingsCustomizer", "org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration", "serverProperties", "duplicateServerPropertiesDetector", "spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties", "org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration", "conventionErrorViewResolver", "org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration", "errorPageCustomizer", "servletContext", "contextParameters", "contextAttributes", "spring.mvc-org.springframework.boot.autoconfigure.web.WebMvcProperties", "spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartProperties", "org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration", "multipartConfigElement", "org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration", "org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletConfiguration", "dispatcherServlet", "dispatcherServletRegistration", "requestContextFilter", "org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration", "hiddenHttpMethodFilter", "httpPutFormContentFilter", "characterEncodingFilter", "org.springframework.context.event.internalEventListenerProcessor", "org.springframework.context.event.internalEventListenerFactory", "reportGeneratorApplication", "exportController", "exportService", "org.springframework.boot.autoconfigure.AutoConfigurationPackages", "org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration", "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration", "spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties", "standardJacksonObjectMapperBuilderCustomizer", "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration", "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration", "jsonComponentModule", "jacksonObjectMapperBuilder", "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration", "jacksonObjectMapper", "org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration", "org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration", "org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration", "org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration", "defaultValidator", "org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration", "error", "beanNameViewResolver", "errorAttributes", "basicErrorController", "org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration", "org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter", "mvcContentNegotiationManager", "org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration", "stringHttpMessageConverter", "org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration", "mappingJackson2HttpMessageConverter", "org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration", "messageConverters", "mvcConversionService", "mvcValidator", "requestMappingHandlerAdapter", "mvcResourceUrlProvider", "requestMappingHandlerMapping", "mvcPathMatcher", "mvcUrlPathHelper", "viewControllerHandlerMapping", "beanNameHandlerMapping", "resourceHandlerMapping", "defaultServletHandlerMapping", "mvcUriComponentsContributor", "httpRequestHandlerAdapter", "simpleControllerHandlerAdapter", "handlerExceptionResolver", "mvcViewResolver", "org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter$FaviconConfiguration", "faviconRequestHandler", "faviconHandlerMapping", "defaultViewResolver", "viewResolver", "welcomePageHandlerMapping", "org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration", "objectNamingStrategy", "mbeanServer", "mbeanExporter", "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration", "springApplicationAdminRegistrar", "org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration", "org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration", "spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties", "org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration", "multipartResolver", "org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration$RestTemplateConfiguration", "restTemplateBuilder", "org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration", "spring.devtools-org.springframework.boot.devtools.autoconfigure.DevToolsProperties", "org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$RestartConfiguration", "fileSystemWatcherFactory", "classPathRestartStrategy", "classPathFileSystemWatcher", "hateoasObjenesisCacheDisabler", "org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration$LiveReloadServerConfiguration", "org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration", "optionalLiveReloadServer", "org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration", "lifecycleProcessor" ]

    0 讨论(0)
  • 2020-12-05 08:37

    I've created a gist ApplicationContextAwareTestBase.

    This helper class does two things:

    1. It sets all internal fields to null. This allows Java to free memory that isn't used anymore. It's less useful with Spring (the Spring context still keeps references to all the beans), though.

    2. It tries to find all methods annotated with @After in all beans in the context and invokes them after the test.

    That way, you can easily reset state of your singletons / mocks without having to destroy / refresh the context.

    Example: You have a mock DAO:

    public void MockDao implements IDao {
    
        private Map<Long, Foo> database = Maps.newHashMap();
    
        @Override
        public Foo byId( Long id ) { return database.get( id ) );
    
        @Override
        public void save( Foo foo ) { database.put( foo.getId(), foo ); }
    
        @After
        public void reset() { database.clear(); }
    }
    

    The annotation will make sure reset() will be called after each unit test to clean up the internal state.

    0 讨论(0)
提交回复
热议问题