0%

前言

在 Linux 命令中,find用于在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名,其支持按名称查找、按正则表达式查找、按文件大小查找、按文件权限查找等多种查询方式。如果在使用该命令时,不设置任何参数,则find命令将在当前目录下查找子目录与文件,并且将查找到的子目录和文件全部进行显示。

前言

在梳理项目的过程中发现很多开发同学对Maven依赖文件的配置并不了解,特别是对Maven的optional元素和scope元素的使用也非常随意。这就会导致发布的jar包或war包非常“胖”、编译速度慢,而且还很容易生产jar冲突等问题。

optional元素

这里以Spring Boot项目中的使用为例,比如我们在项目中经常使用的热部署组件spring-boot-devtools,就可以使用optional元素来进行定义,对应pom文件中配置如下:

1
2
3
4
5
6
<!--devtools 热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>

那么,这里的optional元素设置为true表示何意?optional是Maven依赖jar时的一个选项,表示该依赖是可选的,项目之间依赖不传递。不设置optional(默认)或者optional是false,表示传递依赖。

文字描述可能比较抽象,下面用具体实例场景来进行更直观的描述,这里假设有两个项目A和B,其中A为父项目,B为子项目。在父项目中引入了单元测试的依赖:

1
2
3
4
5
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
optional元素默认值(false)

父项目并未设置optional元素为true,那么便具有依赖传递性。此时,子项目B中会直接引入父项目A中引入的Junit的jar包。也就是说B项目打包时,jar/war包中会包含junit的jar包

optional元素为true

当父项目引入junit依赖时,设置optional元素为true。那么,子项目B便有了更多的选择。

如果项目B不需要Junit的jar包,那么在其pom文件中不需进行任何处理便可以。如果B项目也需要对应的jar包依赖,可以有两种选择:第一、A项目中对应依赖的optional设置为false或去掉;第二、B项目中直接引入需要的该依赖。

scope元素

上面讲完了optional元素的使用,再来看看scope的使用。

scope元素主要用来控制依赖的使用范围,指定当前包的依赖范围和依赖的传递性,也就是哪些依赖在哪些classpath中可用。常见的可选值有:compile, provided, runtime, test, system等。

compile(编译)

默认值。compile表示对应依赖会参与当前项目的编译、测试、运行等,是一个比较强的依赖。打包时通常会包含该依赖,部署时会打包到lib目录下。比如:spring-core这些核心的jar包

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
test(测试)

scope为test表示依赖项目仅参与测试环节,在编译、运行、打包时不会使用。最常见的使用就是单元测试类了:

1
2
3
4
5
6
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

类似单元测试这样的依赖,如果不设置scope为test,很显然它们会被打包、发布,但其实真是环境中并无什么作用。

runntime(运行时)

runntime仅仅适用于运行和测试环节,在编译环境下不会被使用。比如编译时只需要JDBC API的jar,而只有运行时才需要JDBC驱动实现。

1
2
3
4
5
6
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
<scope>runtime</scope>
</dependency>
provided(已提供)

provided适合在编译和测试的环境,和compile功能相似,但provide仅在编译和测试阶段生效,provide不会被打包,也不具有传递性。

比如:上面讲到的spring-boot-devtools、servlet-api等,前者是因为不需要在生产中热部署,后者是因为容器已经提供,不需要重复引入。

1
2
3
4
5
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
system

system范围依赖与provided类似,不过依赖项不会从maven仓库获取,而需要从本地文件系统提供。使用时,一定要配合systemPath属性。不推荐使用,尽量从Maven库中引用依赖。

1
2
3
4
5
6
7
<dependency>
<groupId>sun.jdk</groupId>
<artifactId>tools</artifactId>
<version>1.5.0</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>

前言

今天遇到个问题,项目中用的是shiro和jwt,自定义了一个jwt过滤器来判断当前请求是否有权限,项目跑起来后发现所有的请求都不走过滤器,直接放行了?

解决

在shiroConfig中是这么配置的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Map<String, String> filterRuleMap = new HashMap<>(2);
// 访问401和404页面不通过我们的Filter
//通过http://127.0.0.1:9527/druid/index.html 访问
filterRuleMap.put("/druid/**", "anon");
//放行webSocket
filterRuleMap.put("/websocket/*", "anon");
//放行swagger
filterRuleMap.put("/swagger-ui.html", "anon");
filterRuleMap.put("/swagger-resources", "anon");
filterRuleMap.put("/v2/api-docs", "anon");
filterRuleMap.put("/webjars/springfox-swagger-ui/**", "anon");
// 所有请求通过我们自己的JWT Filter
filterRuleMap.put("/**", "jwt");
filterRuleMap.put("/**", "anon");
factoryBean.setFilterChainDefinitionMap(filterRuleMap);

一眼看过去好像没什么问题,但是就是不走自定义的jwt过滤器,所以判断问题一定出在这里,经过打断点后,发现filterRuleMap中的”/**”没有jwt这条,居然是下面的这个anon这个,那就没啥问题了

总结

anon的匿名的缩写,走的是shiro的默认过滤器,默认放行接口,因为前面是/**,因此会放行所有接口,不走我们的过滤器,删除这行后,接口正常走过滤器

前言

最近在项目上遇到个需求,之前用的post请求,json参数,但是json参数是个数组,现在想要加个参数,我不想在

数组里每个都加一个,一开始想的是我直接在post url后面加个/{id},比如这样的,我们知道Get请求是可以这样的,但是post是不能这样的,否则会报url找不到

解决

@RequestParam@RequestBody 都是从 HttpServletRequest request 中取参的,而 @PathVariable 是映射 URI 请求参数中的占位符到目标方法的参数中的,接下来一一举例说明。

前端在不明确指出 Content-Type 时,默认为 application/x-www-form-urlencoded 格式,@RequestParam 可以获取 application/x-www-form-urlencoded 以及 application/json 这两种类型的参数,但是 @RequestBody 是用来获取非 application/x-www-form-urlencoded 类型的数据,比如 application/jsonapplication/xml 等。

@RequestParam

这种方式无论是 GET 还是 POST 请求,都是可以获取到参数的,举例中使用了 @RequestParam 注解的一些参数,具体参数如下:

  • defaultValue 如果本次请求没有携带这个参数,或者参数为空,那么就会启用默认值
  • name 绑定本次参数的名称,要跟URL上面的一样
  • required 这个参数不是必须的,如果为 true,不传参数会报错
  • valuename一样的作用,是name属性的一个别名

@PathVariable

@PathVariable 也有相应的参数:

  • name 绑定参数的名称,默认不传递时,绑定为同名的形参。 赋值但名称不一致时则报错
  • value 跟name一样的作用,是name属性的一个别名
  • required 这个参数不是必须的,如果为 true,不传参数会报错

使用 @PathVariable 需要注意两点:

  • 参数接收类型使用基本类型
  • 如果@PathVariable标明参数名称,则参数名称必须和URL中参数名称一致
@ReuqestBody(不能用于GET请求)

使用 @RequestBody 注解可以方便的实现 JSON 串到接收参数的数据映射。

说明一下 @RequestBody 为什么不能用用于 GET 请求,RequestBody 顾名思义,是将请求参数设置在请求 Body 中的,也就是请求体,而 GET 请求无请求体。

使用 @RequestBody 需要满足如下条件:

  • Content-Type application/json,确保传递是 JSON 数据;
  • 参数转化的配置必须统一,否则无法接收数据,比如 json、request 混用等
总结

1、在 GET 请求中可以使用 @RequestParam,不能使用 @RequestBody,@RequestBody 是用来获取请求体中的参数,因为 GET 请求没有请求体,所以不能使用。

2、在 POST 请求中,可以使用 @RequestBody 和 @RequestParam ,其中 @RequestParam 是用来获取 application/x-www-form-urlencodedform-data 格式数据的,@RequestBody 用来获取非 application/x-www-form-urlencoded 数据的,比如 application/jsonapplication/xml 等。

3、一个方法中,可以同时使用多个 @RequestParam ,但是只能使用一个 @RequestBody,否则会报错。

4、@PathVariable 起到的作用就是 URI 请求参数中的占位符到目标方法参数的映射。

5、前端请求的 Content-Type ,默认值为 application/x-www-form-urlencoded,在这种格式下,后端直接使用 @RequestParam 就可以直接获取指定的参数,但是一旦前端传递的是 JSON 数据,也就是 Content-Type 的值为 application/json,那么使用 @RequestParam 是取不到值的,不但取不到值还报错。

前言

我用腾讯的服务比较多,比如服务器、域名、消息队列、cdn等,所以我看很多docker镜像加速提供的是阿里的加速服务,我搜了一下腾讯也有docker镜像源加速,那就直接用腾讯的吧,笔记在腾讯的服务器上,应该会更快

配置

执行以下命令,打开 /etc/docker/daemon.json 配置文件

1
vim /etc/docker/daemon.json

不用管有没有这个文件,直接编辑就行,不存在会直接新建

i 切换至编辑模式,添加以下内容,并保存

1
2
3
4
5
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com"
]
}

执行以下命令,重启 Docker 即可

1
sudo systemctl restart docker

前言

最近项目中需要集成websocket,推送消息到浏览器,下面简单说下集成的流程,因为开发android习惯了使用kotlin,所以服务端也改成了用kotlin来写,因为kotlin写起来代码来是真的舒服,下面的代码以kotlin为例,java代码也是一样的

过程
  1. 添加依赖

    1
    2
    3
    4
    <dependency>  
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
  2. 启用websocket配置

    阅读全文 »

前言

今天项目上让同事改个配置文件,不知道同事怎么改的,启动jar包时报错[java.nio.charset.MalformedInputException: Input length=2],因为只改了配置文件,这个报错里又有charset这样的字样,所以猜测可能时配置文件编码给搞乱了

解决

将配置文件改成utf-8编码,用记事本保存时更改,或者直接从编译器或者原始jar包里重新拷贝一份,我是直接重新从代码里拷贝了一份出来

前言

最近项目中遇到一个需求,一个List<entity>,实体里有多个字段,只有部分有值,需要遍历这个实体,给其他的字段也赋值后在更新会数据库,首先想到的就是stream流操作

过程
1
2
3
4
5
6
7
waitDevs.parallelStream().map(rec -> {
rec.setState("1");
rec.setOperator(SecurityUtils.getUser().getUsername());
rec.setOperationTime(DateUtil.date());
rec.setRemark(sysAlarmRecVO.getRemark());
return res;
}).collect(Collectors.toList());

一开始是这么写的,map里设置完,return返回这个实体,但是idea提示了可以用peek这个操作,按照它的提示最后改进了这个代码

1
2
3
4
5
6
waitDevs.parallelStream().peek(rec -> {
rec.setState("1");
rec.setOperator(SecurityUtils.getUser().getUsername());
rec.setOperationTime(DateUtil.date());
rec.setRemark(sysAlarmRecVO.getRemark());
}).collect(Collectors.toList());

不需要return了,下面说下这俩的区别

区别
1
Stream peek(Consumer<? super T> action)

返回由该流的元素组成的流,另外在从生成的流中消耗元素时对每个元素执行提供的操作。

1
Stream map(Function<? super T,? extends R> mapper)

返回由给定函数应用于此流的元素的结果组成的流。

  • 两个函数都是中间操作,都非常的‘懒’,没有对Stream的终止操作,两个函数都不会工作。
  • peek函数的存在仅仅是为了debug,而map是Stream的一个核心函数,两个函数的地位不同。
  • 两个函数的返回值都是一个新的Stream,但是两个函数的参数(peek是Consumer,map是Function)起作用的时机不同。map的Function在生成新的Stream之前被执行,新Stream中的元素是上游Stream中元素经Function作用后的值。peek函数的Consumer工作在生成Stream之后,下一节详细讲解两个函数执行时机。
peek和map修改Stream的元素

map函数对Stream中元素执行的是映射操作,会以新的元素(map的结果)填充新的Stream,严格的讲map不是修改原来的元素。

peek只能消费Stream中的元素,是否可以更改Stream中的元素,取决于Stream中的元素是否是不可变对象。如果是不可变对象,则不可修改Stream中的元素;如果是可变对象,则可以修改对象的值,但是无法修改对象的引用。

总结

举个很简单的例子,比如 Stream.of("one", "two", "three", "four")流中是字符串,如果peek中修改了字符串,则是无效的,但是流中如果是个可变对象,就是可以修改的

前言

最近写客户端项目时用room数据库时,遇到个问题,编译项目报错:No value passed for parameter 'id',翻译过来是: 没有为参数“id”传递值,下面记录下解决方法

过程

我的实体类如下

1
2
3
4
5
6
@Entity(tableName = "user")
data class User(
@PrimaryKey(autoGenerate = true) var id: Int,
var name: String,
var addr: String,
)

我在新建实体的时候报上面的错

1
val user = User(name = 'jinsc', addr = '1')

因为是让主键自动生成,所以不能给ID赋值,但是不赋值会报错

解决
1
2
3
4
5
6
7
@Entity(tableName = "todos")
data class User(
var name: String,
var addr: String,
) {
@PrimaryKey(autoGenerate = true) var id: Int = 0
}

相等于默认赋值

前言

最近在项目上遇到个加解密的问题,用的是3des加密,我这边在Android上需要解密它加密的数据,测试发现一直都是解密失败,但是它加密的肯定没错,它自己也能解密的,了解了这一点,就排除了密钥错误的问题,想想是不是别的问题呢

解决

Java中默认实现为:DESede/ECB/PKCS5Padding, 第一个是3des加密,肯定没问题,第二个模式为ecb(电子密码本模式),也没问题,那存在问题的可能就是第三个补码方式了

上面说了,java默认的补码方式是PKCS5Padding,常用的还有PKCS7PaddingZeroPadding,先了解下三个的区别

  • ZeroPadding,数据长度不对齐时使用0填充,否则不填充。
  • PKCS7Padding,假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小。
  • PKCS5PaddingPKCS7Padding的子集,块大小固定为8字节。

那就用排除法一个个试下吧,PKCS5Padding是默认的补码方式,所以不用试了,PKCS7Padding也是一样的报错,最后用ZeroPadding发现没问题,可以正常解密,看来它加密的时候用的是ZeroPadding补码的