jsp以reqBody传给后端 后端用@RequestParam分别接收参数
curl -X GET \
http://bjfk-staging-ls508.yz02:9494/rest/infra/id/card/user/bind/account?uid=xxx&name=xxx&identity=xxx&appType=xxx
后端使用@RequestParam分别承接几个参数即可
@RequestParam只应用于content-type为application/x-www-form-urlencoded(传统表单方式)的场景或者是请求url ?后的参数
ajax默认的格式为application/x-www-form-urlencoded,相当于(username="admin"&password=123)来传递数据,当ajax为默认格,后端可以用两种方式:1、@RequestParam分别接收参数。2、可以封装为对象接收或者单个参数接收
如果前端传参方式为 -d '{"user":"wer", "pwd":"123"}',后端只能用jsonParam或者@RequestBody
Content-Type使用application/json的时候,data为json字符串(JSON.stringify(xxx)), 后端用@RequestBody 方式接收,参数必须是实体类或者map形式,不能单独设置参数,例如public void test(@RequestBody User user)或者public void test(@RequestBody Map map)
http请求头:application/json;charset=utf-8 表示告诉服务端消息主体是json类型的
collect(Collectors.joining()) 表示把所有字符串连接起来
ObjectUtils.isNotEmpty可以判断数组是否为空,字符串长度等
NumberUtils.toLong() 可以避免判断是否为空
String.valueOf() 可以避免是否判断为空
字符串转成int(注意给默认值):NumberUtils.toInt
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8"):
private Date createTime;
表示序列化时,将createTime字段转为yyyy-MM-dd HH:mm:ss格式
@JsonValue: 序列化时,将枚举序列化为code或name,反序列化时,由code或name反序列化成枚举
public enum GridElement {
/**
* 商业化格元素
*/
BUSINESS_ELEMENT(6, "商业化格元素"),
;
private int intValue;
private String desc;
@JsonValue
public String getDesc() {
return desc;
}
}
表示BUSINESS_ELEMENT序列化的结果是「商业化格元素」,也可以由「商业化格元素」反序列化得到BUSINESS_ELEMENT
@JsonInclude:
JsonJsonInclude.Include.ALWAYS 这个是默认策略,任何情况下都序列化该字段,和不写这个注解是一样的效果。
JsonJsonInclude.Include.NON_NULL这个最常用,即如果加该注解的字段为null,那么就不序列化这个字段了。
JsonJsonInclude.Include.NON_EMPTY 这个属性包含NON_NULL,NON_ABSENT之后还包含如果字段为空也不序列化。这个也比较常用
JsonJsonInclude.Include.NON_DEFAULT 这个也好理解,如果字段是默认值的话就不序列化
在属性上使用NON_DEFAULT:如果字段是默认值就不序列化了
public class Employee2 {
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
private String name;
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
private String dept;
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
private Integer salary;
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
private boolean fullTime;
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
private List<String> phones;
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
private Date dateOfBirth;
}
public class ExampleMain2 {
public static void main(String[] args) throws IOException {
Employee2 employee = new Employee2();
employee.setName("Trish");
employee.setFullTime(false);
employee.setPhones(new ArrayList<>());
employee.setSalary(Integer.valueOf(0));
employee.setDateOfBirth(new Date(0));
ObjectMapper om = new ObjectMapper();
String jsonString = om.writeValueAsString(employee);
System.out.println(jsonString);
}
}
输出结果是{"name":"Trish"},因为Employee2的其他字段都是默认值
@JsonUnwrapped注解:将嵌套的对象平铺展开
https://juejin.cn/post/7067812048419160071
涉及数字转换 要用NumberUtils工具
把null转成empty:nullToEmpty()方法
parameterMap.put("mobile", joiner.join(requests.stream().map(request -> joiner.join(request.getMobiles().stream().map(SmsCommon.CellPhoneNumber::getNumber).collect(toList()))).collect(toList())))
Optional.ofNullable(...) 入参为null则返回null,否则返回入参
ofNullable(...).ifPresent() 如果ofNullable的结果不是空,则调用ifPresent
Optional.orElse(value) 如果optional保存的是null,返回value。如果保存的不是null,返回option自己的值
Optional.orElseGet(...) 和orElse一样,只是入参是对象
一般从stream中选出个体,就要调用stream的findFirst()方法,然后orElse
firstNonEmpty(T... values)方法,第一个入参不为空则返回 为空则判断后面的 以此类推
isPresent() 返回boolean,如果值存在则方法会返回true,否则返回 false
anyMatch(): stream中任何一个元素满足即返回true
allMatch(): stream中所有元素都满足即返回true
.findAny().isPresent() 和 anyMatch的含义一样
forEach和forEachOrder区别:两者完成的功能类似,主要区别在并行处理上,forEachOrdered()将始终按照流(stream)中元素的遇到顺序执行给定的操作,而forEach()方法是不确定的。
sorted中可以把Comparator.comparing()的结果作为入参
Comparator.comparing().thenComparing() 可以定义多个排序规则
ajax JSON.stringify 后端要用实体类或者Map<String, Object>接收
ajax参数 contentType表示告诉服务器请求数据的类型
dataType表示告诉服务器,我要想什么类型的数据
http建立连接timeout: connect timeout
http读取数据timeout: socket timeout
Exception: e.getClass().getSimpleName()可以拿到具体异常的类型
Sets.difference(set1, set2) 可以获取元素:在set1中存在但是在set2中不存在
Set的retainAll方法:求交集
http表单格式:application/x-www-form-urlencoded
@RestController注解相当于@ResponseBody + @Controller
线程池用法:TestAsyncSend类
idea或eclipse的jvm arguments加入 -Dfuck.abc="1234" 在代码中System.getProperty("fuck.abc")可以获取这个值
-D是用来在启动java程序时设置系统属性的
接口中的default方法会被实现类直接继承
Random r = new Random() nextInt(bound) 会生成0到bound的值 每次不一样
Random r = new Random(100) 指定种子后,nextInt(bound) 会生成0到bound的值 每次都一样
ip号段:
103.107.217.0/24 表示
103.107.217.0 到 103.107.217.255
对Map的每个entry操作:
1、map.entryset().stream().foreach()
2、map.foreach()
@POST("/{indexes}/{type}/_search")
search(@Path("indexes") String indexes, @Path("type") String type, @Body RequestBody body);
@Path表示请求路径中的参数,@Body表示请求body
localDatetime和毫秒数转化
LocalDateTime end = LocalDateTime.of(2020, 3, 3, 18, 38, 00);
System.out.println(end.toInstant(ZoneOffset.of("+8")).toEpochMilli());
三个元素的if else:
SmsConstants的userIpV4InString方法
去掉字符串指定的前缀:
StringUtils.removeStart(fullNumber, "+")
//日期转化
DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date myDate2 = dateFormat2.parse("2020-04-25 05:20:01");
System.out.println(myDate2);
System.out.println(myDate2.getTime());
//如果string类型日期想转格式:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日");
Date parsedDate = formatter.parse(date);
SimpleDateFormat resultFormat = new SimpleDateFormat("yyyyMMdd");
String result = resultFormat.format(parsedDate);
// 时间戳转为年月日
SimpleDateFormat MONTH_DAY_FORMAT = new SimpleDateFormat("M月dd日");
Date date = new Date(timestamp);
String result = MONTH_DAY_FORMAT.format(date);
UrlEncode会把/变为%2f
springmvc restful接口 @PathVariable("urlTail") 这样只能映射abcd这样的,不能映射abcd/edf/hik这样多级路径的
zsh: command not found
vim ~/.zshrc
在文件末尾添加上 source ~/.bash_profile 保存即可
可以 open ~/.zshrc 然后添加source ~/.bash_profile
图片转byte数组:
String photoPath = "/home/web_server/files/jiaboyu/src/infra-id-card-verify/infra-id-card-verify-common/4115500_491115.jpg";
File photo = new File(photoPath);
FileInputStream fis = new FileInputStream(photo);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int n;
while ((n = fis.read(b)) != -1)
{
bos.write(b, 0, n);
}
fis.close();
bos.close();
byte[] buffer = bos.toByteArray();
使用IOUtils
String photoPath = "abc";
File photo = new File(photoPath);
byte[] bytes = FileUtils.readFileToByteArray(photo);
实体类转为Map形式:
obj -> jsonStr -> map
curl -X GET \
http://bjfk-rs5034.yz02:9494/rest/infra/id/card/external/enterpriselicense/Fr3TNaD%2FsYjP1%2FmNk3LENt9PXl%2FoUlcoZZ24XZl8grZgBrREI%2FdtNKB1WRxxJg%2Bs \
http请求路径中不能带%,否则会报400 bad request
curl -X GET \
http://bjfk-rs5034.yz02:9494/rest/infra/id/card/external/enterpriselicense?token=M0Hy9_h4QnAseJ_D7nSbQoWqI0wVluBYldPDCcl4-mrHYtlZcVtHrx9C6p63dr6aXR71W0olcgvNXY0hc65m-M2WJJ9aOAEoYS3OCYmxJ3U --output licen.jpg
如果某个字符串需要放入http请求参数中,如?后内容 需要把字符串经过Base64.encodeBase64URLSafeString, 否则会有字符丢失
Base64.encodeBase64URLSafeString() 方法可以实现+转化-,/转化_ 并去掉==
Base64.encodeBase64URLSafeString()和Base64.encodeBase64String() 生成的字符串 用Base64.decodeBase64() 生成的结果是一样的
?后的参数,在经过http请求时会自动urlencode编码
urlencode会把/转为%2F,+转为%2B
http的queryString中内容(即?后的内容)和www-form-urlencoded form表单中的内容,经过http请求后会自动被urlEncode编码
Base64非url safe的编码,会有+等内容,对应的解码方式是Base64.getDecoder().decode()
Base64的url safe编码,会把+,/, ==等转化,对应解码方式是Base64.decodeBase64
Base64.decodeBase64对url safe和url 非safe都是兼容的
url safe编码后 用Base64.getDecoder().decode()解码是不兼容的
对于url safe编码,不管编码多少次,都能用Base64.decodeBase64成功解码
hive解析json:https://blog.csdn.net/qq_34105362/article/details/80454697
get_json_object(response_detail, '$.body.messages[0].status') = 6
hive substr:substr(p_date, 1, 6) 表示截取第1到6位
retrofit2 Response 拿到所有response header:
Headers headersFromResponse = httpRsult.headers();
拿到指定header:
String requestId = String.valueOf(headersFromResponse.get("X-Ca-Request-Id"));
admin重启即为执行apache的startup.sh,所以可以直接执行apacha的脚本
retrofit2.http相关注解:
@QueryMap Map<String, Integer> map 可传多个参数,都以?形式拼接
如果url中?sig=... @Query("sig") 可传入一个参数,对应
@HeaderMap 表示http头部传多个参数
如果@POST("/{indexes}/{type}), @Path("indexes") String indexes 即表示传入参数
@Body 可传入requestBody
根据字符串获取枚举值:
Enums.getIfPresent(ProductPlatform.Platform.class, getValueFromRequest(request, "platform"))
.or(UNKNOWN_PLATFORM)
EnumUtils.fromValue(DrawPrizeExtParam.class, val, UNKNOWN)
peek: 不能修改流中的元素,只能对元素进行打印输出或者改变引用类型的属性。peek入参是consumer
filter(strategy -> strategy != null) 可用filter(Objects::nonNull)代替
java8 stream代替for循环:
IntStream.range(0, 100).forEach()
IntStream.rangeClosed(1, this.5).boxed() boxed表示把int转为Integer
ImageIO.read可以传入InputStream对象,生成BufferedImage即图片,参考验证码中台ShadowLineamentGeneratorTest
也可以传入file对象
IOUtils读取文件:
1、读取File
String photoPath = "abc";
File photo = new File(photoPath);
byte[] bytes = FileUtils.readFileToByteArray(photo);
2、读取inputStream
List<String> words = IOUtils.readLines(ChengYuClickCaptchaTest.class.getResourceAsStream("/words.txt"), "UTF-8")
查看mysql存储数据或图片大小:转为byte[]数组,数组长度即代表多少byte,除以1024可得出kb
scp一般利用ssh传输文件
ssh免密操作:假设本地机器为local,远程服务器为remote,用户均为user。
1、user在local机器上通过ssh-keygen生成一个非对称密钥对(假设公钥key.pub,私钥为key.pri)
2、user通过ssh命令登录user@remote(该阶段需要输入密码),并将key.pub拷贝到remote机器上的/home/user/.ssh/authorized_keys文件中(添加一行即可);该步也可通过ssh-copy-id命令直接操作
3、当user再次从local执行ssh remote的时候,不需要输入任何密码即可登录
该原理也可利用在机器之间传输文件
base64编码会让数据扩大1/3
如果api服务直接抛出异常 前端看到的状态码是500,如果用handler统一处理 会返回http200
为远程分支创建本地分支
git checkout --track origin/01-11-lzb-add-get
git checkout --track origin/jiaby_0530_draw
根据远程分支创建新的本地分支
git checkout -b newbranch origin/master
try (ByteArrayInputStream in = new ByteArrayInputStream(image))
try-with-resource可以自动关闭流
request.getParameter方法:接收post请求参数,发送端content-Type必须设置为application/x-www-form-urlencoded;否则会接收不到
maven去掉依赖:如果我们不想通过 A->B->C->D1 引入 D1 的话,那么我们在声明引入 A 的时候将 D1 排除掉,例如
<dependency>
<groupId>in.zapr.druid</groupId>
<artifactId>druidry</artifactId>
<version>2.12</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
该插件和打war包有关
Maven项目默认的资源文件夹是 src/main/resources,编译后会出现在WAR文件的target/classes 和 zt-captcha-api-1.0-SNAPSHOT/WEB-INF/classes中。WAR插件可以使用webResources参数来包含默认资源文件夹以外的资源
例如:
<webResources>
<resource>
<directory>extra-config</directory>
<targetPath>/WEB-INF/extra-config</targetPath>
</resource>
</webResources>
可以把extra-config里的内容输出到zt-captcha-api-1.0-SNAPSHOT/WEB-INF/extra-config里面
项目本地编译后会在zt-captcha-api模块的target目录下生成zt-captcha-api-1.0-SNAPSHOT,在WEB-INF目录下包含classes,extra-config,lib目录,这也是zt-captcha-api.war包解压后的结果
war插件里的
<outputDirectory>${basedir}/../target/${artifactId}</outputDirectory>
<warName>${artifactId}</warName>
可以给war命名并指定输出位置
war插件文章参考:
https://www.cnblogs.com/larryzeal/p/6181555.html
mvn -pl:在指定模块上执行
-am: 构建指定模块,同时构建指定模块依赖的其他模块
CaptchaType.forNumber() 可以根据pb枚举的数字获取验证码类型
java.io.IOException: Broken pipe
Broken pipe产生原因分析
1.当访问某个服务突然服务器挂了,就会产生Broken pipe
2.客户端读取超时关闭了连接,这时服务器往客户端再写数据就发生了broken pipe异常!(如果线程池打满,线程都处于block状态,也会产生Broken pipe
,如果向serveletResponse中写入数据时没flush,可能导致response缓冲区一直没填满,导致客户端读取超时关闭连接,产生Broken pipe)
1.问题一分析服务器为什么挂了。
2.问题二使用jps/jstack分析线程栈,看是不是有线程阻塞。
ab压测:
测试机上发压即可,本地配置环境难度较大
ab -c 400 -n 240000 -t 10 http://zt.test.gifshow.com/rest/zt/captcha/sliding/cutPic?captchaSn=Cgp6dC5jYXB0Y2hhEvkBwPfNwTAuXa-Zw3udUMG4kfjpYsOJUtvSPL57VKw24b65ODfJYBNdNgqcMNPjGO3hhuCUc-ksdNnen9P2ffcBZhYpkUm56rLJlHHwTSyfuO715jysECs14_IAZl9eZpAB4A2khTK0J87eCRwiG-k6JW5EjrDw5QWbRVb2kENFuaAdQ01UiU0x1vLUUEKhBhtDNQYoo8U_wa9ZjQa6O1D33qgxS1U2ohAp6Tj_7v1KlftBWondggvN2q3qws5sDLOiaYq-c6pZbIH1ZWjk1xLIwzQMEH15KIgr1fw3sFdTG1FG6OZqLvhQ7giohBgrQUQkyEE4w8OkEfhxGhIUZA-c6Wohnx-s4Cp9V_FC4gIoCjAC
计算机器的qps:
单线程执行时间500ms 机器上有200线程
qps:1000 / 500 * 200 = 400
qps和并发数的计算关系:
单线程执行时间200ms,并发数300,qps = 1000 / 200 * 300 = 1500
在Spring里,我们可以使用@ControllerAdvice来声明一些全局性的东西,最常见的是结合@ExceptionHandler注解用于全局异常的处理。
@ExceptionHandler注解标注的方法:用于捕获Controller中抛出的不同类型的异常,从而达到异常全局处理的目的;
@ControllerAdvice("com.cmos.edcreg.web.controller") 可以声明该类要处理的包路径
java枚举在db中存储一般用tinyint,从性能角度考虑 VARCHAR (N) 会比 TINYINT 略差,主要表现在 JOIN 和 ORDER BY 的操作上。首先 TINYINT 的字节数很小,只占 1 个字节。
引入依赖后可能会自动装配某些interceptor 导致健康检查失败等
// 查询某个表中时间最近的记录
SELECT * from package_record A, (SELECT status, max(update_time) max_update_time FROM package_record where status = 6) B
where A.status = B.status and A.update_time = B.max_update_time;
我们用Java编写的文件都是.java文件,JVM在编译项目时,会主动将.java文件编译成 .class文件。class文件才能被jvm运行。jvm会把编译好的class文件和resources目录下的文件一起放在 target/classes目录下,target/classes即classpath
getResourceAsStream方法寻找文件的起点是JAVA项目编译之后的根目录,比如一般maven项目编译之后根目录都是target/classes这个文件
当java读取resources里的文件,
1、如果在本地执行(不在服务器上执行),可以输入绝对路径读取文件
File file = new File("src/main/resources/properties/test.properties");
2、如果在服务器上读取:JavaWeb项目部署服务器中,会将项目打包成Jar包或者war包,此时就不会存在 src/main/resources 目录,resources 下的静态文件会存放在 target/classes目录下。
InputStream in = xxx.class.getResourceAsStream("/properties/test.properties");
class.getResourceAStream() 与 class.getClassLoader().getResorceAsStream() 的区别?
class 是获取当前类的 class 对象,getClassLoader()是获取当前的类加载器,什么是类加载器?简单点说,就是用来加载java类的,类加载器就是负责把class文件加载进内存中,并创建一个java.lang.Class类的一个实例也就是class对象,并且每个类的类加载器都不相同,getResourceAsStream(path)是用来获取资源的。类加载器默认是从 classPath 下获取资源的,因为这下面有class文件。
a、class.getClassLoader().getResourceAsStream(String name) 默认则是从ClassPath根下获取(文件放在resources目录下),name不能带"/",否则会抛空指针。采用相对路径, "/"就相当于当前进程的根目录,即项目根目录;
b、class.getResourceAsStream(String name) 是采用绝对路径,绝对路径是相对于 classpath 根目录的路径,"/" 就代表着 classpath,所以 name 属性需要前面加上 "/";getResourceAsStream()不带"/"时候是从当前类所在包路径去获取资源
getResourceAsStream() 方法仅仅是获取对应路径文件的输入流,在路径的用法上与getResource()一致
getResource读取资源文件详见:
https://blog.csdn.net/weixin_43291944/article/details/106802630
BuilderDemo.class.getResource("")
BuilderDemo.class.getResource("/")的输出分别是
file:/Users/jiaboyu/javacode/program-demo/target/classes/jdk8demo/
file:/Users/jiaboyu/javacode/program-demo/target/classes/
getResource("")不带"/“时候是从当前类所在包路径去获取资源
getResource("/")带”/"时候是从classpath的根路径获取
BuilderDemo.class.getClassLoader().getResource("")
BuilderDemo.class.getClassLoader().getResource("/")的输出分别是
file:/Users/jiaboyu/javacode/program-demo/target/classes/
null
getClassLoader().getResource("")不带"/“时候是从classpath的根路径获取
getClassLoader().getResource("/")带有”/"打印为null,路径中无法带有"/"
数组转为流:Arrays.stream或者Stream.of
将string类型list转为long类型list,使用mapToLong后需要使用boxed。
mapToLong只是把流转换为 LongStream 类型。
调用 LongStream.boxed 方法将LongStream转为为 Stream<Long> 类型
collect(Collectors.toList())把 Stream<Long> 转换为 List
List<Long> longList = NUMBERS_LIST.stream().mapToLong(Long::valueOf).boxed().collect(Collectors.toList());
flatMap:即对流中每个元素进行平铺后,形成多个流合在一起
-Denforcer.skip=true 可以跳过enforcer插件执行
java -D是用于设置环境属性值的重要方式。
java类转为json字符串,下列条件需要满足一个:
1、对应字段需要有set和get方法,否则该字段转化的结果为空。
2、如果没有get和set方法,可以用@JsonProperty注解,也可以达到将类转为json字符串效果
jackson在反序列化时,默认使用对象的默认构造函数,如果默认构造函数不存在,jackson会报错。
@JsonCreator注解,其作用就是,指定对象反序列化时的构造函数或者工厂方法,如果默认构造函数无法满足需求,或者说我们需要在构造对象时做一些特殊逻辑,可以使用该注解。该注解需要搭配@JsonProperty使用。
@JsonCreator
public Person(@JsonProperty("age") int age, @JsonProperty("name") String name) {
this.age = age;
this.name = name;
}
如果不使用JsonCreator,那么需要提供无参构造函数以及对应的setter方法
@JsonProperty注解作用:此注解用于属性上,作用是把该属性的名称序列化为另外一个名称。
在某字段上注释@JsonIgnore,可以在json序列化时忽略此字段
json序列化:SerializationFeature.FAIL_ON_EMPTY_BEANS的值 默认为true,该属性的意思是,如果一个对象中没有任何的属性,那么在序列化的时候就会报错
如果设置mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS),那么序列化就不会报错了。
在类上面加注解@JsonIgnoreProperties(ignoreUnknown = true) 也可以解决此错误
@JsonIgnoreProperties(value = {"name","age"}) 可以注解在类上面,意味着忽略了name和age属性,在序列化的时候,会忽略这两个属性,只序列化其他属性
@JsonIgnoreProperties(ignoreUnknown = true) 主要用在反序列化上。正常情况下,如果我们json串中有一些key值和我们的POJO对象不匹配,那么将会抛出异常。
比如json串中有个key名称为height222,pojo中字段为height111,反序列化就会抛异常。如果加上此注解则不会抛异常了。使用 mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 也可以达到同样的目的
@JsonSerialize:json序列化注解,用于字段或get方法上,作用于getter()方法,将java对象序列化为json数据
@JsonDeserialize:json反序列化注解,用于字段或set方法上,作用于setter()方法,将json数据反序列化为java对象
swagger本地启动域名:http://localhost:8080/swagger-ui.html#/
Caused by异常:https://monkeysayhi.github.io/2017/10/02/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BC%9A%E9%98%85%E8%AF%BBJava%E7%9A%84%E5%BC%82%E5%B8%B8%E4%BF%A1%E6%81%AF%E5%90%97%EF%BC%9F/
logback框架会默认加载classpath下命名为logback-spring或logback或logback-test的配置文件
<configuration>:有三个属性:
scan:当scan被设置为true时,当配置文件发生改变,将会被重新加载,默认为true
scanPeriod:检测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认为毫秒,当scan=true时这个值生效,默认时间间隔为1分钟
debug:当被设置为true时,将打印出logback内部日志信息,实时查看logback运行信息,默认为false
<root>标签:用来指定最基础的日志输出级别,也是<logger>元素,它是根logger,只有一个level属性,因为它的name就是ROOT
<appender>标签:通过使用该标签指定日志的收集策略,name属性指定appender命名,class属性指定输出策略,通常有两种,控制台输出和文件输出,文件输出就是将日志进行一个持久化。ConsoleAppender将日志输出到控制台
<filter>:通过使用该标签指定过滤策略
<rollingPolicy>:标签指定收集策略,比如基于时间进行收集
<logger>:用来设置某一个包或者具体某一个类的日志打印级别、以及指定<appender>。<logger>可以包含零个或者多个<appender-ref>元素,标识这个appender将会添加到这个logger。logger有一个name属性、一个可选的level属性和一个可选的additivity属性,表示会把name这个类的日志打出来
<logger name="org.apache.kafka" level="error" additivity="true">
<appender-ref ref="stdout"/>
</logger>
<turboFilter>中引入的类是TurboFilter的子类,可以重写decide方法,改变日志打印的级别
Filters决定日志事件能否被输出。过滤条件有三个值:ACCEPT(接受),DENY(拒绝),NEUTRAL(中立)
过滤器ACCEPT和DENY之后,后续的过滤器就不会执行了,只有在NEUTRAL的时候才会执行后续的过滤器
异步输出日志:之前的日志配置方式是基于同步的,每次日志输出到文件都会进行一次磁盘IO。采用异步写日志的方式而不让此次写日志发生磁盘IO,阻塞线程从而造成不必要的性能损耗。异步输出日志的方式很简单,添加一个基于异步写日志的appender,并指向原先配置的appender即可
SPI(Service Provider Interface)是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现。从使用人员上来说,SPI 被框架扩展人员使用。
实现:
1、定义个接口:
public interface UploadCDN {
void upload(String url);
}
2、定义实现类:
public class QiyiCDN implements UploadCDN { //上传爱奇艺cdn
@Override
public void upload(String url) {
System.out.println("upload to qiyi cdn");
}
}
public class ChinaNetCDN implements UploadCDN {//上传网宿cdn
@Override
public void upload(String url) {
System.out.println("upload to chinaNet cdn");
}
}
3、在resources目录下新建META-INF/services目录,并且在这个目录下新建一个文件,文件名是接口的全路径名称:
com.kuaishou.cny2022.tiger.infra.util.UploadCDN。
4、文件中写入接口实现类的全路径名:
com.kuaishou.cny2022.tiger.infra.util.QiyiCDN
com.kuaishou.cny2022.tiger.infra.util.ChinaNetCDN
5、调用方式:
public static void main(String[] args) {
ServiceLoader<UploadCDN> uploadCDN = ServiceLoader.load(UploadCDN.class);
for (UploadCDN u : uploadCDN) {
u.upload("filePath");
}
}
6、输出:
upload to qiyi cdn
upload to chinaNet cdn
当进程有OOM等异常出现时,jvm就会将当前的虚拟机的堆等信息放入hprof文件中, 名字是大概java_pid加上进程号
服务进程一直反复重启,可能是OOM了,需要看下每分钟gc次数的监控,并调大内存
git merge request 点revert可以去掉此次提交
MoreFunctions.catching 需要有返回值
MoreFunctions.runCatching 可以没有返回值
MoreFunctions.catchingOptional 可以对产生的异常做兜底处理 详见春节工程
Iterables.getLast可以拿到末尾节点
LoadingCache参数含义:
1、maximumSize:设定缓存项的数目的最大值,当数目空间不足时,会使用LRU策略进行回收
2、expireAfterxxx:过期逐出,例如expireAfterWrite设置为10分钟,则写入十分钟后过期
4、refreshAfterWrite:例如设置为10分钟,则10分钟内没有写操作,则刷新。在到达过期时间后,对cache进行get操作依然可以返回当前已经过期的值,同时触发load操作
5、cache加载方式:
1)load/loadAll:当获取的缓存值不存在时调用,在load操作完成前,调用者会被阻塞。如果有多个线程同时获取同一个key的操作,只会进行一次load;如果load操作失败抛出异常,调用者也会抛异常
2)reload:cache中有数据,但是设置了refreshAfterWrite行为,则会触发reload操作;如果没有设置,则reload不会被使用。 reload操作返回的是一个Future而非结果值。reload操作如果还没有完成,或者reload失败,则依然会返回先前的旧值。reload的默认行为依然是阻塞的,它直接调用了load方法。框架中有一个AsyncReloadCacheLoader是一个使用单独的线程池中执行reload操作的实现,不阻塞用户请求。
6、建议expireAfterXXX和refreshAfterWrite搭配使用,并设置refreshAfterWrite参数的时间小于expireAfterXXX参数的时间。比如refreshAfterWrite=5s,expireAfterXXX = 10s。这样在触发refresh操作的时候cache中数据还没有过期,只需一个线程执行reload
LoadingCache和ReloadableCache的区别
1、LoadingCache主要用来缓存热点数据,而ReloadableCache是用来缓存全量数据的。例:每个用户的profile页数据适合使用LoadingCache,魔表信息适合使用ReloadableCache。
2、LoadingCache有LRU策略,ReloadableCache没有缓存淘汰,所以需要关注内存开销,如果过大的话,可能会造成JVM的GC压力过大。
3、ReloadableCache可以定时刷新或者数据变更时出发通知更新,LoadingCache是请求触发数据更新。
@ExceptionHandler此注解使用在方法级别,声明对Exception的处理逻辑。可以指定目标Exception
@ResponseBody此注解用在请求handler方法上。和@RequestBody作用类似,用于将方法的返回对象直接输出到http响应中。
@RestControllerAdvice此注解用于class上,同时引入了@ControllerAdvice和@ResponseBody两个注解。
SpringBoot 的 ApplicationRunner 接口可以让项目在启动时候初始化一些信息,比如预热服务等
JsonParam参数使用条件:需要加入JsonParamAnnotationResolver,还需加入一些其他的过滤器和拦截器
泛型中ResultView<?> 问号 表示赋值的类型不确定
java computeIfAbsent:对hashMap中指定key的值进行重新计算,如果不存在这个key,则添加到hashMap中。如果存在这个key,则返回次key对应的value
HashMap<String, Integer> prices = new HashMap<>();
// 往HashMap中添加映射项
prices.put("Shoes", 200);
prices.put("Bag", 300);
prices.put("Pant", 150);
prices.computeIfAbsent("Shirt", key -> 280);
{Pant=150, Shirt=280, Bag=300, Shoes=200}
java putIfAbsent:如果所指定的key在HashMap中存在,返回和这个key对应的value, 如果所指定的key不在HashMap 中,则返回 null。
HashMap<Integer, String> sites = new HashMap<>();
// 往 HashMap 添加一些元素
sites.put(1, "Google");
sites.put(2, "Runoob");
sites.put(3, "Taobao");
// HashMap 不存在该key
sites.putIfAbsent(4, "Weibo");
sites.putIfAbsent(2, "Wiki");
此时map的结果为1=Google, 2=Runoob, 3=Taobao, 4=Weibo}
可以在类中定义Supplier,延迟加载。对于会消耗较多资源的对象:这不仅能够节省一些资源,同时也能够加快对象的创建速度,从而从整体上提升性能
private static final Supplier<PhoneNumberUtil> PHONE_NUMBER_UTIL_SUPPLIER = memoize(PhoneNumberUtil::getInstance);
web.xml配置:
创建Servlet实例有两个时机:
1、客户端第一次请求某个Servlet时,系统创建该Servlet的实例,大部分Servlet都是这种Servlet。
2、Web应用启动时立即创建Servlet实例,即load-on-start Servlet。
<servlet>
<servlet-name>zt-captcha-api-gateway</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-name>用来定义servlet的名称,该名称在整个应用中必须是惟一的
<load-on-startup>1</load-on-startup>表示启动容器时,初始化Servlet
web.xml中可以注册过滤器,比如定义了一个类TestFilter1
<filter>
<filter-name>testFilter1</filter-name>
<filter-class>com.scorpios.filter.TestFilter1</filter-class>
</filter>
<filter-mapping>
<filter-name>testFilter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
logback.xml
<property name="ASYNC_APPENDER_INCLUDE_CALLER_DATA" value="false"/> 可以禁止日志输出行号
java对象转成json成立的基本条件只需要有get方法
public class ResultView<DATA> {
public long getTimestamp() {
return System.currentTimeMillis();
}
public String getHostname() {
return HostInfo.getHostName();
}
}
则ObjectMapperUtils.toJSON(resultView)的结果是{hostname":"MacBook-Pro.local","timestamp":1650543262512},所以get方法可以把java对象可自动转为json
ResponseBodyAdvice 接口是在 Controller 执行 return 之后,在 response 返回给客户端之前,执行的对 response 的一些处理,可以实现对 response 数据的一些统一封装或者加密等操作。比如可以实现ResponseBodyAdvice接口,在实现类中获取api controller的返回值。
详见:https://www.cnblogs.com/goloving/p/15045736.html
把单个对象封装成list:Collections.singleton
ApplicationRunner接口:springboot中提供的,该接口中,只有一个run方法,他执行的时机是:spring容器启动完成之后,就会紧接着执行这个接口实现类的run方法。
http请求返回502,是因为转发问题,可能后端路由配置有问题,请求还没到后端
ksboot yml指定服务启动端口:
server:
port: 8080