问题描述
背景:项目是使用netty进行通信的,并没有使用springboot功能,启动的时候是主方法里面有生成线程,通过new创建channel监听端口。
public class RunServer {
public static void main(String[] args) {
Runnable task1 = () -> {
TcpServer server = new TcpServer();
try {
server.start(new InetSocketAddress(ServerConfig.TCP_PORT));
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("启动TCP服务器失败");
}
};
ExecutorService fixedThreadPool = new ThreadPoolExecutor(2, 2,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>(2));
fixedThreadPool.execute(task1);
}
现在开始将项目重构成为一个springboot项目,同时集成邮件和redis功能。
这里是部分的代码
public class ServerHandler extends SimpleChannelInboundHandler {
private final StringBuffer buffer = new StringBuffer(1000);
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private EmailSend emailSend;
@Autowired
private DataForIOTConstant dataForIOTConstant;
结果并没有实现功能。
问题分析
这里我们要说一下传统使用netty和springboot集成netty的差别。在传统使用netty的时候我们会这样初始化bootstrap
serverBootstrap
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelServerHandler());
注意这里的childHandler(new ChannelServerHandler())
,是通过new得到了对象,而我们springboot是自动注入使用的是@Autowired
。
我们回忆一下springboot的启动,他说通过扫描bean,通过autoconfiguration将配置的properties注入到bean实例中,同时将这个bean托管给IOC容器。
当我们需要某一个bean的时候通过@Autowired
注入。
目前的代码:
- 没有托管给springboot进行扫描
- 没有实例化成为bean交给IOC进行管理
问题解决
在类的上面加上@Component
,同时所有的对象通过autowired注入,不能使用new。
小结:
在使用springboot的时候,只有扫描到的bean才会被初始化,只有被初始化的bean才能被加入IOC容器,只有被加入IOC的bean才能使用autowired
来获得.
在部署的时候又出问题了
问题描述:
首先项目在ide(我是用的是idea)跑项目没有问题,但是部署成jar的时候确保空指针。
找到空指针的对象:
/*
这里的想法是通过ConfigurationProperties将properties的属性注入进来,在所有的bean初始化之后将属性赋值给静态变量,从而优雅的调用。
*/
@Component
@Data
@ConfigurationProperties(prefix = "iot.constant")
public class DataForIOTConstant implements InitializingBean {
private String url;
private String dataAddress;
private String apiKey;
private Integer tcpPost;
private String URL_CONSTANT;
private String DATA_ADDRESS_CONSTANT;
private String API_KEY_CONSTANT;
private Integer TCP_POST_CONSTANT;
@Override
public void afterPropertiesSet() throws Exception {
URL_CONSTANT = url;
DATA_ADDRESS_CONSTANT = dataAddress;
API_KEY_CONSTANT = apiKey;
TCP_POST_CONSTANT = tcpPost;
System.out.println(URL_CONSTANT);
System.out.println(DATA_ADDRESS_CONSTANT);
System.out.println(API_KEY_CONSTANT);
System.out.println(TCP_POST_CONSTANT);
}
问题分析:
首先我们说说这个方法,在网络编程中,这个方法是可以用的,通过ConfigurationProperties
,可以将properties的属性放入属性中,由于static不能通过注入的方式获得值,所以只能在某一个节点进行赋值。这个要求我们属性已经过来了。所以我们用了InitializingBean
,在bean初始化之后再进行赋值。
对于一般的网络编程,这样倒是无所谓,但是本项目并不是一个javaweb项目。而是一个启动以后监听某一个端口,等待数据过来进行解析,这个使用的是netty。
这样就意味着项目在启动的要注意启动顺序,使用InitializingBean
虽然可以优雅的调用,不过对于这种在启动的时候就要开始监听端口,就需要属性值的方法,这种方式并不合适。
// DataForIOTConstant.TCP_POST_CONSTANT 优雅的调用配置对象
channelFuture = serverBootstrap.bind(DataForIOTConstant.TCP_POST_CONSTANT).sync();
Log.get().info("绑定了端口{},开始进行监听",dataForIOTConstant.getTcpPost());
问题解决
放弃使用InitializingBean
,直接通过get对属性值调用即可。
小结:
这里是idea和jar的启动不同导致了开发时候没有问题,而部署的时候出现空指针。
即idea是先启动初始化所有的bean实例,在开始调用方法。(这样并不会出现属性为空的情况)。
jar包启动,bean的初始化时放在了最后的。
后记:小编在写总结的时候模拟bug却没有再现。而且本来空指针的error也没出现过。。。如果有遇到一样问题的朋友们我这个可以作为一个选项,如果有大佬知道其中的原因,可以指导一下。