Analysis and solution of @Autowired failure problem after using @ServerEndpoint in SpringBoot

The previous technical blog recorded how SpringBoot integrates WebSocket to achieve mass message push, and mainly built the basic framework:

SpringBoot integrates WebSocket to achieve mass message push

It was later discovered that after @ServerEndpoint was used, @Autowired became invalid. Why?

Problem Description

In a specific business scenario, after the user is successfully connected, 10 pieces of data are first obtained from the library table and displayed as the default initialization data.

We take it for granted that the corresponding Service will be dependency injected through the @Autowired annotation. It was discovered that a null pointer exception was reported, that is, the required Service was not successfully injected.

The current writing is as follows:

@ServerEndpoint("/imserver/{userId}")
@Component
public class WebSocketServer {
    
    
	@Autowired
    private OrderService orderService;
}

problem analysis

Spring management adopts singleton mode (singleton) , and WebSocket is multi-object, that is, each client corresponds to a WebSocket object in the backend, which can also be understood as a new WebSocket, so of course the automatically injected object cannot be obtained, because The two just conflict.

The @Autowired annotation injection object operation is performed at startup, not when it is used. WebSocket only instantiates the object when the connection is in use, and there are multiple objects when there are multiple connections.

So we can conclude that this Service is not injected into WebSocket at all.

solution

Method one uses static static object

Change the Service that needs to be injected to static, let it belong to the current class, and then inject through the setOrderService method to solve the problem.

@ServerEndpoint("/imserver/{userId}")
@Component
public class WebSocketServer {
    
    

    private static OrderService orderService;
    
	@Autowired
	public void setOrderService(OrderService service) {
    
    
		WebSocketServer.orderService = orderService;
	}
}

Method two dynamically remove the OrderService from the Spring container

/**
 * 获取spring容器
 * 当一个类实现了这个接口ApplicationContextAware之后,这个类就可以方便获得ApplicationContext中的所有bean。
 * 换句话说,这个类可以直接获取spring配置文件中所有有引用到的bean对象
 * 前提条件需作为一个普通的bean在spring的配置文件中进行注册 
 */
public class SpringCtxUtils implements ApplicationContextAware {
    
    

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
    	SpringCtxUtils.applicationContext = applicationContext;
    }


    public static <T> T getBean(Class<T> type) {
    
    
        try {
    
    
            return applicationContext.getBean(type);
        } catch (NoUniqueBeanDefinitionException e) {
    
       
        	//出现多个,选第一个
            String beanName = applicationContext.getBeanNamesForType(type)[0];
            return applicationContext.getBean(beanName, type);
        }
    }

    public static <T> T getBean(String beanName, Class<T> type) {
    
    
        return applicationContext.getBean(beanName, type);
    }
}

Call in WebSocketServer:

private OrderService orderService = SpringCtxUtils.getBean(OrderService.class);

Method one is relatively simple and effective in the pro-test. Method two has no specific test. You can try it.

Guess you like

Origin blog.csdn.net/j1231230/article/details/114641956