关于Netty
初次接触Netty,了解到了Netty的组成;
在使用Netty之前需要首先通过Bootstrap来进行初始化,以及对NioEventLoopGroup的出初始化;
Netty分服务端(Server)和客户端(Client),服务端无法直接找到某一个客户端并与之建立连接,只有当客户端主动给服务端发送数据之后,Server-Client的链接才算是建立;
Server与Client如果一段时间没有信息交互则会自动断开连接;
在Netty中如果要对接收到的数据进行处理的话可以在Bootstrap.handler中添加响应的ChannelInitializer,并在initChannel方法中在对SocketChannel.pipeline().addLast()添加相应的继承于ChannelInboundHandlerAdapter的类;
在自己定义的ChannelInboundHandlerAdapter类中,可以对接收到的数据做出处理;
Netty读取数据的模型大概就是从串口读到的信息会被塞到管道中,在管道上我们可以在某些位置加上自定义的类似阀门的装置(类似的装置)可以对数据进行解析或者截留;
ChannelHandlerContext.fireChannelRead()和与之类似的ChannelHandlerContext.fireChannelXXXX()方法也证明了这一点;
在通过Bootstrap初始化Netty时,可以添加自定义的解码,用于自定义的解码处理;
在数据发送时必须要获取到ChannelFuture对象,并通过ChannelFuture对象再获取到Channel对象,最后通过Channel对象将数据发出;
数据即使是byte[]也是无法成功发送的,必须要使用Unpooled类下的方法将其包装才可发出;
值的注意的是,数据在传输的过程是无法预测会发生什么事情的,所以在接收到数据时可能会发生粘包情况,这时就需要我们去将黏在一起的包拆开。
关于协议
协议是一种编码规则,将一组byte数组的某些特定的位置赋予特定的含义;
在协议解析和制作的时候通常会采取位运算的方式进行编码;
一个协议都会提供一定的预留位以供扩展,也必须要提供校验位,以保证收发时的验证;
关于MyBatis
在MyBatis自动生成的Interface和Mapper.xml中写入自定义的方法是不安全的,因为如果与之对应的实体类发生了变动需要重新生成的话,那么之前写在其中的方法也会一同被覆盖;
自定义的sql一定不要写在生成的Interface和Mapper.xml中;
自定义的Mapper.xml一定要遵从某种命名格式,而格式的设定一般是这样的
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
上代码的意思就是Mapper.xml文件就是mapper下的以Mapper.xml结尾的文件;
自定义的Mapper.xml中会有一个namespace值,而namespace的取值就是与之对应的Interface类;
关于SpringBoot
对于SpringBoot中使用除@Autowired注解自动注入并获取Bean的方法是,在Application类的main方法中获取ConfigurableApplicationContext对象,通过ConfigurableApplicationContext对象可以通过Class或者Name等方式获取到自动被注入到框架中的Bean;
如果当使用@Component等注解注入Class到框架中时,可以使用@Conditional注解来提供判断是否当前的条件合适,而Conditional注解的取值就是做出判断的类;
当一个类实现了Condition接口之后,那么这个类就可以当做@Conditional注解的取值了
,当然这个类仅仅实现Condition接口即可不需要自动注入;
读取application.properties配置文件有两种方法,一种是通过@Configuration注解和@ConfigurationProperties(prefix = “xxxx”),而 @ConfigurationProperties的取值prefix则是对应配置文件中的Key值;
第二种方法就是:
{
Environment environment = SpringContextUtil.getBean(Environment.class);
String xxx =environment.getProperty("xxxx");
}
这种方法可以写在代码块中,这样解决在某个字段使用@Value注解注入值后,此字节又被代码块调用时字段值为空的问题,之所以@Value获取不到指定的值是因为类加载顺序问题;
在application.properties中的Key必须都是小写字母不然会报错;
关于Redis
第一次接触Redis,Redis就是Key-Value类型的数据库,数据写入Redis数据库时,数据也就直接被写入了内存中,这样数据出来速度更快而且也确保了断电时对内存中数据的保存;
关于Java
static关键词修饰的对象就是静态对象,也就是一直存在于内存中的对象,也就是说被static修饰过的对象可以充当某种形式的缓存;
活用枚举类(enum)可以适当的减少在编码时可能出现的潜在错误而且也极大的美化了代码;
活用反射机制可以在减少代码量的同时也可提高代码的适用度,实例代码如下:
@Override
public boolean equals(Object obj) {
if (obj == null){
return false;
}
//自己判断自己
if (this == obj){
return true;
}
//判断类型
if (obj instanceof AnyType){
AnyType other = (AnyType)obj;
Method[] methods = AnyType.class.getMethods();
for (Method cursor : methods) {
//只获取get方法
if (!cursor.getName().contains("get")
||cursor.getName().contains("set")){
continue;
}
//对get方法获取的结果进行比较
try {
Method otherMothod = Frame.class.getMethod(cursor.getName());
//如果双方该字段都为null则一定相同
if (cursor.invoke(this) == null
&& otherMothod.invoke(other) == null){
continue;
}
//如果双方该字段一个有一个没有则一定不同
if (cursor.invoke(this) == null
&& otherMothod.invoke(other) != null){
return false;
}
if (cursor.invoke(this) != null
&& otherMothod.invoke(other) == null){
return false;
}
//如果该字段双方均有则需要判断
if (cursor.getReturnType().getCanonicalName().contains("[]")){
//如果是数组
byte[] myData = (byte[]) cursor.invoke(this);
byte[] otherData = (byte[]) cursor.invoke(other);
if (!Arrays.equals(myData, otherData)){
return false;
}
}else if (!cursor.invoke(this).equals(otherMothod.invoke(other))){
//如果不是集合
return false;
}
} catch (Exception e) {
System.out.println("error method:" + cursor.getName());
e.printStackTrace();
return false;
}
}
}else {
return false;
}
return true;
}
上述代码是重写的某个类的equals方法,目的是获取并比较该类的所有字段是否完全相同,如果是使用最笨的方法就是手动调用getter,也就是显式的写出来,这样做是很简单但是无法做到扩展性而且一旦字段非常非常多挨个get是个很麻烦的事情,所以此时使用反射机制来获取并调用方法就显得十分的简洁而且通用性高;
使用这用方式需要对Class类有一定的理解;
关于设计
在本项目中主要使用了一个策略模式一个工厂模式;
策略模式需要有一个策略父类,一般是个抽象类,以便则各处调用一个祖先类;
其次,对于某个策略的具体实现,则是继承于这个抽象类的子类做的;
在每一个具体的策略中要实现的就是对某一个业务特殊的处理方式;
而在使用时是需要声明一个抽象策略的引用,并实例化一个具体策略即可;
这样做大大的降低了耦合度,配合其他的设计模式会极大的提升代码的可扩展性;
对于工厂模式,主要的是要有一个工厂(车间,生产产品的地方),而这个工厂的产品则是一个实例对象;
工厂模式的意义在于可以更加简洁的生产出具有某些特定值的对象或者是某些特定的对象,对象的产生以及对某些特定字段赋特定值的操作全都在工厂的生产线中进行,这样做防止了同一类对象(实现某种特定业务需要的)在n地方新建可能会产生不同对象的错误,而且通过Factory的方式,在未来如果某个特定的业务发生变动时不需要到处改代码而仅仅修改一条生产线即可。
关于常量的设置,常量一般是某一特定的值,这个值一般会影响到整个业务流程,可以这个值发生变动的可能性还比较大;
常量一是方便修改,二是减少了神秘数字或是神秘字符串的产生;