GeXiangDong

精通Java、SQL、Spring的拼写,擅长Linux、Windows的开关机

0%

用maven的shade插件打包javaFX项目

javaFX是java在2007年首次公布,可用于替代Java Swing、AWT 等图形界面的客户端UI框架。

java项目一般会被打包成一个jar,然后和依赖的jar一起发布,运行时要在classpath里指定出来各个依赖的jar。我们开发项目时一般用很多现成的轮子,依赖的jar很多。

maven的shade插件可以把依赖的jar一起打包到一个大jar中(这个jar体积会比较大),之后仅拷贝并运行一个jar就可以了,为使用提供了很多方便。

使用 shade 插件

在pom.xml中添加

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
<plugins>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<!-- 下面这个改成运行jar包时需要执行的java类 -->
<mainClass>cn.devmgr.client.NewMain</mainClass>
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>


createDependencyReducedPom 是用来告诉shade不要生成dependency-reduced-pom.xml文件的,配置成true(默认)每次打包时会自动在pom.xml同级目录生成这个文件,如果配置成true,建议把此文件假如.gitignore。

mainClass用来指定启动类,特别注意这个启动类,不能是javaFX的启动类,要另写一个。

创建一个仅供shade插件用的启动类

不能把javaFX的启动类直接配置给shade插件,否则打包后的文件会运行不起来。出现类似 Error: JavaFX runtime components are missing, and are required to run this application 这样的错误提示。要解决它,需要做一个新的,不使用javaFX的启动类

1
2
3
4
5
6
7
8
9
package cn.devmgr.client;

public class NewMain {

public static void main(String[] args) {
MainApp.main(args); //MainApp是javaFX的启动类,直接调用它即可
}
}

这是由于javaFX启动机制和打包成一个大jar后对分jar运行的改变造成的,详细可参考

  1. https://stackoverflow.com/questions/52653836/maven-shade-javafx-runtime-components-are-missing
  2. https://stackoverflow.com/questions/52569724/javafx-11-create-a-jar-file-with-gradle/52571719#52571719

pom.xml 里的javaFX依赖

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
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>15.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>15.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>15.0.1</version>
</dependency>

<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>15.0.1</version>
<classifier>mac</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>15.0.1</version>
<classifier>win</classifier>
</dependency>

这里只列出来了我使用到的几个,其他(例如webview)如果项目中使用,需要再增加依赖。
特别注意webview和javafx-graphics一样,分平台不同包的,而且体积很大。

IntelliJ IDEA 里直接运行时遇到的错误

在idea里直接运行javaFX启动类,如果遇到以下错误

1
2
3
4
5
6
7
8
Graphics Device initialization failed for :  es2, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:280)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:244)
at javafx.graphics/com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:261)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)

而且命令行运行没问题,这很可能是idea使用了不是自己平台的javafx-graphics包导致的。
例如我在mac idea里,如果maven pom.xml的两个javafx-graphics win在前,mac在后就会出现这个异常,只需要把自己平台的调整到前面,reimport就好。

IntelliJ IDEA 里对module-java.java的错误提示及原因

idea 里会提示module-java.java中有一个错误

Module ‘xxx’ reads package ‘javafx.animation’ from both ‘javafx.graphics’ and ‘javafx.graphics’

这个提示不影响运行,出现这个提示的原因是 javafx.graphics 在依赖中出现了2次。
此处出现两次是因为pom.xml的确写了2个javafx-graphics的依赖(mac和win两个)。如果不需要跨平台,pom里只写一个,也就不会出现这个提示了。

一个完整的 pom.xml

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>cn.devmgr.client</groupId>
<artifactId>client-tool</artifactId>
<version>1.0-SNAPSHOT</version>


<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<javafx.maven.plugin.version>0.0.6</javafx.maven.plugin.version>
</properties>

<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>15.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>15.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>15.0.1</version>
</dependency>

<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>15.0.1</version>
<classifier>mac</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>15.0.1</version>
<classifier>win</classifier>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.31</version>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>

<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>


<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.12</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.36.0.1</version>
</dependency>


</dependencies>


<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<executions>
<execution>
<id>default-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</execution>
<execution>
<id>default-testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</execution>
</executions>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>

<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>${javafx.maven.plugin.version}</version>
<configuration>
<mainClass>cn.devmgr.client.MainApp</mainClass>
</configuration>
</plugin>


<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<!-- <createDependencyReducedPom>false</createDependencyReducedPom>-->
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<!-- 下面这个改成运行jar包时需要执行的java类 -->
<mainClass>cn.devmgr.client.NewMain</mainClass>
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>
</build>
</project>

maven 命令行

运行javaFX项目

1
mvn javafx:run

打包(和其他maven项目相同)

和其他maven项目完全相同,只是打包完毕target目录下的jar包很大。 fat jar

1
mvn package

Spring Gateway 遇到异常 org.springframework.core.io.buffer.DataBufferLimitException:
Exceeded limit on max bytes to buffer : 262144
堆栈如下【似乎没啥意义】

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
org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144
at org.springframework.core.io.buffer.LimitedDataBufferList.raiseLimitException(LimitedDataBufferList.java:101)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Body from GET http://192.168.0.141:8042/companies/62bd6601-7139-4182-aa11-03c7cf304326/orders/summary/cashierjournal?withDetails=true&beginDate=1580572800000&endDate=1617119999000 [DefaultClientResponse]
|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP GET "/web/companies/62bd6601-7139-4182-aa11-03c7cf304326/journal/cashier?withDetails=true&beginDate=1580572800000&endDate=1617119999000" [ExceptionHandlingWebHandler]
Stack trace:
at org.springframework.core.io.buffer.LimitedDataBufferList.raiseLimitException(LimitedDataBufferList.java:101)
at org.springframework.core.io.buffer.LimitedDataBufferList.updateCount(LimitedDataBufferList.java:94)
at org.springframework.core.io.buffer.LimitedDataBufferList.add(LimitedDataBufferList.java:59)
at reactor.core.publisher.MonoCollect$CollectSubscriber.onNext(MonoCollect.java:119)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114)
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192)
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114)
at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:213)
at reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:346)
at reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:348)
at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:572)
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:93)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)

这个异常的原因很明确,返回内容超过了默认能解析的256k的大小,怎么增加这个buffer?

网上搜索大多是说可以在 application.yml 配置

1
2
3
spring:
codec:
max-in-memory-size: 5MB

按照这个修改后,不起作用。网上还有说是spring版本问题…

这个是我自己的GatewayFilter问题,访问到了自定义的一个Filter, 这个filter创建的请求使用了默认值,没有用 application.yml中的配置

1
2
3
WebClient.builder()
.build()
// ...

改成如下:

1
2
3
4
5
6
7
WebClient.builder()
.exchangeStrategies(
ExchangeStrategies.builder()
.codecs(c -> c.defaultCodecs().maxInMemorySize(10 * 1024 * 1024)) // 10M json解析
.build())
.build()
// ...

这里写死了10M,也可以优雅一点,从application.yml中读取配置值。

现象

错误日志

org.elasticsearch.cluster.block.ClusterBlockException: blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];
    at org.elasticsearch.cluster.block.ClusterBlocks.indexBlockedException(ClusterBlocks.java:183)
    at org.elasticsearch.action.support.replication.TransportReplicationAction.blockExceptions(TransportReplicationAction.java:255)
    at org.elasticsearch.action.support.replication.TransportReplicationAction.access$500(TransportReplicationAction.java:100)
    at org.elasticsearch.action.support.replication.TransportReplicationAction$ReroutePhase.doRun(TransportReplicationAction.java:780)
    at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37)
    at org.elasticsearch.action.support.replication.TransportReplicationAction.doExecute(TransportReplicationAction.java:172)
    at org.elasticsearch.action.support.replication.TransportReplicationAction.doExecute(TransportReplicationAction.java:100)
    at org.elasticsearch.action.support.TransportAction$RequestFilterChain.proceed(TransportAction.java:167)
    at org.elasticsearch.xpack.security.action.filter.SecurityActionFilter.apply(SecurityActionFilter.java:124)
    at org.elasticsearch.action.support.TransportAction$RequestFilterChain.proceed(TransportAction.java:165)
    at org.elasticsearch.action.support.TransportAction.execute(TransportAction.java:139)
    at org.elasticsearch.action.support.TransportAction.execute(TransportAction.java:81)
    at org.elasticsearch.action.bulk.TransportBulkAction$BulkOperation.doRun(TransportBulkAction.java:420)
    at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37)
    at org.elasticsearch.action.bulk.TransportBulkAction.executeBulk(TransportBulkAction.java:533)
    at org.elasticsearch.action.bulk.TransportBulkAction.executeIngestAndBulk(TransportBulkAction.java:271)
    at org.elasticsearch.action.bulk.TransportBulkAction.doExecute(TransportBulkAction.java:188)
    at org.elasticsearch.action.bulk.TransportBulkAction.doExecute(TransportBulkAction.java:90)
    at org.elasticsearch.action.support.TransportAction$RequestFilterChain.proceed(TransportAction.java:167)
    at org.elasticsearch.xpack.security.action.filter.SecurityActionFilter.apply(SecurityActionFilter.java:124)
    at org.elasticsearch.action.support.TransportAction$RequestFilterChain.proceed(TransportAction.java:165)
    at org.elasticsearch.action.support.TransportAction.execute(TransportAction.java:139)
    at org.elasticsearch.action.bulk.TransportSingleItemBulkWriteAction.doExecute(TransportSingleItemBulkWriteAction.java:69)
    at org.elasticsearch.action.bulk.TransportSingleItemBulkWriteAction.doExecute(TransportSingleItemBulkWriteAction.java:44)
    at org.elasticsearch.action.support.TransportAction$RequestFilterChain.proceed(TransportAction.java:167)
    at org.elasticsearch.xpack.security.action.filter.SecurityActionFilter.apply(SecurityActionFilter.java:124)
    at org.elasticsearch.action.support.TransportAction$RequestFilterChain.proceed(TransportAction.java:165)
    at org.elasticsearch.action.support.TransportAction.execute(TransportAction.java:139)
    at org.elasticsearch.action.support.replication.TransportReplicationAction$OperationTransportHandler.messageReceived(TransportReplicationAction.java:284)
    at org.elasticsearch.action.support.replication.TransportReplicationAction$OperationTransportHandler.messageReceived(TransportReplicationAction.java:276)
    at org.elasticsearch.xpack.security.transport.SecurityServerTransportInterceptor$ProfileSecuredRequestHandler$1.doRun(SecurityServerTransportInterceptor.java:250)
    at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37)
    at org.elasticsearch.xpack.security.transport.SecurityServerTransportInterceptor$ProfileSecuredRequestHandler.messageReceived(SecurityServerTransportInterceptor.java:308)
    at org.elasticsearch.transport.RequestHandlerRegistry.processMessageReceived(RequestHandlerRegistry.java:66)
    at org.elasticsearch.transport.TcpTransport$RequestHandler.doRun(TcpTransport.java:1289)
    at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37)
    at org.elasticsearch.common.util.concurrent.EsExecutors$1.execute(EsExecutors.java:140)
    at org.elasticsearch.transport.TcpTransport.handleRequest(TcpTransport.java:1247)
    at org.elasticsearch.transport.TcpTransport.messageReceived(TcpTransport.java:1111)
    at org.elasticsearch.transport.TcpTransport.inboundMessage(TcpTransport.java:914)
    at org.elasticsearch.transport.netty4.Netty4MessageChannelHandler.channelRead(Netty4MessageChannelHandler.java:53)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:323)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:297)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:241)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:656)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:556)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:510)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:470)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:909)
    at java.lang.Thread.run(Thread.java:834)

原因

日志里已经很明显了,elasticsearch 进入了只读模式,不能执行删除操作。

Elasticsearch进入只读模式是因为磁盘空间不足造成的,磁盘可用空间过低后(默认低于5%),elasticsearch会进入只读模式,即使增加了磁盘可用空间也不会自动恢复,需要通过如下 curl 命令恢复。

1
curl -XPUT -H "Content-Type: application/json" http://localhost:9200/_all/_settings -d '{"index.blocks.read_only_allow_delete": null}'

Reference

https://stackoverflow.com/questions/50609417/elasticsearch-error-cluster-block-exception-forbidden-12-index-read-only-all

现象

java程序调用https url的服务时,会出错,错误堆栈如下。 不论是使用spring resttemplate,还是自己用jdk里的httpsurlconnection 写段程序访问,都出现这个问题,而这个url在浏览器里调用是好的。

java.lang.RuntimeException: javax.net.ssl.SSLHandshakeException: Could not generate secret

    at cn.devmgr.mall.mms.sync.CommonService.login(CommonService.java:64)
    at cn.devmgr.mall.mms.test.AppTests.testSyncClerks(AppTests.java:40)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1510)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1510)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Caused by: javax.net.ssl.SSLHandshakeException: Could not generate secret
    at java.base/sun.security.ssl.KAKeyDerivation.t12DeriveKey(KAKeyDerivation.java:91)
    at java.base/sun.security.ssl.KAKeyDerivation.deriveKey(KAKeyDerivation.java:61)
    at java.base/sun.security.ssl.ECDHClientKeyExchange$ECDHEClientKeyExchangeProducer.produce(ECDHClientKeyExchange.java:419)
    at java.base/sun.security.ssl.ClientKeyExchange$ClientKeyExchangeProducer.produce(ClientKeyExchange.java:65)
    at java.base/sun.security.ssl.SSLHandshake.produce(SSLHandshake.java:440)
    at java.base/sun.security.ssl.ServerHelloDone$ServerHelloDoneConsumer.consume(ServerHelloDone.java:182)
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:445)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:423)
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:182)
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:167)
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1462)
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1370)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:437)
    at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
    at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:171)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1370)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1345)
    at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:220)
    at cn.devmgr.mall.mms.sync.CommonService.login(CommonService.java:51)
    ... 64 more
Caused by: java.security.InvalidKeyException: cannot identify XDH private key
    at org.bouncycastle.jcajce.provider.asymmetric.edec.KeyAgreementSpi.engineDoPhase(Unknown Source)
    at java.base/javax.crypto.KeyAgreement.doPhase(KeyAgreement.java:579)
    at java.base/sun.security.ssl.KAKeyDerivation.t12DeriveKey(KAKeyDerivation.java:75)
    ... 83 more

排查

单独用 HttpUrlConnection 来写main方法访问url,脱离项目环境,单独运行Java main方法,发现是好的。
因此可以排除是本地java环境和服务器证书问题,断定是堆栈中对应的org.bouncycastle导致的。

通过 mvn -X test 运行项目,找到org.boundcycastle,找到使用它的上级jar,在pom里 exclusion 掉,再次运行,解决。

解决

maven pom 中排除掉 org.bouncycastle:bcprov-jdk15on:jar:1.65

如果你的项目需要这个包,升级到最新版。

有2个java的库可以生成jwt,maven依赖分别如下

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>

<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>

有在一个项目中用jjwt使用HS512方式做签名的token,使用同一个字符串作为key,但是在java-jwt中无法验证,出现错误:

Exception in thread “main” com.auth0.jwt.exceptions.SignatureVerificationException: The Token’s Signature resulted invalid when verified using the Algorithm: HmacSHA512

出错的代码

1
2
3
4
5
6
Jwts.builder()
.setClaims(map)
.setExpiration(new Date(t))
.signWith(SignatureAlgorithm.HS512, "helloworld")
.compact();

1
2
3
4
Algorithm algorithm = Algorithm.HMAC512("helloworld");
JWTVerifier verifier =
JWT.require(algorithm).acceptExpiresAt(5).build(); // Reusable verifier instance
DecodedJWT jwt = verifier.verify(t2);

原因

仔细查看代码后发现jjwt的签名参数传递错误导致,signWith的第二个参数时字符串,但是是需要的做base64后的字符串,在jjwt的源码中对传递进来的字符串参数做了base64 decode

参照: https://github.com/jwtk/jjwt/blob/master/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java

1
2
3
4
5
6
7
@Override
public JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey) throws InvalidKeyException {
Assert.hasText(base64EncodedSecretKey, "base64-encoded secret key cannot be null or empty.");
Assert.isTrue(alg.isHmac(), "Base64-encoded key bytes may only be specified for HMAC signatures. If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.");
byte[] bytes = Decoders.BASE64.decode(base64EncodedSecretKey);
return signWith(alg, bytes);
}

解决方案

知道原因了就很容易解决了,最简单的方式两边加密解密的都用成 byte[] 做key,不会有base64 啥事。

或者jjwt加密时先把明文密码字符串做base64.Encode

1
2
3
4
5
6
String t2 =
Jwts.builder()
.setClaims(buyerMap)
.setExpiration(new Date(t))
.signWith(SignatureAlgorithm.HS512, Base64.encode("helloworld".getBytes()))
.compact();

在使用Springboot 时遇到错误,服务端日志如下:

1
WARN  o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported]

RestController里

1
2
3
4
@RequestMapping(
value = "/xxxxxs",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)

看上去这个错误原因很简单,客户端传递的 Content-Type 没有在服务器端支持。

检查发现,客户端和服务器端都是正确,而且同一个controller里有的POST是好的,不出问题,有的出问题。

网上搜到的办法也都试过,pom中依赖也改过….

后来把日志级别 org.springframeword 改为 TRACE,然后发现日志中有这么一段(有堆栈,很容发现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
12:49:56.112 [http-nio-8050-exec-3] DEBUG o.s.h.c.j.MappingJackson2HttpMessageConverter - Failed to evaluate Jackson deserialization for type [[simple type, class cn.devmgr.mall.product.pojo.Category]]
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a (Map) Key deserializer for type [simple type, class cn.devmgr.mall.product.pojo.ProductPriceKey]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1589)
at com.fasterxml.jackson.databind.deser.DeserializerCache._handleUnknownKeyDeserializer(DeserializerCache.java:599)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findKeyDeserializer(DeserializerCache.java:168)
at com.fasterxml.jackson.databind.DeserializationContext.findKeyDeserializer(DeserializationContext.java:499)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer.createContextual(MapDeserializer.java:248)
at com.fasterxml.jackson.databind.DeserializationContext.handlePrimaryContextualization(DeserializationContext.java:650)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:484)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:443)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:183)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:27)
at com.fasterxml.jackson.databind.DeserializationContext.handlePrimaryContextualization(DeserializationContext.java:650)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:484)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.hasValueDeserializerFor(DeserializerCache.java:191)
at com.fasterxml.jackson.databind.DeserializationContext.hasValueDeserializerFor(DeserializationContext.java:421)

现在原因就很明显了,ProductPriceKey作为map的key导致的。这个属性我本不需要客户端传递,于是在属性增加了只读。

1
2
3
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Map<ProductPriceKey, ProductPrice> salePrices;

Java类和JSON字符串互转,map需要key是string,这个很容易理解,不是string的没法转。(java->json部分在这个类内之前有处理;反过来没处理)

这样就解决了。

这个问题本应该更容易发现,只是spring/fasterxml内把这个错误的日志级别用了DEBUG,不太容易被发现。

在使用 Spring RESTDocs 时,有如下代码

1
2
3
4
5
6
this.mockMvc
.perform(
get("/books/123/images/5")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(...);

运行正常

当把其中url替换成参数形式,改成如下代码时,

1
2
3
4
5
6
7
8
this.mockMvc
.perform(
get("/books/{bookId}/images/{imageId}",
bookId,
imageId)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(...);

执行时出现错误:

java.lang.IllegalArgumentException: urlTemplate not found. If you are using MockMvc did you use RestDocumentationRequestBuilders to build the request?

这个提示也比较清晰,但对于不太了解 RESTDocs 的初学者,可能会迷惑一会,这是由于,静态方法有2个

1
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

上面这个是MockMvc里提供的,不支持URLTemplate这种形式,下面这个是RESTDocs里提供的,支持URLTemplate这种形式,需要用下面这个

1
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;

替换掉import,倒入正确的get方法即可正常运行。

PostgreSQL里有两种特殊的字段类型,数组和JSON,这2个字段在处理一些数据类型时会非常方便。不用多建表还能方便的查询其中内容。

数组

数据中的多对多,我们之前一般需要建一个映射表来处理;或者是在某方建一个比较长的字段(例如text类型)然后用某种分隔符(例如逗号)保存数据。

一对多如果在在「一」方保存「多」的信息,也只能类似多对多的处理方式。

有了数组类型,就可以直接建个数组字段保存这些值了,很简单的实现了一对多。

数组字段的查询

例如,我们创建一个表,产品表,每个产品可以有多张图片,图片用数组字段

1
2
3
4
5
create table product(
id serial primary key,
name char(50) not null,
imgs varchar(50) not null
)

添加一条记录,有3张图p1.jpg, p2.jpg, p3.jpg

1
2
insert into product(name, imgs) 
values('手机', '{"p1.jpg", "p2.jpg", "p3.jpg"}');

查询imgs字段包含 p1.jpg 的记录

1
select * from product where 'p2.jpg' = ANY(imgs)

更多

PostgreSQL 支持多维数组,而且是各种类型的数组、int[], timestamp[] 等等,不仅仅是字符串数组,多维数组形式例如: varchar(100)[][]

JSON

JSON字段用于保存更复杂的数据类型。

json vs. jsonb

PostgreSQL中有2种JSON类型: json 和 jsonb ,两种类型的区别是:

  • json 保存原始值,不会对值进行处理(例如去空格,去重等)。insert/update后 select出来的值的字符串形式是相同的
  • jsonb 保存是处理过的值,会对值去空格去重;insert/update后 select出来的值的字符串形式会不同
  • jsonb 保存(insert/update)速度会稍慢于json
  • 如果使用PostgreSQL 自身的JSON函数查询字段,jsonb会快于json

根据这个特点,如果我们的JSON数据只是在程序中处理,可以选择json类型;如果需要PostgreSQL处理则选择jsonb更佳。

官方文档

Postgresql 的官方文档做得非常好,更多信息看

PostgreSQL还有些其它很有意思的字段,例如:区间类型(日期区间、数值区间等)、复合类型、枚举类型、甚至点线多边形等几何图形等字段类型,而且对于这些类型都有相应的一些辅助运算函数的支持。数据类型

现象

spring gateway 遇到异常,无法启动,异常如下:

14:07:09.901 [main] ERROR o.s.boot.SpringApplication - Application run failed
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:156)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:544)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
    at cn.devmgr.mall.gateway.GatewayMpApplication.main(GatewayMpApplication.java:20)
Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:203)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:179)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:153)
    ... 8 common frames omitted

原因

出现这种状况,是由于项目中混入了 spring-webmvc 的包导致的,spring gateway 使用 webflux, 而webflux和webmvc不兼容,就会出现上面的错误。

解决方法

一般不是直接包含了 spring-webmvc,是引入的某个包需要依赖webmvc,找到并排除就好了,但找起来,需要点方法。

用命令 mvn -X package增加一个 -X 参数去打包,会把详细的依赖路径全部都显示出来。例如:

[INFO] <<< spring-boot-maven-plugin:2.2.1.RELEASE:run (default-cli) < test-compile @ gateway-mp-server <<<
[DEBUG] Dependency collection stats: {ConflictMarker.analyzeTime=1, ConflictMarker.markTime=0, ConflictMarker.nodeCount=909, ConflictIdSorter.graphTime=1, ConflictIdSorter.topsortTime=0, ConflictIdSorter.conflictIdCount=230, ConflictIdSorter.conflictIdCycleCount=0, ConflictResolver.totalTime=15, ConflictResolver.conflictItemCount=552, DefaultDependencyCollector.collectTime=27, DefaultDependencyCollector.transformTime=17}
[DEBUG] cn.devmgr.mall:gateway-mp-server:jar:1.0.0
[DEBUG]    cn.devmgr:common-api:jar:1.0.0:compile
[DEBUG]       org.springframework:spring-webmvc:jar:5.2.1.RELEASE:compile
[DEBUG]          org.springframework:spring-beans:jar:5.2.1.RELEASE:compile
[DEBUG]          org.springframework:spring-context:jar:5.2.1.RELEASE:compile
[DEBUG]          org.springframework:spring-expression:jar:5.2.1.RELEASE:compile
[DEBUG]          org.springframework:spring-web:jar:5.2.1.RELEASE:compile
[DEBUG]       org.springframework.security:spring-security-web:jar:5.2.1.RELEASE:compile
[DEBUG]          org.springframework.security:spring-security-core:jar:5.2.1.RELEASE:compile
[DEBUG]       com.auth0:java-jwt:jar:3.9.0:compile
[DEBUG]          com.fasterxml.jackson.core:jackson-databind:jar:2.10.0:compile (version managed from 2.10.0.pr3 by com.fasterxml.jackson:jackson-bom:2.10.0)
[DEBUG]          commons-codec:commons-codec:jar:1.13:compile (version managed from 1.12 by org.springframework.boot:spring-boot-dependencies:2.2.1.RELEASE)
[DEBUG]    org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:jar:2.2.0.RELEASE:compile
[DEBUG]       org.springframework.cloud:spring-cloud-starter:jar:2.2.0.RELEASE:compile
[DEBUG]          org.springframework.cloud:spring-cloud-context:jar:2.2.0.RELEASE:compile
[DEBUG]             org.springframework.security:spring-security-crypto:jar:5.2.1.RELEASE:compile
[DEBUG]          org.springframework.cloud:spring-cloud-commons:jar:2.2.0.RELEASE:compile
[DEBUG]          org.springframework.security:spring-security-rsa:jar:1.0.7.RELEASE:compile
[DEBUG]             org.bouncycastle:bcpkix-jdk15on:jar:1.60:compile
[DEBUG]                org.bouncycastle:bcprov-jdk15on:jar:1.60:compile
[DEBUG]       org.springframework.cloud:spring-cloud-netflix-hystrix:jar:2.2.0.RELEASE:compile
[DEBUG]          org.springframework.boot:spring-boot-starter-aop:jar:2.2.1.RELEASE:compile
[DEBUG]       org.springframework.cloud:spring-cloud-netflix-eureka-client:jar:2.2.0.RELEASE:compile
[DEBUG]       com.netflix.eureka:eureka-client:jar:1.9.13:compile
[DEBUG]          org.codehaus.jettison:jettison:jar:1.3.7:runtime
[DEBUG]             stax:stax-api:jar:1.0.1:runtime
[DEBUG]          com.netflix.netflix-commons:netflix-eventbus:jar:0.3.0:runtime
[DEBUG]             com.netflix.netflix-commons:netflix-infix:jar:0.3.0:runtime
[DEBUG]                commons-jxpath:commons-jxpath:jar:1.3:runtime
[DEBUG]                joda-time:joda-time:jar:2.10.5:runtime (version managed from 2.3 by org.springframework.boot:spring-boot-dependencies:2.2.1.RELEASE)
[DEBUG]                org.antlr:antlr-runtime:jar:3.4:runtime
[DEBUG]                   org.antlr:stringtemplate:jar:3.2.1:runtime
[DEBUG]                   antlr:antlr:jar:2.7.7:runtime
[DEBUG]             org.apache.commons:commons-math:jar:2.2:runtime
[DEBUG]          com.netflix.archaius:archaius-core:jar:0.7.6:compile
[DEBUG]          javax.ws.rs:jsr311-api:jar:1.1.1:runtime
[DEBUG]          com.netflix.servo:servo-core:jar:0.12.21:runtime
[DEBUG]          com.sun.jersey:jersey-core:jar:1.19.1:runtime
[DEBUG]          com.sun.jersey:jersey-client:jar:1.19.1:runtime
[DEBUG]          com.sun.jersey.contribs:jersey-apache-client4:jar:1.19.1:runtime
[DEBUG]          org.apache.httpcomponents:httpclient:jar:4.5.10:compile (version managed from 4.5.3 by org.springframework.boot:spring-boot-dependencies:2.2.1.RELEASE)

在输入里面搜索 webmvc ,找到最上级后,在pom里排除掉即可,例如:

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>cn.devmgr</groupId>
<artifactId>common-api</artifactId>
<exclusions>
<exclusion><!-- 这里是排除掉webmvc -->
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</exclusion>
</exclusions>
</dependency>

问题

在一个页面上对同一个自定义组件调用了2次,代码如下:

1
2
3
4
5
6
7
8
9
10
11

<MyComponent
v-if="type1 == 2"
v-model="data1"
name="标题A" />

<MyComponent
v-if="banner.type == 3"
v-model="data2"
name="标题B" />

在页面上还有一组单选按钮,用来选择 type,当 type==2 时,显示上面的 name 是“标题A”的自定义组件,当 type==3 时,则显示下面的 name 是“标题B”的组件。 乍看之下,运行良好,当组件扩充里面有data部分时,问题出现了,对上面的组件实例进行操作导致 data 的值变化后,下面的组件实例的data部分也会跟着变,加入日志显示,两个实例似乎共享了data部分,props部分还保持自己的,没有被更改。

这不科学,不符合 VUE.js 手册上对用户自定义组件的描述,而且组件的data部分是函数,也是按照 VUE.js 手册的标准写法写的。

解决方法

后来发现是两次调用都有 v-if 一次只能出现一个导致 vue 在使用时混乱了导致,给每个对组件的引用都增加了key属性,并使用了不同的key值,问题得以解决。

1
2
3
4
5
6
7
8
9
10
11
12
13

<MyComponent
v-if="type1 == 2"
key="componentA"
v-model="data1"
name="标题A" />

<MyComponent
v-if="banner.type == 3"
key="componentB"
v-model="data2"
name="标题B" />