项目结构如图:
logic模块, 该模块负责具体的业务逻辑(分为service层, dao层)
beans.xml 定义在这里
server模块, 负责具体的客户端的接入(用的netty)
这里的main方法负责启动spring
现在出现一个诡异的地方, server中的一个类ConnectHandler, 该类中的成员对象UserLoginService上@AutoWire, 都没有成功, 也不报错, 只是运行的时候会有空指针异常
其次,我在server模块中的main方法中, 通过spring的getBean方法, 得到 ConnectHandler, 成功, 调用getUserLoginService(), 成功, 为什么这里都不为空,
综上, 基本可以排除, 什么忘记注解, 没有扫描到server模块啊,之类的错误
以下是beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
">
<context:component-scan base-package="com.haoyin.*"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql:///localtest" />
<property name="user" value="root" />
<property name="password" value="" />
<property name="initialPoolSize" value="3" />
<property name="minPoolSize" value="3" />
<property name="maxPoolSize" value="10" />
<property name="maxIdleTime" value="5" />
</bean>
<!-- myBatis 配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath*:com/haoyin/logic/dao/*.xml"/>
</bean>
<!-- mybatis自动实现dao-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.haoyin.logic.dao"/>
</bean>
<!-- 配置sqlSessionTemplate
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
-->
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
</beans>
以下是ConnectHandler类:
package com.haoyin.server.handler;
import com.haoyin.logic.service.UserLoginService;
import com.haoyin.server.exception.connect.ConnectMessageException;
import com.haoyin.server.exception.connect.ConnectionAlreadyExistException;
import com.haoyin.server.exception.connect.MessageFormatException;
import com.haoyin.server.exception.connect.UserPermissionException;
import com.haoyin.server.state.Connection;
import com.haoyin.server.state.ConnectionManager;
import com.haoyin.server.state.UserConfig;
import com.haoyin.server.utils.StringUtils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.mqtt.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.net.InetSocketAddress;
/**
* Created by dong on 2016/1/12.
*/
@Service("connectHandler")
public class ConnectHandler extends SimpleChannelInboundHandler<MqttMessage> {
private Logger logger = LoggerFactory.getLogger(ConnectHandler.class);
@Autowired
private TestHandler handler;
@Autowired
private UserLoginService userLoginService;
private ChannelHandlerContext ctx;
public void setUserLoginService(UserLoginService userLoginService) {
this.userLoginService = userLoginService;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
this.ctx = ctx;
System.out.println("active");
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, MqttMessage msg) throws Exception {
if (msg.fixedHeader().messageType() == MqttMessageType.CONNECT) {
doConnect(ctx, (MqttConnectMessage) msg);
sendResponse(buildSuccessAck());
} else if (msg.fixedHeader().messageType() == MqttMessageType.DISCONNECT) {
} else {
//想绕过建立连接
ctx.fireChannelRead(msg);
}
}
/**
*
*
* @throws ConnectionAlreadyExistException
* @throws UserPermissionException
* @throws MessageFormatException
*
* */
private void doConnect(ChannelHandlerContext ctx, MqttConnectMessage connectMessage) {
checkMessageFormat(connectMessage);
MqttConnectVariableHeader variableHeader = connectMessage.variableHeader();
MqttConnectPayload payload = connectMessage.payload();
//1. 验证用户合法
String uid = payload.userName(); //约定, username为uid
String token = payload.password(); //约定, password为token
checkUserAuth(uid, token);
String clientId = payload.clientIdentifier();
checkConnectionExist(clientId); // 2. 确保用户唯一
UserConfig userConfig = extractUserConfig(variableHeader, payload);
buildAndInitConnection(userConfig);
}
private void checkMessageFormat(MqttConnectMessage connectMessage)
throws MessageFormatException {
MqttConnectPayload payload = connectMessage.payload();
String clientId = payload.clientIdentifier();
if (!StringUtils.checkLength(clientId, 1, 23)){
throw new MessageFormatException(
"clientId长度不合法!",
MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED);
}
}
private void checkUserAuth(String uid, String token) {
boolean ok = userLoginService.authenticate(uid, token);
if (ok) return;
throw new UserPermissionException("用户验证不通过",
MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD);
}
private void checkConnectionExist(String clientId) {
//约定clientId为userId
}
/**
* 保存用户配置, 是否需要遗嘱, clean Session
* 这些配置在Connect报文的 可选头部和载荷中
*
* */
private UserConfig extractUserConfig(MqttConnectVariableHeader variableHeader,
MqttConnectPayload payload){
String uid = payload.userName();
boolean cleanSession = variableHeader.isCleanSession();
int keepAliveTime = variableHeader.keepAliveTimeSeconds();
UserConfig profile = new UserConfig(uid, cleanSession, keepAliveTime);
return profile;
}
private void buildAndInitConnection(UserConfig userConfig) {
Connection connection = ConnectionManager.INSTANCE.create(ctx);
connection.setUserConfig(userConfig);
}
private MqttConnAckMessage buildSuccessAck(){
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.CONNACK, false,
MqttQoS.EXACTLY_ONCE, false, 0);
MqttConnAckVariableHeader variableHeader = new MqttConnAckVariableHeader(
MqttConnectReturnCode.CONNECTION_ACCEPTED, false);
MqttConnAckMessage ack = new MqttConnAckMessage(fixedHeader, variableHeader);
return ack;
}
private String getClientIP(){
InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
String clientIP = address.getAddress().getHostAddress();
return clientIP;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause instanceof MessageFormatException) {
handleProtocolFormatError((MessageFormatException) cause);
} else if (cause instanceof ConnectionAlreadyExistException) {
handleConnectionAlreadyExist((ConnectionAlreadyExistException) cause);
} else if (cause instanceof UserPermissionException) {
handleUserPermission((UserPermissionException) cause);
} else {
ctx.fireExceptionCaught(cause);
}
}
private void handleProtocolFormatError(MessageFormatException e) {
if (logger.isDebugEnabled()) {
logger.info("ProtocolFormatError");
}
sendResponse(buildErrorAck(e));
}
private void handleConnectionAlreadyExist(ConnectionAlreadyExistException e) {
if (logger.isDebugEnabled()) {
logger.info("ConnectionAlreadyExist");
}
sendResponse(buildErrorAck(e));
}
private void handleUserPermission(UserPermissionException e) {
if (logger.isDebugEnabled()) {
logger.info("UserPermission");
}
sendResponse(buildErrorAck(e));
}
private MqttConnAckMessage buildErrorAck(ConnectMessageException e){
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.CONNACK, false,
MqttQoS.EXACTLY_ONCE, false, 0);
MqttConnAckVariableHeader variableHeader = new MqttConnAckVariableHeader(
e.getMqttConnectReturnCode(), false);
MqttConnAckMessage ack = new MqttConnAckMessage(fixedHeader, variableHeader);
return ack;
}
private void sendResponse(MqttMessage mqttMessage) {
ctx.writeAndFlush(mqttMessage);
}
}
请各位帮忙看看, 困在这里一天了..
确定空指针是由UserLoginService造成的?