代码 & 开发
- 启动springboot项目jar包时给main方法传入参数,根据参数执行不同的方法,可在main方法的形参String[] args中获取
java -jar xxx.jar hello java
public static void main(String[] args) throws Exception {
SpringApplication.run(VDSRVDSTATApplication.class, args);
System.out.println(args[0]);
System.out.println(args[1]);
}
- MVN打包跳过测试的命令:mvn package -DskipTests 或者直接点击IDEA中maven按钮“蓝色闪电”图标
- 将仓库地址动态化放在 标签中,打包时可直接使用命令 mvn package -Drepository.url=XXX 设置仓库
- 记一次打包失败:maven异常信息:No Sources to Compile。 原因:项目文件结构错误,java包和resources包没有放在main包下
- 使用IDEA打包失败时,首先尝试删除配置的仓库中的依赖,如果不行在CMD中进入项目所在文件使用Maven命令打包:
mvn clean package -Dmaven.test.skip=true -Dproject.version=0.0.1 - 当服务需要进行HTTPS连接时,需要在服务启动时设置证书。可使用KeyTool生成假证书
- 启动加载类:在Spring容器初始化完毕之后执行重写的 run 方法
- 实现ApplicationRunner接口:run方法形参进行了封装是 ApplicationArguments args
- 实现CommandLineRunner接口:run方法形参是字符串:String…args
- 实现接口的类都需要添加 @Component 注解,且都可配合 @Order 注解指定执行顺序,数字越小优先级越高。
- DateFormat不需要手动指定格式 'yyyy-HH-dd’等格式
DateFormat format1 = DateFormat.getDateInstance(); //日期格式,精确到日
DateFormat format2 = DateFormat.getDateTimeInstance(); //可以精确到时分秒
DateFormat format3 = DateFormat.getTimeInstance(); //只显示出时分秒
- SimpleDateFormat通常只需要定义一次但线程不安全:在调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改。
- 解决方式1:使用1.8的 DateTimeFormatter 配合 LocalDateTime ,即使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat
- 解决方式2:使用ThreadLocal给每个线程提供独立的对象副本,解决线程安全问题。
- 异常打印:直接使用 e.printStack() 时记录的堆栈信息太多,占用太多内存,且日志交错混合不易读
- 解决方式:使用 logger.error(各类参数或者对象toString + “_” + e.getMessage(), e)
- RateLimiter rateLimiter = RateLimiter.create(2); 定义一个令牌桶,每秒产生2个令牌,可用来实现每秒最多处理2个请求
- String.format的使用 (%s 字符串类型 %d十进制整数 %f 浮点数)
- 将十进制数转成8位的二进制数 0补位:String.format("%08d",Integer.valueOf(Integer.toBinaryString(value)))
- 保留两位小数四舍五入:String.format("%.2f",value)
- 正数或者负数添加符号:String.format(“%+d”,value)
- Integer.valueOf(“num”, X) 把X进制转换为十进制 ; Integer.parseInt() 和 valueOf 作用类似:parseInt转为基本类型int ,valueOf转为Integer包装类型
- 类中的序列化ID的作用:在反序列化的时候需要校验序列化ID是否一致,用于验证版本即判断对象内部是否修改。
原理:java可以将一个对象序列化 经过远程传输,在另一端进行反序列化 重新得到原始数据;序列化id存在的意义 差不多等于锁的version,用于验证版本即判断对象内部是否修改。如果不显式地声明一个serialVersionUID,系统也会隐性地内置,声明出来方便出错时排查 - 创建线程池时一定要指定ThreadFactory名字
public class ThreadPoolUtils {
private ThreadPoolUtils() {}
public static ThreadFactory getThreadFactory(String threadName) {
return new VdsRvdThreadFactory(threadName);
}
private static class VdsRvdThreadFactory implements ThreadFactory {
private final String preFix;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private VdsRvdThreadFactory(String preFix) {
this.preFix = preFix;
}
@Override
public Thread newThread(@NotNull Runnable r) {
return new Thread(r, preFix + "-" + threadNumber.getAndIncrement());
}
}
}
- 当需要重定向时:接口方法返回值类型是String、返回值前缀 “redirect:" ,所在类不能用@RestController修饰应该是@Controller
- 定时任务实现方式
- @Scheduled:不支持对“年”的设置,方便直接静态定义cron表达式
- Quartz:支持设置”年“的周期,可动态配置cron表达式(可设置startAt/endAt)
- ThreadPoolTaskScheduler:直接线程池开启周期任务,直接@Autowired可使用(也可自定义参数)
@Schedule使用的是spring的任务调度线程池ThreadPoolTaskScheduler,ThreadPoolTaskScheduler底层使用的为JDK自带的ScheduledExecutorService(其实现类为ScheduledThreadPoolExecutor)。执行任务时线程池默认只有一个线程,多个任务时串行。
实现多个任务之间并行执行:配置ThreadPoolTaskScheduler的@schedule+@Async
@Scheduled支持SpringEL表达式:@Scheduled(cron = “${schedule.cycle.history}”)
- Windows关闭指定端口任务:任务管理器—性能—资源监视器(左下)—网络—找到端口对应的PID—返回到进程—右键展示PID—找到目标进程
- 使用Gson转换成List时 使用 TypeToken:gson.fromJson(bean, new TypeToken<List>() {}.getType())
- 读取本地 JSON 文件
- 引入apache.commom包,直接 FileUtils.readFileToString(file,“UTF-8”)
- 使用 InputStreamReader 和 StringBuffer: Reader reader = new InputStreamReader(new FileInputStream(new File(fileName)),“utf-8”)
组件和框架
- Debug kafka消费服务时间过长导致kafka未及时提交commit,引起消息重发
- 主表 left join on 从表时,如果 on 连接的字段存在主表:从表 = 1:N 时,连接查询的结果数量会大于主表数量,可在sql最后添加group by 来保证数据唯一
- Jenkins打包build成功但部署失败,可以考虑是否磁盘已满或者网络问题。查看磁盘占用:df -h
- Jenkins核心配置:Git仓库地址(需要认证)、build构建jarb包的命令(一般是执行Shell脚本)
- kafka 命令:在apache-kafka_*\bin\windows目录下开启cmd窗口
在这里插入代码片
- SQL Server的 CONVERT 函数可以用不同的格式显示日期/时间数据
- SQL Server 正则表达式查询数字开头:select t.ID from tableName t where t.ID LIKE ‘[0-9]%’
- 当数据库里的值可能为null时,注意在查询时添加 null 值判断 。避免使用包装类型封装时值为null,并引发操作数据时空指针的问题
- MySQL:ifnull(字段,0)
- SQL Server :isnull(字段,0)
- SQL根据小时进行分组 group by DATE_FORMAT(
start_time
,’%H’) 或者 hour(beg_time
) - 对 datetime 做日期范围判断时:date_format(DATE_TIME,’%Y-%m-%d’) BETWEEN ‘2020-01-01’ and ‘2020-12-31’
- 按2小时进行分组,统计时间范围内的平均值
SELECT
ROUND(avg(a.power),2) as cost,
count(1) as noOfCars,
hTime
FROM
(
SELECT
power,
DATE_FORMAT(concat(date(beg_time), ' ', floor(HOUR(beg_time)/2) * 2 ),'%H') AS hTime
FROM
`my_table`
WHERE
`status` in ('7','8') and beg_time between '2020-01-01 00:00:00' and '2020-12-01 23:00:00'
) a
GROUP BY hTime
- JPA的@OneToMany 配合其属性cascade = CascadeType.ALL的使用
- 使用 EntityManager 操作数据库时可以不给实体类配置@Entity、@ID等相关注解,但是查询结果只能是List<Object[]>
- 使用 EntityManager 的 save 方法批量保存数据实际是 for 循环依次插入,效率低下,可使用 createNativeQuery 拼接 values(),(),()… 完成原生SQL的批量插入
- SQLServer批量插入时存在的问题:最大容量规范中规定,每个用户定义函数的参数个数最大为2100,即values后面的参数占位符数量不能超过2100。且values后面接的数据条数不超过1000
- 如果遇到异常:MappingException: No Dialect mapping for JDBC type: -157 。 是数据库中的字段类型无法对应,要指定配置文件中的hibernate方言Dialect
- 截断表时如果出现存在关联外键异常,需要先删除从表关联的外键再截断主表
- 使用 SpringCloud Config 配置中心时在配置中设置的 logfile 和启动jar包时设置的路径不一致,导致在预期路径中的日志打印终止到Fetching config from server…,检查发现服务正常运行,实际上后续的日志全部输出到配置中心指定的目录中
- 搜索日志复杂关键字使用 NodePad++ 配合正则表达式
- 匹配ab首cd尾: ab.*cd
- 换行匹配ab首cd尾:ab.*?[\r\n.]*cd
- logback.xml 规定日志最大容量、最大保存时间、实现按天分类压缩保存。注意配置的参数顺序会影响最终日志效果
- 排查生产环境问题时可以给相关异常所在的类,提高日志打印等级 :
标签中设置属性 additivity=“false”, 则子Logger只会在自己的appender里输出,不会在root的logger的appender里输出(可以狭义理解为不会在rootLogger中打印相关日志) - 一些Linux命令
- 查看磁盘占用情况:df -h
- 查看端口情况 : netstat -nptl | grep 8080
- 打包 : tar -zcvf target.tar.gz source.txt
- vim 修改jar包内文件(需要先安装 vim/zip/unzip 命令):vim target.file 进入终端输入“ / ” 加上文件名,再Enter进入查看页面,“ i ”进行编辑
- 修改文件权限
chown aa xx.txt :修改文件和文件夹的用户和用户组属性,把 xx.txt 的用户访问权限应用到aa作为所有者
chmod 777 xx.txt :修改文件和文件夹读写执行属性,把xx.txt文件修改为可写可读可执行
chmod -R 777 /a/b :修改 /a/b下所有的文件和文件夹及其子文件夹属性为可写可读可执行
* 同时复制多个文件
cp /home/{file1,file2,file3,file4} /home/destination/
cp /home/file1 /home/file2 /home/file3 /home/file4 /home/destination/
* 访问某个地址失败,首先尝试Ping IP(Ping域名可解析出IP) ,能Ping通再考虑端口是否开放,开放端口实际上是设置防火墙暴露TCP/UDP对应端口
* 查看某端口是否有数据发出、是否有数据发往某端口
telnet 10.100.20.137 7780 # LINUX&WIN 查看10.100.20.137服务器的7780是否有数据发出
tcpdump udp port 11011 # LINUX 是否有数据走UDP协议往端口11011发
tcpdump tcp port 8888 # LINUX 是否有数据走TCP协议往端口8888发
* 找到正在运行的 jar 包位置
# 方式1
find / -name 'x.jar' # 直接全局搜索找出jar包位置
# 方式2
ps-ef | grep 'x.jar' # 找出正在运行的进程pid号
lsof -p pid | grep 'x.jar' # 根据pid找出使用这个进程的文件信息
* 列出最新的三个文件
ll -t logs/vd* |head -3
* 复制最新的三个文件到当前目录
ls -t logs/vd* |head -n 3 |xargs -i cp -r {} .
* 输出指定文件中关键字所在行
cat myfile.txt | grep 'keyword' -C 0 # -C num:匹配到搜索到的行以及上下各num行
- 生成实体类:添加hibernate.cfg.xml \ mybatis-generator (还可生成mapper、实体类、接口)
- IDEA快捷键转换大小写 crtl+shift+u ,shift+alt 可出现多处光标,Ctrl+Shift+Alt 可多处双击选择
设计
- 配置文件:
- bootstrap.yml配置spring cloud config 动态获取环境配置
- application.properties 静态设置环境配置
- 注意修改静态环境配置时也要同时修改config server 在 git 上的配置
- RabbitMQ和WebSocket整合实现消息实时推送和实时页面数据刷新
概念
- 测试代码覆盖率
- 敏捷开发
- 分析业务逻辑时绘制流程图、分析代码时注意数据的输入输出和处理单位、测试时提交简单报告
- KeyCloak 组件封装实现了 oauth2 的认证授权以及网关功能
- Java反编译工具:Java Decompiler
启动jar包的Shell脚本
#!/bin/bash 启动jar包的Shell脚本
# COMMAND LINE VARIABLES
# FIRST ARGUMENT deploy jar name
jarFileName=$1 # service-0.1.war
# SECOND ARGUMENT project folder
projectFolder=$2 # /opt/service/
# THIRD ARGUMENT log file name
logFileName=$3 # service.log
# FOURTH ARGUMENT profile name
profileName=$4 # dev
# FIFTH ARGUMENT stop file name
jarFileWildCardName=$5 # AID*.jar
## CONSTANT VARIABLES DECLARATION ##
dstLogFile=$projectFolder/$logFileName
#function to stop the existing service on the port
function stop() {
echo " "
echo "Stoping process for jar file $jarFileWildCardName"
/usr/bin/kill -9 $(/usr/sbin/fuser $projectFolder/$jarFileWildCardName) &
echo "Killed process using the file $projectFolder/$jarFileWildCardName"
echo " "
}
#function to run the service on the port
function start() {
echo "Application $projectFolder/$jarFileName starting with profile $profileName..."
nohup java -Xmx1024M -Djavax.net.ssl.trustStorePassword="password" -Djavax.net.ssl.trustStore=/opt/service/truststore.jks -Djavax.net.ssl.trustStoreType=jks -Dspring.profiles.active=$profileName -Dlogging.file="$projectFolder/logs/$logFileName" -jar $projectFolder/$jarFileName $> $dstLogFile 2>&1 &
echo "Application $projectFolder/$jarFileName started with profile $profileName."
echo " "
}
#function to change the jar file permissions
function changeFilePermission() {
echo "Changing File Permission: chmod 775 $projectFolder/$jarFileName"
chmod 775 $projectFolder/$jarFileName
echo "Changing File Permission: chmod 775 $projectFolder/logs/$logFileName"
chmod 775 $projectFolder/logs/$logFileName
# echo "Changing the File Permission: chmod 775 $dstLogFile"
# chmod 775 $dstLogFile
echo " "
}
#function to sleep for 10 ms
function sleepCommand() {
/usr/bin/sleep 10
}
# 1 - stop server on port ...
stop
sleepCommand
# 2 - change file permissions to run
changeFilePermission
sleepCommand
# 3 - start server
start
exit 0