Maven依赖

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.10</version>
</dependency>

注解开发

使用xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
public static void main(String[] args) {
//ApplicationContext是应用程序上下文的顶层接口,它有很多种实现,这里我们先介绍第一种
//因为这里使用的是XML配置文件,所以说我们就使用 ClassPathXmlApplicationContext 这个实现类
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); //这里写上刚刚的名字
}

使用注解

public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
}

@Configuration
public class MainConfiguration { }

@Component
public class MyClass {
@Autowired
SomeClass someClass;
}

所有的Java云平台都能够使用基于JAR的打包方式,WAR文件只在一些云平台上能够运行。

Pom.xml 更换 Maven 源

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.springframework.quoters</groupId>
<artifactId>quoters-incorporated</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>quoters-incorporated</name>
<description>REST service to support the guides</description>

<developers>
<developer>
<id>gturnquist</id>
<name>Greg Turnquist</name>
<email>gturnquist at vmware.com</email>
<organization>VMware, Inc.</organization>
<roles>
<role>Project Lead</role>
</roles>
</developer>
</developers>

<organization>
<name>VMware, Inc.</name>
<url>https://spring.io</url>
</organization>

<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<comments>
Copyright 2011 the original author or authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
See the License for the specific language governing permissions and
limitations under the License.
</comments>
</license>
</licenses>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<!-- 配置阿里云仓库 -->
<repositories>
<repository>
<id>aliyun-repos</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>aliyun-repos</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

Tight Coupling 紧耦合

在Spring框架以前,使用排序算法需要将算法实例化

public class ComplexBusinessService {
SortAlgorithm sortAlgorithm = new BubbleSortAlgorithm;
}
public class BubbleSortAlgorithm implements SortAlgorithm {...}

Good code has loose coupling.

移除依赖项的实例化可以移除紧耦合

public class ComplexBusinessService {
SortAlgorithm sortAlgorithm; // = new BubbleSortAlgorithm();

public ComplexBusinessService(SortAlgorithm sortAlgorithm) { // 创建构造函数
this.sortAlgorithm = sortAlgorithm;
}

public classBubbleSortAlgorithm implements SortAlgorithm {...}

Spring Framework instantiates objects and populates the dependencies.

阅读全文 »

基本操作

General

# 返回给定模式的keys
KEYS patter
KEYS * # 返回全部
KEYS set* # 返回set开头的keys
EXISTS key
TYPE key
DEL key

String

SET key value
GET key
# Set Extend Time
SETEX key seconds value
# Set When Key Not Exist
SETNX key value

Hash

HSET key field value
HGET key field
HDEL key field
# Get All Fields
HKEYS key
# Get All Values
HVALS key
flowchart LR
key[key]
item[
field1: value1
field2: value2
]
key --> item

List

LPUSH key value1 value2
# Get Key From Start To Stop
LRANGE key start stop
# Right POP
RPOP key
# List Length
LLEN key

Set

SADD key mem1 mem2
SMEMBERS key
# Set Size
SCARD key
SINTER key1 key2
SUNION key1 key2
# Delete
SREM key mem1 mem2

Sorted Set / ZSet

ZADD key score1 mem1 score2 mem2
# Show List
ZRANGE key start stop (WITHSCORES)
# Increse Member
ZINCRBY key increment member
ZREM key mem1 mem2
阅读全文 »

1.0 Session

有状态:用户请求接口 -> 从Session中读取用户信息 -> 根据当前的用户来处理业务 -> 返回

缺点:不支持分布式

2.0 Token

无状态:用户携带Token请求接口 -> 从请求中获取用户信息 -> 根据当前的用户来处理业务 -> 返回

<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.3.0</version>
</dependency>
  • 工具类
public class JwtUtils {
//Jwt秘钥
private static final String key = "abcdefghijklmn";

// 根据用户信息创建Jwt令牌
public static String createJwt(UserDetails user){
Algorithm algorithm = Algorithm.HMAC256(key);
Calendar calendar = Calendar.getInstance();
Date now = calendar.getTime();
calendar.add(Calendar.SECOND, 3600 * 24 * 7);
return JWT.create()
.withClaim("name", user.getUsername()) // 配置JWT自定义信息
.withClaim("authorities", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList())
.withExpiresAt(calendar.getTime()) // 设置过期时间
.withIssuedAt(now) // 设置创建创建时间
.sign(algorithm); // 最终签名
}

// 根据Jwt验证并解析用户信息
public static UserDetails resolveJwt(String token){
Algorithm algorithm = Algorithm.HMAC256(key);
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
try {
DecodedJWT verify = jwtVerifier.verify(token); // 对JWT令牌进行验证,看看是否被修改
Map<String, Claim> claims = verify.getClaims(); // 获取令牌中内容
if(new Date().after(claims.get("exp").asDate())) // 如果是过期令牌则返回null
return null;
else
// 重新组装为UserDetails对象,包括用户名、授权信息等
return User
.withUsername(claims.get("name").asString())
.password("")
.authorities(claims.get("authorities").asArray(String.class))
.build();
} catch (JWTVerificationException e) {
return null;
}
}
}
  • Filter
public class JwtAuthenticationFilter extends OncePerRequestFilter {  
// 继承OncePerRequestFilter表示每次请求过滤一次,用于快速编写JWT校验规则

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 首先从Header中取出JWT
String authorization = request.getHeader("Authorization");
// 判断是否包含JWT且格式正确
if (authorization != null && authorization.startsWith("Bearer ")) {
String token = authorization.substring(7);
// 开始解析成UserDetails对象,如果得到的是null说明解析失败,JWT有问题
UserDetails user = JwtUtils.resolveJwt(token);
if(user != null) {
// 验证没有问题,那么就可以开始创建Authentication了,这里我们跟默认情况保持一致
// 使用UsernamePasswordAuthenticationToken作为实体,填写相关用户信息进去
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 然后直接把配置好的Authentication塞给SecurityContext表示已经完成验证
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
// 最后放行,继续下一个过滤器
// 可能各位小伙伴会好奇,要是没验证成功不是应该拦截吗?这个其实没有关系的
// 因为如果没有验证失败上面是不会给SecurityContext设置Authentication的,后面直接就被拦截掉了
// 而且有可能用户发起的是用户名密码登录请求,这种情况也要放行的,不然怎么登录,所以说直接放行就好
filterChain.doFilter(request, response);
}
}
  • Security修改为无状态
// 将Session管理创建策略改成无状态,这样SpringSecurity就不会创建会话了,也不会采用之前那套机制记录用户,因为现在我们可以直接从JWT中获取信息
.sessionManagement(conf -> {
conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
})
// 添加我们用于处理JWT的过滤器到Security过滤器链中,注意要放在UsernamePasswordAuthenticationFilter之前
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)

JWT退出登录

采用黑名单方案。一台服务器存储JWT黑名单,共享给所有微服务。

JWT.create()
// 额外添加一个UUID用于记录黑名单,将其作为JWT的ID属性jti
.withJWTId(UUID.randomUUID().toString())
public class JwtUtils {    

private static final HashSet<String> blackList = new HashSet<>();
// 加入黑名单方法
public static boolean invalidate(String token){
Algorithm algorithm = Algorithm.HMAC256(key);
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
try {
DecodedJWT verify = jwtVerifier.verify(token);
Map<String, Claim> claims = verify.getClaims();
//取出UUID丢进黑名单中
return blackList.add(verify.getId());
} catch (JWTVerificationException e) {
return false;
}
}

public static UserDetails resolveJwt(String token){
Algorithm algorithm = Algorithm.HMAC256(key);
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
try {
DecodedJWT verify = jwtVerifier.verify(token);
// 判断是否存在于黑名单中,如果存在,则返回null表示失效
if(blackList.contains(verify.getId()))
return null;
Map<String, Claim> claims = verify.getClaims();
if(new Date().after(claims.get("exp").asDate()))
return null;
return User
.withUsername(claims.get("name").asString())
.password("")
.authorities(claims.get("authorities").asArray(String.class))
.build();
} catch (JWTVerificationException e) {
return null;
}
}
}

上传文件

scp ./upload/path/file.postfix user@host.com:/path/to/file
# 文件夹
scp -r ./upload/path/folder user@host.com:/path/to/folder

下载文件

scp user@host.com:/path/to/file.postfix ./download/path
# 文件夹
scp -r user@host.com:/path/to/folader ./download/path

基本操作

  • <C-o>, <C-i>:回到前一个/后一个位置(例如,打开文件默认在第一行,<C-o>回到上次编辑位置)。注意这个操作是跨文件的
  • `0:返回上次位置
  • 词:w(下一个词),b(词初),e(词尾)
  • 行:0(行初),^(第一个非空格字符),$(行尾)
  • 文件:gg(文件头),G(文件尾)
  • 搜索:/{正则表达式},n/N用于导航匹配
  • x删除字符(等同于dl
  • s(substitute)替换字符(等同于xi
    • 替换命令:{作用范围}s/{目标文本}/{替换文本}/{替换标志}
    • :%s/s_content/o_content/g 全局替换
Sign Range
% 整个文件
. 当前行
$ 最后一行
,n 当前行到n行
n, n行到当前行
+n 当前行后n行
  • 可视化模式 + 操作
    • 选中文字,d删除(剪切) 或者c改变
  • u撤销,<C-r>重做
  • y复制 / “yank” (其他一些命令比如d也会复制)
  • p粘贴
    • +p 粘贴系统剪贴板
  • 更多值得学习的:
    • :<line> 跳到line行
    • ~改变字符的大小写
    • 3w向前移动三个词
    • A(大写)可以迅速定位到行尾进行修改
    • :!python prog.py 使用!直接运行shell命令
  • %匹配括号

多行操作

<C-v> 选中多行,Shift + i输入后Esc,即可多行同步输入

录制宏

命令模式下,

  1. q<marcoName>进行录制,<marcoName>是宏的名字,例如qa
  2. 执行一系列操作后,再次按q结束录制

使用宏

命令模式下,使用@<marcoName>即可执行;使用5@<marcoName>可以重复执行宏

文件操作

  • :e filename切换到filename文件
  • :bn/bp切换到下/上个文件
  • <C-x><C-f> 自动补全路径

0.0 PTA实录

0.1.1 最大子列和plus

  • 如何利用语句先后顺序记录上一条信息pre_start_tag
void sum_max_sequence(void)
{
long long int this_sum, max_sum;
int start_tag, end_tag, pre_start_tag, neg_tag;

start_tag = pre_start_tag = end_tag = 0;
this_sum = max_sum = 0;
neg_tag = 1;

for (int i = 0; i < num; i++)
{
if (list[i] >= 0) {
neg_tag = 0;
}
this_sum += list[i];
if (this_sum > max_sum)
{
max_sum = this_sum;
end_tag = i;
pre_start_tag = start_tag; // 我没有想到的代码!
}
if (this_sum < 0)
{
this_sum = 0;
start_tag = i+1;
}
}


if(neg_tag) // 输出0,第一个和最后一个数
printf("%lld %d %d\n", max_sum, list[0], list[num-1]);
else if (max_sum == 0)
{
printf("0 0 0\n");
}

else
printf("%lld %d %d\n", max_sum, list[pre_start_tag], list[end_tag]);
}
阅读全文 »

Shortcuts 快捷键

  • CTRL+N|P 下、上一条命令
  • CTRL+A|E 跳到行首、行尾
  • ALT+F|BCTRL+←|→ 下、上一个单词(右ALT开始使用~)
  • CTRL+W 删除前面的单词
  • CTRL+D|H 删除一个字符(D向后删除=Delete,H向前删除=Backspace)
  • CTRL+U 删除整行
  • CTRL+R 搜索整行,Esc退出

Proxy

export https_proxy=http://host:port;
export http_proxy=http://host:port;
export all_proxy=socks5://host:port;

Bash prompt string配置

使用Windows CMD比使用MinTTY更快!

  • PS(prompt string): 是命令行的默认显示文本,如:
username@hostname /work_directory
$
  • 可以在~/.bash_profile.bashrc中修改默认显示(Windows Git Bash是git-prompt.sh
# Windows Git Bash默认配置,其中\007前面的$TITLEPREFIX和$PWD是标签栏的内容,git_ps1会显示git工作分支
export PS1="\[\e]0;$TITLEPREFIX:$PWD\007\]\n\[\e[32m\]\u@\h \[\e[35m\]$MSYSTEM \[\e[33m\]\w\[\e[36m\]`__git_ps1`\[\e[0m\]\n$ "
# 显示效果
username@hostname MINGW /work_directory
$

# 简洁配置,将\h换成了指定文本,保留了命令行前的换行,将标签名改为当前目录
export PS1="\[\e]0;\W\007\] \n\[\e[32m\]\u@Host \[\e[33m\]\w\[\e[36m\]`__git_ps1`\[\e[0m\]\n$ "
# 显示效果
username@Host /work_directory
$
  • Windows虚拟环境后不会换行:设置venv/Scripts/activate
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1-}"
PS1="\n(${VIRTUAL_ENV_PROMPT}) ${PS1-}" # 添加换行符
export PS1
fi
  • MSYS2显示git prompt
# etc/profile.d/git-prompt.sh
if test -f /etc/profile.d/git-sdk.sh
then
TITLEPREFIX=SDK-${MSYSTEM#MINGW}
else
TITLEPREFIX=$MSYSTEM
fi

if test -f ~/.config/git/git-prompt.sh
then
. ~/.config/git/git-prompt.sh
else
PS1='\[\033]0;$TITLEPREFIX:$PWD\007\]' # set window title
PS1="$PS1"'\n' # new line
PS1="$PS1"'\[\033[32m\]' # change to green
PS1="$PS1"'\u@\h ' # user@host<space>
PS1="$PS1"'\[\033[35m\]' # change to purple
PS1="$PS1"'$MSYSTEM ' # show MSYSTEM
PS1="$PS1"'\[\033[33m\]' # change to brownish yellow
PS1="$PS1"'\w' # current working directory
if test -z "$WINELOADERNOEXEC"
then
GIT_EXEC_PATH="$(git --exec-path 2>/dev/null)"
COMPLETION_PATH="${GIT_EXEC_PATH%/libexec/git-core}"
COMPLETION_PATH="${COMPLETION_PATH%/lib/git-core}"
COMPLETION_PATH="$COMPLETION_PATH/share/git/completion"
if test -f "$COMPLETION_PATH/git-prompt.sh"
then
. "$COMPLETION_PATH/git-completion.bash"
. "$COMPLETION_PATH/git-prompt.sh"
PS1="$PS1"'\[\033[36m\]' # change color to cyan
PS1="$PS1"'`__git_ps1`' # bash function
fi
fi
PS1="$PS1"'\[\033[0m\]' # change color
PS1="$PS1"'\n' # new line
PS1="$PS1"'$ ' # prompt: always $
fi

MSYS2_PS1="$PS1" # for detection by MSYS2 SDK's bash.basrc

# Evaluate all user-specific Bash completion scripts (if any)
if test -z "$WINELOADERNOEXEC"
then
for c in "$HOME"/bash_completion.d/*.bash
do
# Handle absence of any scripts (or the folder) gracefully
test ! -f "$c" ||
. "$c"
done
fi

# ~/.bashrc || ~/.bash_profile
shopt -q login_shell || . /etc/profile.d/git-prompt.sh
syntax description
\u username
\h hostname
\e set colors
\w work directory
\W current diretory
\n line feed
阅读全文 »

网络搜索

网站 URL 备注
哔哩哔哩 https://search.bilibili.com/all?keyword={query}
知乎 https://www.zhihu.com/search?type=content&q={query}
百度贴吧 https://tieba.baidu.com/f?ie=utf-8&kw={query}&fr=search 优先搜索吧名
小红书 https://www.xiaohongshu.com/search_result?keyword={query}&source=web_search_result_notes 需要登录
淘宝 https://s.taobao.com/search?q={query}&commend=all&search_type=item&sourceId=tb.index&ie=utf8
京东 https://search.jd.com/Search?keyword={query}&enc=utf-8
咸鱼 https://www.goofish.com/search?q={query}
Yandex https://yandex.com/search/?text={query}
GitHub https://github.com/search?q={query}&type=repositories
tldr https://tldr.inbrowser.app/pages/common/{query} mannual查Linux命令
必应翻译 https://cn.bing.com/translator?ref=TThis&text={query}&from=en&to=zh-Hant 英译中
DeepL翻译 https://www.deepl.com/translator#en/zh/{query} 英译中
Wiki外部网站 https://encyclopedia.thefreedictionary.com/{query}
求闻百科 https://www.qiuwenbaike.cn/wiki/{query}
Wolfram|Alpha https://www.wolframalpha.com/input?i={query}&lang=zh
  • 有时候站内搜索没有搜索引擎准确,可以使用搜索引擎过滤网站
    过滤站点:
http://<ENGINE.URL>/search?text=site%3A<SITE.URL>%20{query}

启动应用

功能 路径 参数
当前目录启动终端 wt.exe -d “{current_folder}”
Golden Dict查词 GoldenDict.exe -s {query}
启动ipython wt.exe ipython
查看网络连接 control.exe ncpa.cpl
编辑环境变量(需要管理员权限) rundll32 sysdm.cpl,EditEnvironmentVariables

信息系统架构

  • 信息化系统:前台 + 后台 + 数据库
  • B/S 模式
  • 前端 -> Server[Apache->PHP(TP6.0)] -> SQL DataBase
  • 运行逻辑:
    • Client发送require请求(http协议),包含header(length、refer信息)、body(数据)
    • Apache接受请求,给PHP做处理
    • PHP读取、处理数据;判断数据是否损毁、是否需要退回
    • 数据更新到前端,并返回结果(success/fail)
阅读全文 »
0%