0%

文章字数:440,阅读全文大约需要1分钟

maven的依赖添加并不是只添加一个依赖。添加的依赖包里如果也有依赖其他的包,则会一同被添加进来,形成依赖树。而有时这些被动添加进来的依赖并不是我们想要的版本,导致依赖冲突。

发现依赖冲突

  1. 程序运行中发生类未找到,方法未找到,版本不匹配等问题都可能是依赖冲突导致的。依赖了一个低版本的包,导致一些类和方法找不到。

  2. 可以通过maven提供的依赖树查看功能看目前依赖的是那个包,然后查看应该依赖的包,并进行对比。

解决依赖冲突

  1. 利用maven的最小依赖路径原则,在更近的路径,如本包下引入需要的依赖。

  2. 可以使用exclusions排除包中不需要的依赖

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>

文章字数:126,阅读全文大约需要1分钟

将依赖打包进jar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<build>
<!-- 指定JAVA源文件目录 -->
<sourceDirectory>main/src</sourceDirectory>
<!-- 配置资源文件-->
<resources>
<resource>
<directory>src</directory>
<includes>
<include>*/*.xml</include>
<include>*.properties</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>

<plugins>
<!-- 配置资源文件插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 配置编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 配置Jar打包插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<excludes>
<exclude>*/*.xml</exclude>
<exclude>*.properties</exclude>
<exclude>**/Test/*</exclude>
</excludes>
</configuration>
</plugin>
<!-- 配置Jar打包源文件插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>

<!--输出依赖的jar-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!--配置jar的入口main所在类-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>article.Article</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

文章字数:417,阅读全文大约需要1分钟

执行过程

mvn clean package依次执行了cleanresourcescompiletestResourcestestCompiletestjar(打包)等7个阶段。
mvn clean install依次执行了cleanresourcescompiletestResources、testCompile、test、jar(打包)、install等8个阶段。
mvn clean deploy依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install、deploy等9个阶段。

区别

package命令完成了项目编译、单元测试、打包功能,但没有把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库
install命令完成了项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库,但没有布署到远程maven私服仓库
deploy命令完成了项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库


文章字数:178,阅读全文大约需要1分钟

使用org.eclipse.paho.client.mqttv3包下的工具创建mqtt连接,并订阅和发布消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public static void main(String[] args) {

String topic = "defaultTopic";
String content = "Message from 123xxx";
int qos = 2;
String broker = "tcp://127.0.0.1:1883";

String clientId="123xxx";
String userName = "admin";
String pwd = "admin";
try {
/**
* 不传递persistence默认使用文件保存未发送的消息,传递null使用内存保存未发送的消息
*/
MqttClient mqttClient = new MqttClient(broker, clientId, null);
MqttConnectOptions connOpts = new MqttConnectOptions();

/**
* 再次连接时是否清除上次的session,即重新连接时是否不接收未收到的消息。只有重新连接时才能够更改此参数
*/
connOpts.setCleanSession(true);
connOpts.setUserName(userName);
connOpts.setPassword(pwd.toCharArray());
// 发起连接
mqttClient.connect(connOpts);

// 订阅
MqttCallback callback = new MessageProcess();
mqttClient.setCallback(callback);
mqttClient.subscribe(topic);
// 发布
// 1.创建需要发布的消息
MqttMessage message = new MqttMessage(content.getBytes());
message.setQos(qos);
// 2.循环模拟发布
while (true) {
TimeUnit.SECONDS.sleep(5);
mqttClient.publish(topic, message);
System.out.println("Message published");
}
} catch (MqttException me) {
System.out.println("reasonCode " + me.getReasonCode());
System.out.println("message " + me.getMessage());
System.out.println("localizedMessage " + me.getLocalizedMessage());
System.out.println("cause " + me.getCause());
System.out.println("exception " + me.getMessage());
me.printStackTrace();
} catch (Exception e) {
e.getMessage();
}
}

static class MessageProcess implements MqttCallback {
@Override
public void connectionLost(Throwable throwable) {
// 断线 手动重连
}

@Override
public void messageArrived(String topic, MqttMessage message) {
byte[] mess = message.getPayload();
System.out.println("topic[" + topic + "]:" + new String(mess));
}

@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
// 确认信息已经传递完毕调用
}
}

文章字数:77,阅读全文大约需要1分钟

输出固定字符串,保存字符串到文件内。

  • 输出字符串
    1
    SELECT substring_index('重庆,四川,北京','.', 1);
  • 保存结果到文件
    1
    2
    3
    4
    //语句
    SELECT * FROM (表名) INTO OUTFILE '/usr/local/test.txt' CHARACTER SET gbk;
    //命令行
    mysql -h 127.0.0.1 -u用户名 -p密码 --default-character-set=gb2312 -e "select * from 表名" 数据库名 > test.txt

文章字数:281,阅读全文大约需要1分钟

记录一次debug的过程。

现象

  1. 最初是发现mqtt只会接收一次订阅到的消息,然后就不会再接收消息了。
  2. 之后经过排查发现是publish方法被阻塞了,进去就出不来。

排查过程

  1. 最早的表现是使用mqttBox模拟设备向服务器发送请求,只发现服务器回应了一次。
  2. 因为是本地开发环境,所以直接打断点。发现不是回应出错,而是只接受到了一次订阅的信息。
  3. 再次跟踪程序,发现进入publish方法之后再也没有出来。
  4. 期初以为只是发送的方法被阻塞,后来意识到发布消息是在处理订阅的方法里以同步的形式进行的,而且还使用同一个连接。有可能是发送需要等待上个任务结束才能进行,然而上个任务需要发送结束了才能返回。于是陷入死循环。
  5. 接收到消息用新开的线程处理,问题解决。

文章字数:311,阅读全文大约需要1分钟

使用netty构建websocket服务器

开启服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class NettyServer {
private final int port;

public NettyServer(int port) {
this.port = port;
}

public void start() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();

EventLoopGroup group = new NioEventLoopGroup();
try {
ServerBootstrap sb = new ServerBootstrap();
sb.option(ChannelOption.SO_BACKLOG, 1024);
sb.group(group, bossGroup) // 绑定线程池
.channel(NioServerSocketChannel.class) // 指定使用的channel
.localAddress(this.port)// 绑定监听端口
.childHandler(new ChannelInitializer<SocketChannel>() { // 绑定客户端连接时候触发操作

@Override
protected void initChannel(SocketChannel ch) throws Exception {
System.out.println("收到新连接");
//websocket协议本身是基于http协议的,所以这边也要使用http解编码器
ch.pipeline().addLast(new HttpServerCodec());
//以块的方式来写的处理器
ch.pipeline().addLast(new ChunkedWriteHandler());
ch.pipeline().addLast(new HttpObjectAggregator(8192));
ch.pipeline().addLast(new MyWebSocketHandler());
ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws", "WebSocket", true, 65536 * 10));
}
});
ChannelFuture cf = sb.bind().sync(); // 服务器异步创建绑定
System.out.println(NettyServer.class + " 启动正在监听: " + cf.channel().localAddress());
cf.channel().closeFuture().sync(); // 关闭服务器通道
} finally {
group.shutdownGracefully().sync(); // 释放线程池资源
bossGroup.shutdownGracefully().sync();
}
}
}

通道处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class MyWebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("与客户端建立连接,通道开启!");

//添加到channelGroup通道组
MyChannelHandlerPool.channelGroup.add(ctx.channel());
}

@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("与客户端断开连接,通道关闭!");
//添加到channelGroup 通道组
MyChannelHandlerPool.channelGroup.remove(ctx.channel());
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//首次连接是FullHttpRequest,处理参数 by zhengkai.blog.csdn.net
if (null != msg && msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
String uri = request.uri();

Map paramMap=getUrlParams(uri);
System.out.println("接收到的参数是:"+JSON.toJSONString(paramMap));
//如果url包含参数,需要处理
if(uri.contains("?")){
String newUri=uri.substring(0,uri.indexOf("?"));
System.out.println(newUri);
request.setUri(newUri);
}

}else if(msg instanceof TextWebSocketFrame){
//正常的TEXT消息类型
TextWebSocketFrame frame=(TextWebSocketFrame)msg;
System.out.println("客户端收到服务器数据:" +frame.text());
sendAllMessage(frame.text());
}
super.channelRead(ctx, msg);
}

@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {

}

private void sendAllMessage(String message){
//收到信息后,群发给所有channel
MyChannelHandlerPool.channelGroup.writeAndFlush( new TextWebSocketFrame(message));
}

private static Map getUrlParams(String url){
Map<String,String> map = new HashMap<>();
url = url.replace("?",";");
if (!url.contains(";")){
return map;
}
if (url.split(";").length > 0){
String[] arr = url.split(";")[1].split("&");
for (String s : arr){
String key = s.split("=")[0];
String value = s.split("=")[1];
map.put(key,value);
}
return map;

}else{
return map;
}
}
}

使用

后端有做参数截取,url里可以直接放参数

1
socket = new WebSocket("ws://127.0.0.1:12345/ws?uid=666&gid=777");

文章字数:38,阅读全文大约需要1分钟

分类

  1. key-value类型:redis
  2. 列存储数据库: HBase
  3. 文档型数据库:MongoDB
  4. 图数据库:Neo4j(使用查询语言Cypher)

文章字数:94,阅读全文大约需要1分钟
表现:表单使用form.submit()提交为出发点onsubmit事件
原因:

  1. elemForm.submit()不会触发表单的onsubmit事件
  2. name="submit"的表单元素会覆盖默认的提交元素,而form.submit()本身就是对默认元素的引用
  3. onclick="xxx"xxx代表着触发事件之后的执行,如果是执行某个函数需要括号onclick="xxx()"

文章字数:308,阅读全文大约需要1分钟

HotSpot JVMjava的对象是用OOP-Klass模型来对应的。

OOP

Ordinary Object Pointer 普通对象指针,主要职能是标识对象的实例数据(存储在堆里)。
根据JVM内部使用的对象业务类型,具有多种oopDesc子类,比如instanceOopDesc表示类的实例,arrayOopDesc表示数组。
包含:

  1. Mark Word主要存储对象运行时记录信息,如hashcodeGC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;
  2. 元数据指针,_metadata成员,可以表示未压缩和压缩的Klass指针,指向Klass对象(存储元数据的类)
1
2
3
4
5
6
7
8
9
10
class oopDesc {
//....
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
//....
}

Klass

包含元数据方法信息,用来描述java类(存储在方法区)
每个已加载的java类都会创建一个instanceKlass对象,用来标识java内部类型的机制。
包含:

  1. ClassState: 描述了类加载的状态:分配、加载、连接、初始化
  2. instanceKlass: 声明接口、字段、方法数组、常量池、源文件名等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class instanceKlass: public Klass {
friend class VMStructs;
public:

enum ClassState {
unparsable_by_gc = 0, // object is not yet parsable by gc. Value of _init_state at object allocation.
allocated, // allocated (but not yet linked)
loaded, // loaded and inserted in class hierarchy (but not linked yet)
linked, // successfully linked/verified (but not initialized yet)
being_initialized, // currently running class initializer
fully_initialized, // initialized (successfull final state)
initialization_error // error happened during initialization
};

//部分内容省略
protected:
// Method array. 方法数组
objArrayOop _methods;
// Interface (klassOops) this class declares locally to implement.
objArrayOop _local_interfaces; //该类声明要实现的接口.
// Instance and static variable information
typeArrayOop _fields;
// Constant pool for this class.
constantPoolOop _constants; //常量池
// Class loader used to load this class, NULL if VM loader used.
oop _class_loader; //类加载器
typeArrayOop _inner_classes; //内部类
Symbol* _source_file_name; //源文件名
}