0%

实践知识整理

知识内容

WebServer

什么是HTTP请求头/响应头

1)请求(客户端->服务端[request])
GET(请求的方式) /newcoder/hello.html(请求的目标资源) HTTP/1.1(请求采用的协议和版本号)
Accept: /(客户端能接收的资源类型)
Accept-Language: en-us(客户端接收的语言类型)
Connection: Keep-Alive(维护客户端和服务端的连接关系)
Host: localhost:8080(连接的目标主机和端口号)
Referer: http://localhost/links.asp(告诉服务器我来自于哪里)
User-Agent: Mozilla/4.0(客户端版本号的名字)
Accept-Encoding: gzip, deflate(客户端能接收的压缩数据的类型)
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT(缓存时间)
Cookie(客户端暂存服务端的信息)
Date: Tue, 11 Jul 2000 18:23:51 GMT(客户端请求服务端的时间)

2)响应(服务端->客户端[response])
​ HTTP/1.1(响应采用的协议和版本号) 200(状态码) OK(描述信息)
​ Location: http://www.baidu.com(服务端需要客户端访问的页面路径)
​ Server:apache tomcat(服务端的Web服务端名)
​ Content-Encoding: gzip(服务端能够发送压缩编码类型)
​ Content-Length: 80(服务端发送的压缩数据的长度)
​ Content-Language: zh-cn(服务端发送的语言类型)
​ Content-Type: text/html; charset=GB2312(服务端发送的类型及采用的编码方式)
​ Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT(服务端对该资源最后修改的时间)
​ Refresh: 1;url=http://www.it315.org(服务端要求客户端1秒钟后,刷新,然后访问指定的页面路径)
​ Content-Disposition: attachment; filename=aaa.zip(服务端要求客户端以下载文件的方式打开该文件)
​ Transfer-Encoding: chunked(分块传递数据到客户端)
​ Set-Cookie:SS=Q0=5Lb_nQ; path=/search(服务端发送到客户端的暂存数据)
​ Expires: -1//3种(服务端禁止客户端缓存页面数据)
​ Cache-Control: no-cache(服务端禁止客户端缓存页面数据)
​ Pragma: no-cache(服务端禁止客户端缓存页面数据)
​ Connection: close(1.0)/(1.1)Keep-Alive(维护客户端和服务端的连接关系)

Lambda表达式

1.匿名内部类

匿名内部类也就是没有名字的内部类,正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写

但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

实例1:不使用匿名内部类来实现抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
abstract class Person {
public abstract void eat();
}

class Child extends Person {
public void eat() {
System.out.println("eat something");
}
}

public class Demo {
public static void main(String[] args) {
Person p = new Child();
p.eat();
}
}

运行结果: eat something

可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用

但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?

这个时候就引入了匿名内部类

实例2:匿名内部类的基本实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
abstract class Person {
public abstract void eat();
}

public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}

运行结果: eat something

可以看到,我们直接将抽象类Person中的方法在大括号中实现了

这样便可以省略一个类的书写,并且,匿名内部类还能用于接口上

实例3:在接口上使用匿名内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Person {
public void eat();
}

public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}

运行结果: eat something

由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现

2.Lambda表达式

对于单方法接口,即一个接口只定义了一种方法,我们可以只写出方法定义:

即简化匿名内部类的方式,其核心是一个接口实现类的覆盖重写,返回一个接口对象

1
2
3
4
5
6
7
8
9
10
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();

//简化后
Person p = ()->System.out.println("eat something");
p.eat();

3.联系与区别

  • 匿名内部类可以为任意接口创建实例——不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可;但 Lambda 表达式只能为函数式接口创建实例。
  • 匿名内部类可以为抽象类甚至普通类创建实例;但 Lambda 表达式只能为函数式接口创建实例。
  • 匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但 Lambda 表达式的代码块不允许调用接口中定义的默认方法。

MySQL

图谱

img

Data Manipulation Language

连接词:

1
2
3
4
5
select * from Table where
column like 'name_%'
column between '100' and '150'
column is null
in (column1,column2...)

通配符:

1
2
3
4
5
%:任意字符
_:任意单个字符
[ ]:指定范围 ([a-f]) 或集合 ([abcdef]) 中的任何单个字符
[^]:不属于指定范围 ([a-f]) 或集合 ([abcdef]) 的任何单个字符
escape '\':转义,select * from table where name like 'user\_' escape '\'

限定词:

1
2
3
4
5
6
distinct:唯一值
order by:排序规则,desc/asc
group by:分组.having group_conditon
limit nums:限制个数
offset nums:从第几个开始
Is Not/Is NULL:判空

多表查询:

1
2
3
4
table A 
inner/left/right/full
outer join Table B
on conditions

函数:

1
2
3
4
单行函数:
字符函数、数值函数、日期函数、转换函数、通用函数
组合函数:
count、min、max、avg、sum

Data Define Language

库的管理

用于定义数据库的三级结构,包括外模式、概念模式、内模式及其相互之间的映像,定义数据的完整性、安全控制等约束

表的管理

1
2
3
4
5
6
7
8
create table if not exits MyTable{
column1 DATATYPE TABLECONSTRAINT DEFAULT default_value,
column2 DATATYPE TABLECONSTRAINT DEFAULT default_value,
}
DATATYPE:
Integer,boolean
TABLECONSTRAINT:
primary keys,autoincrement,unique,not null,check,foreign key
1
2
3
4
alter table MyTable
add column datatype tableconstraint default default_value
drop column_to_be_deleted
③ rename to new_table_name
1
drop table if exists MyTable

Data Control Language

数据库控制语言:授权,角色控制等

  • GRANT – 为用户赋予访问权限

  • REVOKE – 撤回授权权限

Transaction Control Language

事务的概念:要么全部执行,要么全部不执行。
​事务的acid属性:atomicity、consistency、isolation、durability
​事务的创建:隐式事务、显示事务。事务具有明显的开启和结束标记。

工具使用

1. GitFlow

可以在idea里面直接看到流程:提交需要反复确认,然后进行拉取

image-20210818173247374

name Function
master 存放随时可供生产环境中的部署的代码
develop 存放当前最新开发成果的分支,当代码足够稳定时可以合并到master分支上去。
feature 开发新功能使用,最终合并到develop分支或抛弃掉
release 做小的缺陷修正、准备发布版本所需的各项说明信息
hotfix 代码的紧急修复工作

查看Git工作区、暂存区的变更情况(可以知道哪些没有commit、哪些没有被Git追踪):git status

拉取远程最新的变更到本地:git fetch

切换分支:git checkout 分支名

将代码还原到某个版本(包括工作目录):git reset --hard 版本号

查看Git的提交(commit)记录:git log

将代码还原到某个版本后,后悔了,想重新回去,但在提交记录已经找不到了。git reset --hardreset 之后的 commit都给抹杀掉了。找到最近的执行Git命令:git reflog

还原到某个版本了,现在我为了稳健,不想再原来的分支上修改了,再新建一个分支吧(-b 参数把当前分支切换到了要创建的分支上):git checkout -b 分支名

我们把上一次还是”相对稳健“的分支合并到我新建的分支上:git merge 分支

突然想看看现在有多少个分支:git branch -a

新增几个文件了,随手git add一下吧

改得差不多了,随手git commit -m一下吧,最好还是写好备注,不然以后等改多了,你都不知道你改了什么啦。

改完了,提交到远程吧:git push

想把远程分支最新的代码给拉下来,然后合并到本地上。我们可以用git fetchgit merge来实现,也可以通过git pull来实现。一般我用的都是git fetch+git merge,这样会更加可控一些

有的时候,本地分支在master分支,然后忘了切其他的分支去修改,直接在master改了,然后也push到远程了。等你发现的时候,你会真的想骂自己。

咋办?最简单的办法其实我们还是可以git reset --hard到对应的版本,然后将其修改或者复原,再强制提交到master分支:git push -u origin/master -f

2. idea 插件

2.1 RestfulToolkit—RESTful服务开发

  • 2.1.1 根据 URL 直接跳转到对应的方法定义 ( Ctrl \ or Ctrl Alt N );
  • 2.1.2 提供了一个 Services tree 的显示窗口;
  • 2.1.3 一个简单的 http 请求工具;
  • 2.1.4 在请求方法上添加了有用功能: 复制生成 URL;复制方法参数…
  • 2.1.5 其他功能: java 类上添加 Convert to JSON 功能,格式化 json 数据 ( Windows: Ctrl + Enter; Mac: Command + Enter )。

2.2 快捷键

Ctrl+Shift+Enter 完善整条语句、分号、if语句等

Ctrl+W 扩大选取范围

Ctrl+F8 打断点

Shift+F9 debug

F7 F8 F9 进入方法、下一步、下个断点

ctrl+T 直接拉取git更新

ctrl+alt+shift+C copy reference

用户评级 项目

PageInfo

1
2
Request URL: 
/api/audiences?orderColumn=createTime&orderType=desc&page=1&pagesize=30&readState=READ_SUCCESS

读取过程 : PageInterceptor.class

1
2
3
4
PageInfo page = new PageInfo();
page.setCurrent(this.getIntValue(request, "page", 1));
page.setPagesize(this.getIntValue(request, "pagesize", annotation.size()));
String orderColumn = request.getParameter("orderColumn");

项目组件

  • parent : crius

  • 被dependencyManagement管理版本号的组件 :

    opencv、log4j、reflections 、 lombok 、compress 、commons-jcs-core 、kryo 、httpclient 、mapstruct 、aws-java-sdk-s3 、com.amazonaws 、jmespath-java 、commons-collections 、spring-cloud-dependencies 、dependencies

  • har-manager模块组件:

    spring-cloud-starter-openfeign 、spring-cloud-starter-netflix-hystrix 、spring-boot-starter-cache 、ehcache、crius-spring-boot-starter 、mybatis 、crius-oss-amazon-s3-spring-boot-starter 、crius-oss-azure-storage-spring-boot-starter 、jmespath-java 、commons-collections、spring-boot-starter-actuator 、spring-boot-starter-rabbitmq 、spring-cloud-starter-alibaba-nacos-config

Oauth授权

1.网关拦截

由devops平台上的网关决定redirect地址,Oauth进行授权鉴权,如没有权限,用户拿着token去访问第三方平台

注:微服务技术解决方案下的,网关至少需要具备图示基本功能。

  1. 网关作为单点入口,完成统一的请求管理
  2. 免去客户端直接对接众多微服务的复杂性,采用单点入口,实现路由转发,从而实现服务调用
  3. 服务对于整个系统来讲,是不稳定的,那么网关,需要进行限流熔断,保持系统的稳定与分区容错性
  4. 对于服务调用的链路,网关有职责进行记录,日志监控,保证整个系统,在监控下工作
  5. 系统可能不仅仅是由自有客户端调用,很多时候,系统开放能力API给外部,因此网关需要安全认证,来保证安全

2.Security授权

Security通过authLoginFilter同步hac用户信息

Maven依赖管理

  • 父pom需要添加<packaging>pom</packaging>

  • 父pom需要用<modules><module>子module名</module></modules>注明子module有哪些。

  • 父pom声明依赖时<dependencies>外要嵌套<dependencyManagement>才能被子pom继承到,我就是忘了这点。

  • 子pom需要通过<parent></parent>指定父项目,声明依赖时就默认会用父pom中的版本了。

项目创建顺序

业务 - > 授权 - > 性能优化

DDD

  • entity\dao\mapper\vo写在repository中
  • service\特有的entity写在特定的module中

MyBatis枚举类映射

mybatis默认的枚举类型处理器 :

  • EnumTypeHandler
    mybatis的默认枚举类型处理器,将枚举类型的name持久化到数据库;

  • EnumOrdinalTypeHandler
    mybatis原生支持的另一种枚举类型处理器,将枚举类型的索引序号持久化到数据库,需要全局配置或者在需要的字段上单独配置;

mybatis配置全局默认枚举类型处理器 : defaultEnumTypeHandler

  • mybatis在3.4.5及之后版本中,新增了一个指定全局默认枚举类型处理器的配置项 : default-enum-type-handler
    在mybatis-config.xml中添加如下配置即可使自定义处理器全局生效,解决了之前新增枚举都需要单独配置的烦恼;

image-20210910184840551

image-20210910184912954

使用TypeHandler将List集合数据存入数据库

https://www.dtmao.cc/news_show_785309.shtml

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
@MappedJdbcTypes(value=JdbcType.VARCHAR)
@MappedTypes(List.class)
public class ListToVarchar implements TypeHandler<List<String>> {

/**
* 遍历List类型的入参,转换为JSON格式,使用Statement对象插入数据库
*/
@Override
public void setParameter(PreparedStatement ps, int i, List<String> objectList, JdbcType jdbcType) throws SQLException {
if(objectList.isEmpty()){
ps.setString(i,null);
return;
}
String s = JSON.toJSONString(objectList);
ps.setString(i,s);
}

/**
* 获取String类型的结果,使用parseObject将json对象转换为java对象
*/
@Override
public List<String> getResult(ResultSet resultSet, String s) throws SQLException {
return JSON.parseObject(resultSet.getString(s),new TypeReference<List<String>>() {});
}

/**
* 获取String类型的结果,使用parseObject将json对象转换为java对象
*/
@Override
public List<String> getResult(ResultSet resultSet, int i) throws SQLException {
String s = resultSet.getString(i);
return JSON.parseObject(s,new TypeReference<List<String>>() {});

}

/**
* 获取String类型的结果,使用parseObject将json对象转换为java对象
*/
@Override
public List<String> getResult(CallableStatement callableStatement, int i) throws SQLException {
String s = callableStatement.getString(i);
return JSON.parseObject(s,new TypeReference<List<String>>() {});
}

}

开发避坑

1.判空

  • Constant.equals(Variables);

  • Java中判断list为空(CollectionUtils.isEmpty)等同于 (list==null&&list.isEmpty())

2.typeHandler匹配问题

  • 使用typeHandler去数据库中查询匹配时,注意序列化和反序列化的问题,需要二者hashcode完全一致,所以最好还是从数据库中找出json,映射为实体对象,在判断是否匹配
  • 错误示例: select语句中,即使数据库中json内容一致,但仍旧不能匹配
  • image-20211116161549936

隐私条款项目

1.定时任务

- @SchedulerLock注解:为方法加上锁。

name属性(锁的名称)必须指定,每次只能执行一个具有相同名字的任务。

- lockAtMostFor属性,指定执行节点死亡时应该保留锁的时间。

设置锁的最大持有时间,为了解决如果持有锁的节点挂了,无法释放锁,其他节点无法进行下一次任务。设置了最大持有时间,当持有时间到了自动释放锁,不影响下一次执行。

- lockAtLeastFor属性,指定保留锁的最短时间。

主要目的是在任务非常短的且节点之间存在时钟差异的情况下防止多个节点执行。这个属性是锁的持有时间。设置了多少就一定会持有多长时间,在此期间,下一次任务执行时,其他节点包括它本身是不会执行任务的。

2.隐私条款模板

接口文档

  • 查看模板 GET /api/templates
  • 新建模板 POST /api/templates
  • 编辑模板 PATCH /api/templates/{templateId}
  • 删除模板 DELETE /api/templates/{templateId}

3.校验

  • @Unique name字段一致,表示绑定在一起校验image-20211104145855450

4.特殊用法

  • ```java
    /**
    • 则在路径中悄悄接收projectIds
    • /
      @JsonIgnore
      private List projectIds;
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10

      - 通过一系列Id查找

      ```xml
      <select id="getClauses" resultType="long">
      select id from def_privacy_clause where project_id in
      <foreach collection="list" item="id" open="(" close=")" separator=",">
      #{id}
      </foreach>
      </select>

3.部署踩坑

(1)消息已发,计算端没有日志

  • 检查MQ配置

  • 检查消费者

  • 计算端磁盘已满