部署
vue项目部署
vue打包命令
renren-fast 修改配置
index-prod.js
修改为服务器地址
npm run build
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location / {
#采用docker目录挂载,只需要在宿主机对应的目录放好vue打包文件
root /usr/share/nginx/html;
index index.html index.htm;
}
location /test {
root /usr/share/nginx/html;
index /test/index.html /test/index.htm;
}
#vue与服务部署在同一台服务器,所以用localhost
location /renren-fast {
proxy_pass http://localhost:8080/renren-fast;
}
参考博文:
feign
get请求传对象
PageVO<User> getObject(@SpringQueryMap User user);
返回mybatisplus的分页数据,无法序列化
自己封装一个page
@Data
public class PageVO<T> implements Serializable {
private static final long serialVersionUID = -5161145370999313156L;
private List<T> records;
private Long total;
private Long size;
private Long current;
}
java
java应用访问mysql失败
参考博文:
在这里插入代码片
解决java.sql.SQLException: null, message from server: "Host ‘XXX’ is not allowed to connect异常
启动jar包并指定配置文件
spring:
profiles:
active: dev
java -jar demo.jar --spring.profiles.active=pro
参考博文:
序列化和反序列化
一个对象实现了序列化接口,他的集合也可序列化,因为集合也实现了序列化接口。
如果一个对象内部的属性也是一个集合,则他的泛型对象也需要实现序列化接口。
序列化前后版本号需要一致,否则会出现下面的错误
java.io.InvalidClassException: java基础.序列化.User; local class incompatible: stream classdesc serialVersionUID = 4680606320177667957, local class serialVersionUID = 1680606320177667957
User rx = new User("rx", 1);
User rx2 = new User("rx2", 2);
User rx3 = new User("rx3", 3);
ArrayList<User> users = new ArrayList<>();
try (
FileOutputStream fos = new FileOutputStream("object.out");
ObjectOutputStream oos = new ObjectOutputStream(fos)
){
users.add(rx);
users.add(rx2);
users.add(rx3);
oos.writeObject(users);
}catch (Exception e){
e.printStackTrace();
}
try (
FileInputStream fis = new FileInputStream("object.out");
ObjectInputStream ois = new ObjectInputStream(fis)
){
ArrayList<User> o = (ArrayList<User>)ois.readObject();
System.err.println(o);
}catch (Exception e){
e.printStackTrace();
}
集合
arraylist
参考博文:
ArrayList常用方法总结
ArrayList使用迭代器遍历删除元素(迭代器的具体实现)
ArrayList和HashMap的遍历选择删除,集合的迭代删除☆
treeset
排序:实现Comparable或者Comparator,后者比较灵活。
一般需要排序才使用treeset,否则使用hashset
参考博文:
map转对象
HashMap<String, Object> map = new HashMap<>();
map.put("name","rx");
map.put("userType",12);
UserInfo userInfo1 = JSONObject.parseObject(JSONObject.toJSONString(map), UserInfo.class);
System.err.println(userInfo1);
异常
try-catch-finally
1、三个代码块中都有return,最终返回的是finally
2、try或者catch中执行了return,finally再对return的结果做修改,不会影响返回结果。
jdk7以后自动关闭流的方式
try(
FileInputStream fis = new FileInputStream("D:\\javaDemo\\test\\jjj");
FileOutputStream fos = new FileOutputStream("D:\\javaDemo\\test\\kkk");
) {
byte[] bs = new byte[1024];
int len = 0;
while ((len = fis.read(bs)) != -1) {
fos.write(bs, 0, len);
}
}
接口
接口中不可含有抽象方法
流式处理
数组转化为流
Integer[] arr = {1, 2, 3, 4, 5};
Stream<Integer> stream7 = Stream.of(arr);
对象拷贝
参考博文:
BeanUtils
@Test
public void r2(){
UserBindVO userBindVO = new UserBindVO();
userBindVO.setIdCard("iddsd");
userBindVO.setName("rx");
userBindVO.setBankType("12");
userBindVO.setBankNo("xsx");
userBindVO.setMobile("43");
UserBind userBind = new UserBind();
BeanUtils.copyProperties(userBindVO, userBind);
System.err.println(userBind);
}
idea
自制idea插件
参考博文:
idea插件
参考博客:
idea引用本地jar包
导入库中的所有jar
也可以从这里添加单个jar包
idea启动多个实例
idea打jar包
选择主类
jar包输出位置
构建jar
运行jar
java -jar xxx.jar
nohup java -jar xxx.jar &
使用此方法对springboot项目进行打包
如果之前用同样的方法打包过,需要删除文件
如果只勾选框出来的
就是打包不包含依赖的jar包,否则就是包含依赖的jar包
运行
java -cp maventest.jar rx.maventest.MaventestApplication
虽然可以运行,但总感觉有问题。暂时存疑。
存在多个主类时。
然后build
运行对应的主类
java -cp **.jar 主类的全类名
如果有多个主类的情况下,选择第一种打包方式,则用java -jar 运行,并且只会运行之前指定的主类。
maven
参考博文:
maven搭建项目
参考博文:
maven打包
微服务的pom配置
父工程
<build>
<plugins>
<plugin>
<!--spring-boot-maven-plugin插件是将springboot的应用程序打包成fat jar的插件-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
不含有主方法类的模块
<build>
<plugins>
<!--跳过父项目传递过来的 spring-boot-maven-plugin 的 repackage -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- maven-shade-plugin 是为了打包其依赖的 lib -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<configuration>
<!--不生成 dependency-reduced-pom.xml-->
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
参考博文:
多 module Spring-Boot 项目 repackage 问题
解决spring-boot-maven-plugin:2.2.1.RELEASE:repackage failed: Unable to find main class
spring-boot-maven插件repackage(goal)的那些事
Maven生命周期和插件的那些事(2021版)
maven项目部署
nexus私服
将windos版本解压。然后设置环境变量
启动并访问,默认8081
http://localhost:8081/
登入账号admin,
密码在
F:\Program Files\sonatype-work\nexus3\admin.password
访问需要验证
创建仓库
设置阿里云镜像仓库代理
点击public,将aliyun添加进分组
修改maven settings文件
<mirrors>
<mirror>
<id>nexus</id>
<name>nexus Maven</name>
<mirrorOf>*</mirrorOf>
<url>http://localhost:8081/repository/maven-public/</url>
</mirror>
</mirrors>
踩坑:mvn deploy 在命令行和idea右侧执行结果不同
原因:在命令行默认读取的是settings.xml文件,idea插件读取的是自己配置的xml
参考博文:
maven命令
命令 | 作用 |
---|---|
mvn dependency:list | 显示项目依赖列表 |
mvn dependency:tree | 以树形结构查看工程依赖信息 |
mvn install | jar包安装到本地仓库 |
mvn clean install -Dmaven.test.skip=true | 安装到本地仓库 |
mvn clean package spring-boot:repackage -Dmaven.test.skip=true | 打包 |
依赖作用域
compile:通常使用的第三方框架的 jar 包这样在项目实际运行时真正要用到的 jar 包都是以 compile 范围进行依赖的。比如 SSM 框架所需jar包。
test:测试过程中使用的 jar 包,以 test 范围依赖进来。比如 junit。
provided:在开发过程中需要用到的“服务器上的 jar 包”通常以 provided 范围依赖进来。比如 servlet-api、jsp-api。而这个范围的 jar 包之所以不参与部署、不放进 war 包,就是避免和服务器上已有的同类 jar 包产生冲突,同时减轻服务器的负担。说白了就是:“服务器上已经有了,你就别带啦!”
1、打成war包
直接将war包放到本地tomcat的webapps目录下面,然后启动tomcat。
访问:http://localhost:8080/test5/ (test5是war包名称)
maven clean执行失败,检查项目是不是正在运行。
Failed to execute goal org.apache.maven.plugins:maven-clean-plugin:2.5:clean (default-clean) on project maventest: Failed to clean project: Failed to delete C:\Users\Rx\Desktop\maventest\target\maventest-0.0.1-SNAPSHOT.jar
如果在idea中执行过java -jar 需要退出程序,重新进入idea。
经过实验,还必须出现这样的报错后,再退出进入,才会有用。
optional
只会被自己使用,不会发生依赖传递
exclusion
主动排除传递过来的依赖
idea maven插件爆红
原因是根据换了,本地maven仓库的地址,需要从新导入jar包,可以用webapp模板新建的maven工程。会用到这些插件,然后自动导入jar。
pom.xml
参考博文:
settings.xml
参考博文:
Maven的settings.xml配置详解
Maven的仓库和settings.xml配置文件
Idea新建项目默认是JDK1.5解决办法
idea每次创建项目JDK版本都是1.5的解决方法
mybatis
mybatis在xml中遍历集合
<if test="eqpGroupQueryDTO.eqpIdList != null and eqpGroupQueryDTO.eqpIdList.size>0">
and a.id not in
<foreach collection="eqpGroupQueryDTO.eqpIdList" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</if>
redisson
缓存一致性问题
- 缓存一致性问题
* 1、双写模式 写数据库之后,写缓存
* 2、失效模式,改完数据库之后,删除缓存
* 可以加读写锁
* 经常修改不用放缓存,直接读数据库
单一实例+本地锁实现读redis缓存
//每个线程进来,先判断redis中数据是否存在。如果不存在,就会去查询数据库,并放入缓存。
//当这个线程释放锁以后,其他线程再次竞争锁,需要再次查看缓存中是否有数据。这样就可以实
//现只查询一次数据库。
public ProdObjProductEntity testRedis(QueryWrapper<ProdObjProductEntity> queryWrapper) {
Object testRedis = redisTemplate.opsForValue().get("testRedis");
if (testRedis == null) {
synchronized (this) {
System.err.println("读数据库");
return getProdObjProductEntity(queryWrapper);
}
} else {
System.err.println("查缓存");
return (ProdObjProductEntity) testRedis;
}
}
private ProdObjProductEntity getProdObjProductEntity(QueryWrapper<ProdObjProductEntity> queryWrapper) {
Object testRedis;
testRedis = redisTemplate.opsForValue().get("testRedis");
if (testRedis == null) {
System.err.println("查库了");
ProdObjProductEntity one = this.getOne(queryWrapper);
redisTemplate.opsForValue().set("testRedis", one);
return one;
}
System.err.println("查缓存");
return (ProdObjProductEntity) testRedis;
}
但是如果是多实例状态,就会查询多次数据库。
多实例+redisLock实现分布式锁读缓存
public ProdObjProductEntity testRedisWithRedisLock(QueryWrapper<ProdObjProductEntity> queryWrapper) {
// System.err.println(redissonClient);
Object testRedis = redisTemplate.opsForValue().get("testRedis");
if (testRedis == null) {
ProdObjProductEntity redisLock = getRedisLock(queryWrapper);
return redisLock;
} else {
System.err.println("查缓存");
return (ProdObjProductEntity) testRedis;
}
}
/**
* 缓存一致性问题
* 1、双写模式 写数据库之后,写缓存
* 2、失效模式,改完数据库之后,删除缓存
* 可以加读写锁
* 经常修改不用放缓存,直接读数据库
*
* @param queryWrapper
* @return
*/
public ProdObjProductEntity getRedisLock(QueryWrapper<ProdObjProductEntity> queryWrapper) {
RLock redisLock = redissonClient.getLock("redisLock");
redisLock.lock();
System.err.println("获取分布式锁成功");
ProdObjProductEntity prodObjProductEntity;
try {
prodObjProductEntity = getProdObjProductEntity(queryWrapper);
} finally {
redisLock.unlock();
}
return prodObjProductEntity;
}
过滤重复请求
实现功能:所有请求排队进入,并且一分钟内只有一个请求会被执行,其他会被跳过。
可用于实现接口幂等。
@GetMapping("/testBladeLock")
public String testBladeLock() {
//String baldelock = redisLockClient.lock("baldeLock", LockType.REENTRANT, 10, 10, TimeUnit.SECONDS, () -> {
// return "success";
// });
RLock lock = redissonClient.getLock("my-lock");
//redisson 有看门狗会自动续期 并且可以自动释放锁 运行期间会自动续期30秒,如果服务挂了或者异常,就无法自动续
//期,所以不会发生死锁
//方法一、10秒后自动解锁 (存在问题:业务还没执行完毕,就给解锁了) 自动解锁时间一定要大于业务执行时间 不会自
//动续期
// lock.lock();
//方法二、每隔10秒续期到30
// lock.lock(10, TimeUnit.SECONDS);
//方法三、最佳时间设置30秒 而不是采用自动续期
//trylock 尝试加锁,等待一段时间后,就放弃,lock是一直等待
//公平锁 有顺序的获取锁 先来后到 非公平锁 只要锁释放 会继续抢占
try {
//尝试5秒去获取锁,没有成功就放弃。
boolean b = lock.tryLock(5, 30, TimeUnit.SECONDS);
System.err.println("加锁成功" + Thread.currentThread().getId());
if (b) {
// String uuid = UUID.randomUUID().toString();
//该请求,在一分钟内只会执行一次
Boolean lockFlag = redisTemplate.opsForValue().setIfAbsent("lock", "1", 60, TimeUnit.SECONDS);
if (lockFlag) {
System.out.println("执行业务" + Thread.currentThread().getId());
// Thread.sleep(5000);
} else {
System.err.println("跳过业务" + Thread.currentThread().getId());
}
} else {
System.err.println("加锁失败" + Thread.currentThread().getId());
return "hello world";
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//假设解锁代码没有执行,会不会死锁 (不会死锁)
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
System.err.println("解锁成功" + Thread.currentThread().getId());
lock.unlock();
}
}
return "hello";
}
测试可重入锁
/**
* 测试可重入锁
*/
@GetMapping("/testReEntranceLock")
@ApiOperationSupport(order = 1)
@ApiOperation(value = "详情", notes = "传入prodobjproduct")
public String testReEntranceLock() {
RLock lock = redissonClient.getLock("my-lock");
//redisson 有看门狗会自动续期 并且可以自动释放锁 运行期间会自动续期30秒,如果服务挂了或者异常,就无法自动续期,所以不会发生死锁
// lock.lock();
//10秒后自动解锁 (存在问题:业务还没执行完毕,就给解锁了) 自动解锁时间一定要大于业务执行时间 不会自动续期
//每隔10秒续期到30
lock.lock(10, TimeUnit.SECONDS);
//最佳时间设置30秒 而不是采用自动续期
//trylock 尝试加锁,等待一段时间后,就放弃,lock是一直等待
//公平锁 有顺序的获取锁 先来后到 非公平锁 只要锁释放 会继续抢占
try {
System.err.println("加锁成功" + Thread.currentThread().getId());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//假设解锁代码没有执行,会不会死锁 (不会死锁)
System.err.println("解锁成功" + Thread.currentThread().getId());
lock.unlock();
}
return "hello";
}
读写锁
//写锁是排它锁,保证读取到最新数据,写锁没有释放,读取就必须等待
//写+读 等待写锁释放
//读+写 写锁等待
//写+写 阻塞
//读+读 相当于无锁
@GetMapping("/write")
public String writeValue() {
RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
String s = "";
RLock rLock = lock.writeLock();
try {
//改数据加写锁,读数据加读锁 保证读取的都是最新数据
rLock.lock();
System.err.println("加写锁");
s = UUID.randomUUID().toString();
Thread.sleep(30000);
redisTemplate.opsForValue().set("writeValue", s);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.err.println("解写锁");
rLock.unlock();
}
return s;
}
@GetMapping("/read")
public String readValue() {
RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
String s = "";
RLock rLock = lock.readLock();
try {
rLock.lock();
System.err.println("加读锁");
Thread.sleep(30000);
s = redisTemplate.opsForValue().get("writeValue");
} catch (Exception e) {
e.printStackTrace();
} finally {
System.err.println("解读锁");
rLock.unlock();
}
return s;
}
闭锁
/**
* 闭锁
* 放假锁门 必须等待所有人走完了 才可以锁门
*/
@GetMapping("/lockDoor")
public String lockDoor() throws InterruptedException {
RCountDownLatch door = redissonClient.getCountDownLatch("door");
door.trySetCount(5);
//等待闭锁完成
door.await();
return "放假了";
}
@GetMapping("/gogo/{id}")
public String gogo(@PathVariable("id") Long id) {
RCountDownLatch door = redissonClient.getCountDownLatch("door");
door.countDown();
return id + "走了";
}
信号量
/**
* 车库停车
* 走一个进入一个
* 信号量可以用于分布式限流
* 先go再park
*/
@GetMapping("/park")
public String park() {
RSemaphore park = redissonClient.getSemaphore("park");
//获取信号量 阻塞方法
try {
park.acquire();
// park.tryAcquire() 不会阻塞等待 不行就算了
} catch (InterruptedException e) {
e.printStackTrace();
}
return "ok";
}
/**
* 车库停车
* 走 释放车位
*/
@GetMapping("/go")
public String go() {
RSemaphore park = redissonClient.getSemaphore("park");
//获取信号量 阻塞方法
try {
park.release();
} catch (Exception e) {
e.printStackTrace();
}
return "go";
}
shell
新建和运行shell
新建test.sh
#!/bin/bash
echo "Hello World !"
执行
1、作为可执行程序
#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。
chmod +x test.sh
2、作为解释器参数
/bin/sh test.sh
/bin/php test.php
变量定义和赋值
your_name="tom"
echo $your_name
your_name="alibaba"
echo $your_name
使用变量,建议${aaa}
字符串,单引号和双引号都可以使用
参数传递
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
shell文件包含
a.sh引用b.sh
#使用 . 号来引用test1.sh 文件
. ./test1.sh
# 或者使用以下包含文件代码
# source ./test1.sh