Search code examples
javaspring-bootautowiredjava-websocket

Java Spring-boot - How to use @Autowired with @ServerEndpoint?


I know there are lot of questions on this topic. I have read the spring boot doc and all of the solutions here. According spring boot doc, @ServerEndpoint is a Javax annotation and @Autowired components are spring-boot managed. These two cannot be used together. The solution to this would be to add SpringConfigurator as configurator of the ServerEndpoint. When I tried this I do get the following error:

Failed to find the root WebApplicationContext. Was ContextLoaderListener not used?

There is no example in the spring-boot websocket page to use ContextLoaderListener. How can use ContextLoaderListener so that components can be injected into @ServerEndpoint annotated controllers?

The following is my code.

Websocket controller

@ServerEndpoint(value = "/call-stream", configurator = SpringConfigurator.class)
public class CallStreamWebSocketController
{  
    @Autowired
    private IntelligentResponseService responseServiceFacade;

    // Other methods
}

Websocket configurations

@Configuration
public class WebSocketConfiguration
{
    @Bean
    public CallStreamWebSocketController callStreamWebSocketController()
    {
        return new CallStreamWebSocketController();
    }

    @Bean
    public ServerEndpointExporter serverEndpointExporter()
    {
        return new ServerEndpointExporter();
    }
}

Edit: This has been tagged as a duplicate of this question. I have tried the solution specified in the answers. The solution is to add SpringConfigurator as configurator of the @ServerEndpoint. After adding this I still do get the error mentioned in the details.


Solution

  • After some research I found a way to force spring-boot to inject a component into an externally managed/instantiated class.

    1) Add a generic method to your class extending ApplicationContextAware to return a bean.

    @Component
    public class SpringContext implements ApplicationContextAware {
    
        private static ApplicationContext context;
    
        @Override
        public void setApplicationContext(ApplicationContext context) throws BeansException {
            SpringContext.context = context;
        }
    
        public ApplicationContext getApplicationContext() {
            return context;
        }
    
        // Generic method to return a beanClass
        public static <T> T getBean(Class<T> beanClass)
        {
            return context.getBean(beanClass);
        }
    }
    

    2) Use this method to initialize the class object you want to be injected

    private IntelligentResponseService responseServiceFacade = SpringContext.getBean(IntelligentResponseService.class);
    

    So after the above changes my websocket controller would look like this

    @ServerEndpoint(value = "/call-stream", configurator = SpringConfigurator.class)
    public class CallStreamWebSocketController
    {  
        private IntelligentResponseService responseServiceFacade = SpringContext.getBean(IntelligentResponseService.class);
    
        // Other methods
    }