GeXiangDong

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

0%

安装 babel-plugin-transform-remove-consol

1
npm install babel-plugin-transform-remove-console --save-dev

修改 babel.config.js 文件,加入 plugin transform-remove-console

1
2
3
module.exports = {
plugins: ["transform-remove-console"]
};

这是再运行项目就不会有console输出了,但是我们期望的是 development 环境有,production 环境没有,可以这样设置:

1
2
3
4
5
6
7
8
9
10
var babelconfig = {
presets: ["@vue/cli-plugin-babel/preset"], //其它原有设置
plugins: []
};
if (process.env.NODE_ENV === "production") {
// production 环境则增加此插件
babelconfig.plugins.push("transform-remove-console");
}

module.exports = babelconfig;

这样就可以了。

异常 I

spring cloud 的 eureka server 启动时出现如下错误

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
09:17:44.673 [SpringContextShutdownHook] WARN  c.n.d.s.t.d.RetryableEurekaHttpClient - Request execution failed with message: java.net.ConnectException: Connection refused (Connection refused)
09:17:44.674 [SpringContextShutdownHook] ERROR c.netflix.discovery.DiscoveryClient - DiscoveryClient_REGISTER-SERVER/192.168.123.121:register-server:8012 - de-registration failedCannot execute request on any known server
com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
at com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute(RetryableEurekaHttpClient.java:112)
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.cancel(EurekaHttpClientDecorator.java:71)
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$2.execute(EurekaHttpClientDecorator.java:74)
at com.netflix.discovery.shared.transport.decorator.SessionedEurekaHttpClient.execute(SessionedEurekaHttpClient.java:77)
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.cancel(EurekaHttpClientDecorator.java:71)
at com.netflix.discovery.DiscoveryClient.unregister(DiscoveryClient.java:941)
at com.netflix.discovery.DiscoveryClient.shutdown(DiscoveryClient.java:919)
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:566)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeDestroyMethods(InitDestroyAnnotationBeanPostProcessor.java:347)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeDestruction(InitDestroyAnnotationBeanPostProcessor.java:177)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:242)
at org.springframework.beans.factory.support.DisposableBeanAdapter.run(DisposableBeanAdapter.java:235)
at org.springframework.cloud.context.scope.GenericScope$BeanLifecycleWrapper.destroy(GenericScope.java:403)
at org.springframework.cloud.context.scope.GenericScope.destroy(GenericScope.java:142)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:258)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1072)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1065)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1060)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1029)
at org.springframework.context.support.AbstractApplicationContext$1.run(AbstractApplicationContext.java:948)

这个错误是由于 Eureka 启动时默认会把自己注册到注册中心(默认注册中心是localhost,虽然没配置,但是有这么个默认值),而此时没有注册中心(自己是注册中心,尚未启动),所以出错了。

解决方案很简单,禁止注册就好了

1
2
3
4
5
6
eureka:
instance:
preferIpAddress: true
client:
register-with-eureka: false
fetch-registry: false

如果网络上已经有注册中心,也可以提供注册中心的URL,让 eureka server 能购找到注册中心也可

1
2
3
4
5
6
eureka:
client:
register-with-eureka: true
fetch-registry: false
service-url:
defaultZone: http://192.168.1.1/eureka/

异常 II

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
09:42:18.813 [TaskBatchingWorker-target_localhost-9] ERROR c.n.e.c.ReplicationTaskProcessor - Network level connection to peer localhost; retrying after delay
com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused (Connection refused)
at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:187)
at com.netflix.eureka.cluster.DynamicGZIPContentEncodingFilter.handle(DynamicGZIPContentEncodingFilter.java:48)
at com.netflix.discovery.EurekaIdentityHeaderFilter.handle(EurekaIdentityHeaderFilter.java:27)
at com.sun.jersey.api.client.Client.handle(Client.java:652)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:682)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.post(WebResource.java:570)
at com.netflix.eureka.transport.JerseyReplicationClient.submitBatchUpdates(JerseyReplicationClient.java:117)
at com.netflix.eureka.cluster.ReplicationTaskProcessor.process(ReplicationTaskProcessor.java:80)
at com.netflix.eureka.util.batcher.TaskExecutors$BatchWorkerRunnable.run(TaskExecutors.java:193)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.net.ConnectException: Connection refused (Connection refused)
at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399)
at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242)
at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:403)
at java.base/java.net.Socket.connect(Socket.java:591)
at org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:121)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:144)
at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:134)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:605)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:440)
at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:118)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:173)
... 10 common frames omitted

erureka server 使用中出现上面的错误,这也是2个注册中心连接复制信息造成的。如果只有一个注册中心,可以用这个禁止:

1
2
3
eureka:
server:
maxThreadsForPeerReplication: 0

原因

在微服务体系中,注册中心是十分重要的一环,注册中心如果死掉,那么所有微服务之间的通讯就没法进行了,整个系统陷入瘫痪。所以eureka才默认就开启了双机模式,这也是开发环境中出现上述2个异常的原因,没有使用双注册中心的备份机制。

生产环境应该使用双注册中心(或更多),不要开启上述几个设置仅仅屏蔽异常。

Spring Security 5.x 开始把密码机制升级了,使用DelegatingPasswordEncoder。 这是一种支持多种加密密码并存的方法,取代了以前只支持一种密码机制的方法。

这种升级需要密码的密文增加一个前缀,来说明密文是通过那种办法加密的,例如之前用 BCryptPasswordEncoder 加密出来的密文

$2a$10$H2z/oHZgviqC8srzsCZq/ObzTNRTfygC4N0NEZlm5trYPFrvdNHeS

需要被存储为:

{bcrypt}$2a$10$H2z/oHZgviqC8srzsCZq/ObzTNRTfygC4N0NEZlm5trYPFrvdNHeS

如果是从 spring security 4.x 升级过了,需要修改下数据库里保存的密文密码。

在开发测试过程中可以使用 {noop}12345 这样的明文密码就比较方便了。

可能遇到的异常或警告

下面2种异常/警告 都是和密码加密有关的。

WARN o.s.s.c.bcrypt.BCryptPasswordEncoder - Encoded password does not look like BCrypt

这个警告是因为密文前缺少{bcypt},用了BCryptPasswordEncoder去match,Spring 会给出这个警告。

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id “null”

这也是因为密文没有指明加密方式的前缀,用了DelegatingPasswordEncoder去match这个password是否正确,密文的加密方法前缀是null,才出这个错。

其它

另外需要注意的,除了WebSecurityConfigurerAdapter会涉及到登录用户的密码外,在oauth Authentication Server的 ClientDetail 里的 client_secret 也需要类似处理。

参考

https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released#password-encoding

在使用jason做json和类的转换时,遇到错误

Cause: java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.LinkedHashMap<java.lang.Object,java.lang.Object>` out of START_ARRAY token

是因为json是数组格式,而提供的待转化成的类是 Map 格式,导致转化失败。

问题表现

在老程序升级到java9或以后版本时,如果遇到 java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException 错误:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:645) ~[spring-beans-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:475) ~[spring-beans-5.2.1.RELEASE.jar:5.2.1.RELEASE]

原因

这是由于JAXB API没找到造成的。 JAXB 在是 Java EE API的一部分,在java9开始不再包含在默认的java9的module中(java9开始分module了),甚至在java11开始,干脆把它给移除出JDK了。

解决方法

知道了原因解决办法也就有了:

  • 继续用Java8
  • 命令后增加 add-modules java.xml.bin,这个方法只对 java9 java10有效
  • 一劳永逸的解决问题(对9和以后版本都生效)增加maven依赖(或者gradle)
1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
</dependency>

<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
</dependency>

其它

这篇文章:org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat也遇到过相同的JAXBException,解决方式类似。

用java的 Graphics 的 drawString 往图片上描画文字的时候,文字边缘可能会出现粗糙的锯齿(如下图,上部的”文“字的放大效果),可以通过Graphics2D设置抗锯齿效果。

代码和两种的比较效果如下(只有第18行是设置抗锯齿效果的代码):

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
BufferedImage img = new BufferedImage(800, 300, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();

InputStream fi = Thread.currentThread().getContextClassLoader().getResourceAsStream("fonts/msyh.ttf");
BufferedInputStream bisYh = new BufferedInputStream(fi);
Font yahei = Font.createFont(Font.TRUETYPE_FONT, bisYh);

g.setColor(Color.WHITE);
g.fillRect(0, 0, 800, 300);

Font font = yahei.deriveFont(Font.BOLD, 48);
g.setColor(Color.BLACK);

g.setFont(font);
g.drawString("中文字体,无抗锯齿效果", 10, 50);

// 下面这行是设置文字抗锯齿效果的
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.drawString("中文字体,有抗锯齿效果", 10, 150);

FileOutputStream fos = new FileOutputStream("target/font.png" );
ImageIO.write(img, "png", fos);

fos.flush();
fos.close();

字体

通过上图可以看出,设置抗锯齿效果后,会在文字边缘有些过度颜色的点。如果是要在单色打印机上打印(例如android下连接小票打印机),就不需要这种抗锯齿效果了,有它反而会增加麻烦,我就是在调试android下单色打印机时发现的这个效果。

描画图片时也可以设置抗锯齿效果

1
2
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

JDK中Graphics2D类 drawString(String str, float x, float y) 方法的后两个参数是用来控制字符串的位置。

这2个参数即不是字符串左上角距画布原点的坐标,也不是左下角距原点的坐标,而是baseline起点距原点的坐标。说到baseline,请参照下图

字体

如上图,红色线是字体描画时的baseline(基线),小写英文字母是可能有些部分在baseline以下的,比如 gjpqy 等,中文每个字都有可能有部分在baseline以下。baseline和字的最上方之间的距离称作 ascent,baseline和最下方之间的距离叫 descent. 这两段距离可以通过 FontMetrics 类获取。

如果要想被画的文字左上角在x,y 位置,可以通过如下代码

1
2
3
4
5
public void drawString(Graphics2D g, String s, float x, float y){
FontMetrics metrics = g.getFontMetrics(g.getFont());
g.drawString(s, x, y - metrics.getAscent());
}

通过FontMetrics类,我们还可以获取一段文字的实际占用宽度等,可以实现右对齐和居中对齐

spring boot 项目访问某个controller时出错,错误信息如下:

2019-10-19 09:46:01.451 ERROR 1040 --- [nio-4675-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Circular view path []: would dispatch back to the current handler URL [/] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)] with root cause

javax.servlet.ServletException: Circular view path []: would dispatch back to the current handler URL [/] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
    at org.springframework.web.servlet.view.InternalResourceView.prepareForRendering(InternalResourceView.java:209) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:147) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1370) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1116) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1055) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_131]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.14.jar:9.0.14]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]

这个错误是由于访问的是 Controller, 而该 Controller对方的方法没有对应的视图造成的。 我这里出现这个错误,是因为我本来就不需要视图,我是想返回 JSON,但把 RestController 误写成了 Controller。

字体的大小,在网页中常见单位是px,印刷和word中,常用五号、四号等形式。

印刷常用字号和毫米对应关系

1英寸=72磅=2.54厘米;显示器的

px和pt(磅)之间的换算和DPI有关,DPI有些地方也称作PPI

1pt = DPI / 72 px

  • Windows 的默认DPI是 96 (用户可以修改);
  • Mac 的DPI是72

DPI=72时 1英寸显示72px, 1pt = 1px
DPI=96时 1英寸显示96个px, 1pt = 96 / 72 = 1.333px (9pt = 12px)

根据字号对应的磅值,可以得到下表的换算后数值

字号 磅 (pt) 毫米 (mm) 像素 (px) DPI=72 像素 (px) DPI=96
八号 4.5 1.581 5 6
七号 5.25 1.845 5 7
小六 6.875 2.416 7 9
六号 8 2.812 8 11
小五 9 3.163 9 12
五号 10.5 3.690 11 14
小四 12 4.218 12 16
四号 14 4.920 14 19
三号 16 5.623 16 21
小二 18 6.326 17 24
二号 21 7.381 21 28
一号 28 9.841 28 37
小初 36 12.653 36 48
初号 42 14.761 42 56

网页中常用的单位

网页中除了px外,还会用到pt、rem, em,

  • em 是指一个字母的宽度(最初指定的是字母M),因为网页默认字体是16px,所以 1em=16px;后来就固定下来了。这个单位会随着浏览器/操作系统放大字号而放大。(px指定的字体在早期IE下不会随着缩放字体而缩放),现在浏览器的缩放功能,都是对整个网页的放大缩小,已经不存在这个问题了。
  • % 字体也可以用百分比指定大小,100% = 16px
  • pt (win 1pt ≈ 1.333px; max 1pt = 1px)
  • cm / mm / in in是inch英寸的缩写, cm是厘米mm是毫米和生活中用到厘米毫米没区别,1in = 2.54cm,这三个单位到px又和DPI相关了。 1in = DPI * 1px;
  • ex 和em类似,用的是字母X的高度,很少有人用

CSS3中新增了几个长度单位

  • ch字符0的宽度
  • rem HTML标签元素的em数
  • vm (viewpoint width), 1vm = viewpointwidth * 1%
  • vh (viewpoint height), 1vh = viewpointheight * 1%
  • vmin = min(viewpoint width, viewpoint height)
  • calc(100% - 5px) 表达式,可以做点简单运算

viewpoint在做手机端网页时会用到

在使用微信开发者工具开发小程序时遇到两个怪异问题,记录于此。

websocket 不往服务器端发送 header 信息

某天,突然开始看到报错,小程序的websocket连接不上了,服务端的验证一直通过不,想着没改小程序端,于是一直各种跟踪调试服务端的websocket配置,发现没有收到用于传递身份信息的头导致的。思前想后百思不解,为啥这个头消失了。后来想到电脑有几天没重启过了,之间也没退出过微信开发者工具,尝试退出微信开发者工具,重新进入,问题得以解决。

模拟器中小程序保存图片到相册后自动返回首页

在模拟器中有一个保存图片的功能(把canvas上的图存入相册),保存后,模拟器会弹出一个对话框,选择文件的存储位置,然后保存。有几次没注意文件的位置就存了,存了后,发现小程序自动跳转到了首页,而且console的日志也消失了,好像重启了小程序。原因是弹出的对话框默认是小程序的路径,文件存在了小程序项目目录下,开发者工具检测到了文件变化会重启模拟器内小程序已变反应变化。 换个路径保存就好了。这也不能算开发者工具的bug,只是没注意到这点时,会很麻烦。