2016-12-21 10 views
0

У меня есть класс, который расширяет TextWebSocketHandler, и его основная цель - отправлять сообщения клиенту после определенного события с WebSocketSession. Чтобы реализовать это, я решил использовать перехват метода, предоставленный Spring AOP.Использование TextWebSocketHandler как @Aspect: после запуска перехватчика WebSocketSession instanse становится null

Проблема, когда перехватчик называется инкапсулированным экземпляром WebSocketSession становится null несмотря на то, прежде чем он был экземпляра в методе afterConnectionEstablished() (который можно увидеть с отладчиком). Интересно, что в каждом другом методе класса можно получить доступ к экземпляру сеанса.

Вот код класса:

@Aspect 
public class SystemStateWS extends TextWebSocketHandler { 

    private static final Logger LOGGER = LoggerFactory.getLogger(SystemStateWS.class); 

    private WebSocketSession session; 

    public SystemStateWS() { 

    } 

    @Override 
    public synchronized void afterConnectionEstablished(WebSocketSession session) throws Exception { 
    this.session = session; 
    } 

    @Override 
    protected synchronized void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { 
    System.out.println(message.getPayload()); 
    } 

    @Before("execution(* org.company.MyRestService.processTablesClearQuery(..))") 
    public void onConfigurationStart(JoinPoint joinPoint) { 
    if (session.isOpen()) { // session is null here 
     try { 
     session.sendMessage(new TextMessage("config started")); 
     LOGGER.debug("Configuration status message was sent to client"); 

     } catch (IOException e) { 
     LOGGER.warn("Error during the passing of the message to client"); 
     } 
    } else { 
     LOGGER.warn("Unable to send message to client: session is closed"); 
    } 

    } 

    @Override 
    public synchronized void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { 
    LOGGER.debug("Socket is closing"); 
    } 


    @Override 
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { 
    super.handleTransportError(session, exception); 
    LOGGER.warn("Transport error", exception); 
    } 
} 

И файл конфигурации контекста:

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:websocket="http://www.springframework.org/schema/websocket" 
    xsi:schemaLocation=" 
     http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans.xsd  
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-3.0.xsd 
     http://www.springframework.org/schema/aop   
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
     http://www.springframework.org/schema/mvc 
     http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd 
     http://www.springframework.org/schema/websocket 
     http://www.springframework.org/schema/websocket/spring-websocket.xsd"> 

    <context:component-scan base-package="org.company" /> 

    <aop:aspectj-autoproxy/> 
    ... 

    <websocket:handlers> 
     <websocket:mapping path="/ws/sysState" handler="sysStateHandler" /> 
     <websocket:handshake-handler ref="handshakeHandler"/> 
    </websocket:handlers> 

    <bean class="org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean"> 
     <property name="maxTextMessageBufferSize" value="335544"/> 
     <property name="maxBinaryMessageBufferSize" value="335544"/> 
    </bean> 

    <bean id="handshakeHandler" class="org.springframework.web.socket.server.support.DefaultHandshakeHandler"> 
     <constructor-arg ref="upgradeStrategy"/> 
    </bean> 

    <bean id="upgradeStrategy" class="org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy"> 
    </bean> 

    <bean id="sysStateHandler" class="org.springframework.web.socket.handler.PerConnectionWebSocketHandler"> 
     <constructor-arg type="java.lang.Class" value="org.company.SystemStateWS"/> 
    </bean> 

    <bean id="systemStateWS" class="org.company.SystemStateWS"/> 

</beans> 

MyRestService.processTablesClearQuery ссылка в @Before фактически метод в одном из контроллеров REST приложение, но я не думаю, что это имеет большое значение.

К сожалению, я не знаком с такими аспектами. Для начала я даже не знаю, возможно ли комбинировать обработчик сокета с @Aspect. И если это возможно, в конце концов, почему именно WebSocketSession становится null как я могу избежать этого? Любая помощь приветствуется.

ответ

0

Таким образом, фактическая причина такого поведения заключалась в существовании многих случаев SystemStateWS в момент запуска перехватчика. И перехватчик просто срабатывает точно в том случае, когда метод afterConnectionEstablished() никогда не вызывался, поэтому ссылка WebSocketSession продолжала оставаться здесь null.

Итак, я решил зарегистрировать каждый экземпляр класса веб-сокета, когда метод afterConnectionEstablished() вызывается в одном специальном компоненте, а затем сделан автономным классом для перехватчика. Когда он вызывается сейчас, он просто вызывает необходимый метод во всех зарегистрированных экземплярах веб-сокета. Этот код сделал трюк для меня:

Регистрация класса сокета в буфер:

@Autowired 
    private SystemStateWebSocketBuffer buffer; 
    //... 
    @Override 
    public synchronized void afterConnectionEstablished(WebSocketSession session) 
          throws Exception { 
    this.session = session; 
    buffer.add(this); 
    } 

SystemStateWebSocketBuffer.java

@Component 
public class SystemStateWebSocketBuffer { 
    private List<SystemStateWS> systemStateSockets = new ArrayList<>(); 

    public void add(SystemStateWS systemStateWS) { 
    this.systemStateSockets.add(systemStateWS); 
    } 

    public List<SystemStateWS> getSystemStateSockets() { 
    return systemStateSockets; 
    } 

    public void setSystemStateSockets(List<SystemStateWS> systemStateSockets) { 
    this.systemStateSockets = systemStateSockets; 
    } 

} 

SystemStateRestInterceptor.java

@Aspect 
public class SystemStateRestInterceptor { 
    @Autowired 
    private SystemStateWebSocketBuffer systemStateWebSocketBuffer; 

    @Before("execution(* org.company.MyRestService.processTablesClearQuery(..))") 
    public void interceptConfigStartEvent() { 
    systemStateWebSocketBuffer.getSystemStateSockets() 
           .forEach(socket -> socket.onConfigurationStart()); 
    } 

}