GeXiangDong

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

0%

git用起来很方便,小型团队不需要权限区分时,仅仅使用git就够了。

需要权限区分时,可以使用gitolite来做权限控制。

安装

首先安装好 git openssh 等,这里不再累述。

ssh到安装好git的服务器上,

切换到git用户 sudo su - git ,之后确认下 .ssh 目录下没有 authorization_keys 文件或改文件为空。(以下这些命令都在git用户下执行)

运行下面的命令

1
2
3
git clone https://github.com/sitaramc/gitolite
mkdir -p $HOME/bin
gitolite/install -to $HOME/bin

这三个命令的作用是,从github.com克隆gitolite的安装仓库,在当前用户(git)的用户目录下创建一个bin目录,并且把gitolite安装到这个目录。

安装到这个目录后,由于path里没有这个目录,调用此目录下新安装的命令时,需要写全路径,不过这影响不大,因为这几个命令不会经常用。

下面这个很关键,不要错,把自己用的客户机上的公钥(.ssh目录下的id_rsa.pub)文件拷贝到服务器上git用户目录下,并且命名为admin.pub,之后再运行下面的命令

1
/home/git/bin/gitolite setup -pk admin.pub

此命令运行成功后,会出现如下内容:

1
2
Initialized empty Git repository in /home/git/repositories/gitolite-admin.git/
Initialized empty Git repository in /home/git/repositories/testing.git/

而且会在git用户目录下创建 repository目录和projects.list文件。
如果用的公钥不正确,可以删除这2个目录/文件,然后重新来过。

此时gitolite已经安装好了,要设置仓库、用户和权限,都需要在客户端(就是前面生成admin.pub的机器)上进行了

日常管理

首先把gitolite-admin这个仓库克隆到本地,所有管理工作都由这个仓库进行。

1
git clone ssh://git@host/gitolite-admin

如果出错,请首先检查客户端电脑的用户公钥是否就是安装过程中使用的admin.pub。

克隆下 gitolite-admin 这个仓库后,会发现有2个目录 conf 和 keydir,

  • conf是仓库和用户的配置目录,内有一个gitolite.conf 文件,存储用户和仓库的对应权限
  • keydir 是保存的用户的公钥,按照上述安装步骤进行的,应该已经有一个 admin.pub 文件了

创建仓库

不需要手工创建仓库,在 conf/gitolite.conf 文件中配置的仓库名,在commit并push后,会自动被创建。

仓库的URL

仓库的URL是

ssh://git@host/repo-name

例如:

ssh://git@192.168.1.2/testing

仓库名后面有或没有.git都可以。

gitolite 命令

除了安装时使用gitolite命令的setup参数初始化了 gitolite-admin 和 testing 两个仓库外,这个命令还可以接收以下这些参数

list-groups                 list all group names in conf
list-users                  list all users/user groups in conf
list-repos                  list all repos/repo groups in conf
list-phy-repos              list all repos actually on disk
list-memberships            list all groups a name is a member of
list-members                list all members of a group

这可以帮助我们确认服务器上到底有哪些仓库/用户等。

参考

gitolite github网址:https://github.com/sitaramc/gitolite

安装 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在做手机端网页时会用到