• 主页
  • 标签
所有文章 关于我

  • 主页
  • 标签

项目踩坑

2021-03-26

1.R是com.baomidou.mybatisplus.extension.api中写好的一个统一结果返回集。主要包含code,data,msg

**R(IErrorCode errorCode)**: {data:null,code:errorCode.getCode(),msg:errorCode.getMsg()}

R.ok(T data): 默认情况下:{data:data,code:0,msg:执行成功} 如果data为Boolean类型且就是false:{data:false,code:-1,msg:操作失败}

R.failed(String msg): {data:null,code:-1,msg:msg}

R.failed(IErrorCode errorCode) : {data:null,code:errorCode.getCode(),msg:errorCode.getMsg()}

R.restResult(T data, IErrorCode errorCode) : {data:data,code:errorCode.getCode(),msg:errorCode.getMsg()}

R.restResult(T data, long code, String msg): {data:data,code:code,msg:msg}

可以自己定义枚举类实现IErrorCode接口

遇到的问题:

1
2
3
R.restResult(vo, ApiSuccessCode.YELLOW)
当YELLOW定义为(-1,"yellow")时,前端会toast出yellow。(因为前端代码如果不符合要求Toast(res.data.msg);)
//解决方法:将YELLOW的code定义为其他不常用的特殊数字,前端对这个数字进行判断来满足这个单独的场景

2.多看文档的API,可能有封装好的方法

3.lombok里的@data可以自动装填get/set

4.docker里的时间比写入到mysql的时间少8小时。java程序在本地运行时间(正常,相当于就是mysql的时间)比在docker容器上运行时间迟(多)8小时。(用date命令查看时间是CST还是UTC)

原因 :

一般宿主机用的都是CST时区(China Standard Time UT+8:00 中国标准时间),而docker容器中初始用的都是UTC时区(Coordinated Universal Time 世界协调时间)比CST慢8个小时

解决方案:

查阅相关资料后得知: jre是通过/etc/timezone 配置文件读取本地时间的

需要将挂载宿主机的/etc/localtime 到容器的/etc/localtime,这时输入date命令容器时区显示正常,但是跑在容器中的java项目取到的时间却早了8小时。

接着我修改了/etc/timezone配置命令如下:
echo "Asia/Shanghai" > /etc/timezone

重启了下容器,然后java项目中读取的时区恢复正常了。

但是仅仅是java容器内的正常了,nginx容器内时间还是少8个小时,所以还需要将nginx的容器的时区进行更改。

也可以执行命令更改时区:

1
2
3
root@06057b1eeb48:/usr/share# cd /etc/
root@06057b1eeb48:/etc# mv localtime localtime.bak
root@06057b1eeb48:/etc# cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

输入date查看时间是否是CST

5.程序内微信的acces_token过期,返回值为400001,但是postman正常发送返回值为0。清下服务器上redis的数据库即可。

image-20210410201322948

6.关于vue项目打包的问题:

花了一个下午,差不多搞明白了。

node.js有一个版本号,安装好node.js之后,会自动安装了npm。npm也有一个版本号。

npm可以再安装vue,再安装vue的脚手架:vue -cli,这两个的版本号是同一个。

​ 1.注意这里的版本号。写vue项目用到什么的版本号都会写在vue项目里的package.json。版本过高或者过低都会报错。还要注意这几个软件的安装位置,最好都在一起。

​ 2.特别坑。为什么每次打包都会出错?是因为你运行的VUE的文件外面还包了一个文件。运行的时候一般就是总文件,然后直接打包会成功,如果外面再有一个文件夹(比如你是从git上pull的项目,它外面会自带一个文件夹,你再直接打开这个文件就会出现这种情况),就会报错。

7.String类的int类型hex进制转10进制的时候:00000000会自动转为0,需要转换为00000000再转为string类型,可以用String.format(“%08d”,i)方法

8.vue页面的图片插入问题:require(’相对路径’)

服务器上的/xx/xx/xxx.jpg

9.HttpServletRequest

public void weixin(@RequestParam(name = “openid”, defaultValue = “”) String openid, @RequestBody String xml)

10.将一个xml格式的string类型数据进行解析:

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
46
47
String str= sb.toString();
Document doc = null;
try {
doc = DocumentHelper.parseText(str);
} catch (DocumentException e) {
e.printStackTrace();
}
Element root = doc.getRootElement();// 指向根节点 <root>
try {
//这里的是Element,需要转换为String类型
Element mark=root.element("header").element("mark");
Element second=root.element("body").element("data").element("first").element("second");
//获取xml的节点内容
System.out.println(mark.getTextTrim());
System.out.println(second.getTextTrim());
}catch(Exception e){
e.printStackTrace();
}

//将xml存入map
public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
// 从request中取得输入流
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);

// 得到xml根元素
Element root = document.getRootElement();
//获得子元素下的元素
Iterator<Element> iterator = root.elementIterator();
while (iterator.hasNext()) {
Element e = iterator.next();
map.put(e.getName(), e.getText());
@SuppressWarnings("unchecked")
List<Element> sonList = e.elements();
for (Element son : sonList) {
map.put(son.getName(), son.getText());
}
}
// 释放资源
inputStream.close();
inputStream = null;
return map;
}

11.idea里的文件名字变蓝是因为文件里的内容被修改过,没有提交到版本库。变红是新增的文件。

12.微信回复消息的时候,<Content><![CDATA["+text+"]]></Content>中的text文本乱码成?????

是由于spring mvc的@ResponseBody注解返回字符串时默认返回的是“ISO-8859-1”而不是utf-8。

虽然大家的项目里面可能都有字符编码过滤器,但是有一个问题在这里,我们设置response.setContentType(“text/html; charset=utf-8”);时都是在chain.doFilter(request, response);之前设置的,也就是过滤器前处理是设置的响应编码格式,之所以不在后处理时设置是因为后处理时响应内容已经生成此时设置是无效的,必须在响应内容生成之前设置响应编码格式。

问题就出在这个地方,我们虽然设置了响应编码格式,可是spring mvc在有@ResponseBody注解的响应是,篡改了我们的响应编码!
相信大家对spring的Message Converters不会陌生,HttpMessageConverters这个接口是用来把请求信息转化为对象T,把T输出为响应信息的一个接口,在该接口众多的实现类中有一个实现类StringHttpMessageConverter,这个类的作用就是把请求信息转换未字符串,而@ResponseBody注解就是默认调用的这个类,而这个类中默认的编码格式就是ISO-8859-1,所以罪魁祸首找到了

原因:

由于spring默认对String类型的返回的编码采用的是 StringHttpMessageConverter
>>> spring mvc的一个bug,spring MVC有一系列HttpMessageConverter去处理用@ResponseBody注解的返回值,如返回list则使用MappingJacksonHttpMessageConverter。返回string,则使用StringHttpMessageConverter。这个convert使用的是字符集是iso-8859-1,而且是final的:
public static final Charset DEFAULT_CHARSET = Charset.forName(“ISO-8859-1”);

解决:

在RequestMapping里加上 produces = "text/html;charset=UTF-8"//只针对单个方法生效,不全局生效

例:@RequestMapping(value = “/weixin/validation” , method = RequestMethod.POST , produces = “text/html;charset=UTF-8”)

13.程序内是生成6位数字的验证码结果,通过阿里云短信发送出来有时只有5位:因为返回到阿里云里面的验证码,会被识别为数字。然后第一位数字是0就默认去掉了。

提供的解决方法:

​ 1.随机生成数开头为0的话,继续随机:

1
2
3
4
5
6
7
public static int getRandom(){ 	//递归方法
int number = new Random().nextInt(10);
if(0 == number){
return getRandom();
}
return number;
}

​ 2.是在验证码外层拼接一个单引号 :"{code:"+"'"+code+"'"+"}" 为什么加了一个单引号就可以实现?阿里云的接口定义这里应该是一个json格式。

14.记录表和状态表:状态表的主键唯一,unquire。记录表主键为normal,可以重复多条。

15.关于移动端的v-console按钮:

image-20210514122852087

当不需要显示时:在main.js文件中将new Vconsole()注释掉即可。

16.spring MVC接收请求体总是多一个等号

例:我发送的请求体是字符串aaa,spring MVC 接收到的是aaa=

//因为请求的content type不对,应该是:application/json;charset=UTF-8。而我设置的content type是:application/x-www-form-urlencoded;charset=UTF-8

原因是后端接收的是context-type:application/json
而axios的post请求默认是context-type: application/x-www-form-urlencoded
这里在前端发起请求前,在请求拦截器中设置一下context-type为json就好了

因为默认是以键值对形式传递
前端传过来的内容是放在k中,v为空,这时候取值时,内容就变成了k=,也就是为什么后端接收的数据,末尾多了一个=

17.qw条件构造器连续使用的问题

1
2
3
4
5
6
7
QueryWrapper qw = new QueryWrapper();
qw.eq("node_num", "123");
Nodes nodes = nodesService.getOne(qw);

qw = new QueryWrapper();//需要新加这一行代码才能保证上下两个查询语句互不相连。如果没有这条语句,下面的查询语句会有两个限定条件
qw.eq("user_id", "1");
List<Bind> bind = bindService.list(qw);
  1. qs.stringify(this.form)
1
uid=cs11&pwd=000000als&username=cs11&password=000000als

qs是一个npm仓库所管理的包,可通过npm install qs命令进行安装

  1. qs.parse()将URL解析成对象的形式
  2. qs.stringify()将对象 序列化成URL的形式,以&进行拼接

JSON中同样存在stringify方法:

1
{"uid":"cs11","pwd":"000000als","username":"cs11","password":"000000als"}   

当前端页面需要借助url来往后台传递参数的时候我们通常会这样写:

1
var  url="base/exchangeController/1/searchExchanges?code="+code+"&name="+name;

如果你的name传递的是中文的话,在谷歌浏览器中传递到后台的数据是正常的(其他浏览器未测试),但是在IE浏览器中,后台接受到的将会是乱码,这是因为IE浏览器没有将你传递的参数序列化为URL 编码文本字符串,后台在解码你的参数的时候就会形成乱码,解决方法如下:

1
2
var  param={"code":code,"name":name};
var url="base/exchangeController/1/searchExchanges"+"?"+$.param(param);

  借助jquery的param方法将你要携带的参数对象化之后,再序列化一下,这样IE浏览器下即使你传递中文,后台接收到的数据也是正常的中文了

19.前端调试的时候:console.log(A) 输出的数组A显示是Object,而不是包含的值,但只是打印出来的log是Object,在引用时候还是数组的值。需要查看打印的值(将数组A转换为json字符串):console.log(JSON.stringify(A))

20.前端页面动态赋值时不起作用,在属性前面加:绑定后生效。

21.前端页面的css样式复用问题:原页面a.vue用的v-router去跳转到b.vue,然后退回来时a.vue的样式全乱了,我在b.vue中导入了一个css文件,然后页面调试是看到退回后styles是不变的,也就是说a.vue用的不是原来的样式而是b.vue中导入的样式,刷新一下的话就又可以了。

怎么让vue不去使用上个页面的样式,或者说让当前vue只用自己写的当前样式:两个名字不要起一样的,就是两个css样式了。

22.导航下拉菜单被遮住或显示不全:

原因:层叠关系错误

解决:设置z-index,最上面的层级值越大。但是必须有两个前提条件:1.必须是同级。2.二者分别设定了position:relative 或 absolute 或 fixed;

23.后端springboot项目,入口的Application类必须放在最上层,才能读取到所有的其他配置。

24.@Slf4j

如果不想每次都写**private final Logger logger = LoggerFactory.getLogger(当前类名.class); **

可以在类上用注解@Slf4j,然后就可以直接使用log.info(“字符串内容”);

25.在工具类中调用静态方法时,mapper无法注入的问题:一个工具类中的静态方法调用mybatis的mapper接口时,会出现@Autowired无法注入的问题,即使添加了这个注解,spring容器加载完成声明的参数也是空值

在SpringFramework里,不能@Autowired一个静态变量,使之成为一个Spring bean的

解决方案:

首先,在实体类上加上注解@Component,方便spring容器进行加载,然后定义如下:

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
/**
在静态工具类中注入mapper的方式示例
*/
@Component
public class CodeMapUtils {


private static Logger LOGGER = LoggerFactory.getLogger(DoAllController.class);

private static CodeMapUtils codeMapUtils;

@Autowired
private CodeMapMapper codeMapMapper;

@PostConstruct
public void init() {
codeMapUtils = this;
codeMapUtils.codeMapMapper = this.codeMapMapper;
}

public static String queryFromCodeMap(String key, String colName) {
String value = codeMapUtils.codeMapMapper.getValueByKeyAndColName(key, colName);
// do something...

}
}

另外,spring中Constructor、@Autowired、@PostConstruct的顺序:

其实从依赖注入的字面意思就可以知道,要将对象p注入到对象a,那么首先就必须得生成对象a和对象p,才能执行注入。所以,如果一个类A中有个成员变量p被@Autowried注解,那么@Autowired注入是发生在A的构造方法执行完之后的。

如果想在生成对象时完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。

加载顺序:Constructor >> @Autowired >> @PostConstruct

另:

spring无法自动注入静态变量、spring注入null 的解决方法:

方式一:使用@Resource注解set方法,将注入的bean对象赋值给静态变量。

/**

  • @description: 自定義公共方法

  • @author: H2103424

  • @createTime: 2021/1/22 下午 02:15
    **/
    @Component
    public class CustomUtil {

    private static UserService userService;
    private static MailSendService mailSendService;

    @Resource
    public void setMailSendService(MailSendService mailSendService) {

    CustomUtil.mailSendService = mailSendService;
    

    }

    @Resource
    public void setUserService(UserService userService) {

    CustomUtil.userService = userService;
    

    }

    /**

    • @description: 根據 用戶編號list 獲取用戶郵箱list
    • @author: H2103424
    • @dateTime: 2021/1/22 下午 03:06
      *
    • @param users 用戶編號集合
    • @return 用戶郵箱集合
      */
      public static List getEmailsByUsers(List users){
      List<Map<String,Object>> userInfos = userService.getUserByUserNo(users);
      List userEmails = new ArrayList<>();
      userInfos.forEach(u -> {
      userEmails.add((String) u.get("user_email"));
      
      });
      return userEmails;
      }

      }

      1
      2
      3

      方法二:使用@Resource自动注入普通变量,然后使用@PostConstruct注解将普通变量赋值给静态变量。

      /**

      • @description: 自定義公共方法

      • @author: H2103424

      • @createTime: 2021/1/22 下午 02:15

      • */
        @Component
        public class CustomUtil {

        @Resource
        private UserService userServiceBean;
        @Resource
        private MailSendService mailSendServiceBean;

        private static UserService userService;
        private static MailSendService mailSendService;

        @PostConstruct
        public void init(){

          userService = userServiceBean;
          mailSendService = mailSendServiceBean;
        

        }

        /**

        • @description: 根據 用戶編號list 獲取用戶郵箱list
        • @author: H2103424
        • @dateTime: 2021/1/22 下午 03:06
        • @param users 用戶編號集合
        • @return 用戶郵箱集合
        • /
          public static List getEmailsByUsers(List users){
          List<Map<String,Object>> userInfos = userService.getUserByUserNo(users);
          List userEmails = new ArrayList<>();
          userInfos.forEach(u -> {
            userEmails.add((String) u.get("user_email"));
          
          });
          return userEmails;
          }
          }
          1
          2
          3
          4
          5

          @PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。

          Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

          package com.example.studySpringBoot.util;

          import com.example.studySpringBoot.service.MyMethorClassService;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.stereotype.Component;

          import javax.annotation.PostConstruct;

          @Component
          public class MyUtils {

          private static MyUtils staticInstance = new MyUtils();

          @Autowired
          private MyMethorClassService myService;

          @PostConstruct
          public void init(){
          staticInstance.myService = myService;
          }

          public static Integer invokeBean(){
          return staticInstance.myService.add(10,20);
          }

          }

          项目里实际用的这种也可以:(这两个上面加不加@Autowired效果一样的)

          1
          2
          3
          4
          5
          static UserMapper userMapper;

          private UserUtil(UserMapper userMapper) {
          UserUtil.userMapper = userMapper;
          }

          26.vue项目地址里的/#/,#是因为 Vue 的路由使用了 Hash 模式,是单页面应用的经典用法。如果需要去掉的话:可以在路由配置中选择使用 History 模式,但会引发一些问题,需要在后端作出处理。

          27.如果自动安装maven依赖的过程没有执行,可以在 pom.xml 上右键,选择 Maven ->reload project。

          如果右边没有maven菜单栏时,shift+shift 选择actions 点击add maven project

          28.springboot项目报错:(未实操)

          1
          2
          3
          4
          o.apache.coyote.http11.Http11Processor   : Error parsing HTTP request header
          Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.
          java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
          .......

          用的是tomcat9.0版本

          解决方法:不推荐降低tomcat版本,这等于掩耳盗铃,绝对得不偿失。Tomcat在 7.0.73, 8.0.39, 8.5.7 版本后,在http解析时做了严格限制。RFC3986文档规定,请求的Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符

          1)在server.xml中的Connector中添加maxHttpHeaderSize=”8192”

          1
          <Connector port="8080" protocol="HTTP/1.1"   maxHttpHeaderSize="8192"    connectionTimeout="20000"   maxThreads="150"  maxSpareThreads="75"   redirectPort="8443" />  

          2)在在conf/catalina.properties中最后添加2行

          1
          2
          tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}
          org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true

          requestTargetAllow 只能配置|、{、} 允许这三个字符,对于其他的(例如” < > [ \ ] ^ ` { | } .),在请求时,仍然拦截,如果使用了|{}之外的其他字符:

          1
          relaxedPathChars="[\]^`{|}" relaxedQueryChars="[\]^`{|}"

          3)如果是https访问的页面,改为http

          4)将HTTP1.1改成org.apache.coyote.http11.Http11NioProtocol,结果启动tomcat的时候出现了一大堆“地址已使用”的错误

          29.学习项目时,如果跑起来报错jedis,可能是因为本地redis密码没有设置,需要在命令行进行redis登录,再设置密码,退出,再启动项目。

          30.idea里如果get,set方法飘红,是因为lombok插件没有下载,需要在settings里的plugins下载。如果插件半天加载不出来,可以在设置里的HTTP Proxy setting勾选Auto-detect proxy settings,再勾选Automatic proxy configuration URL:填入代理地址url:http://127.0.0.1:1080

          31.代码规范可以下载alibaba java coding guidelines,在项目处右击选择 编码规约扫描

          32.localStorage 是存储在浏览器前端的,后台想获取,就需要前端传过去获取,可以在axios中将head的参数全局设置为localStorage 中的某个值

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          axios.interceptors.request.use(
          (config) => {
          Toast.loading({
          message: "加载中...",
          loadingType: "spinner",
          forbidClick: true,
          duration: 0,
          });
          const openid = localStorage.getItem("openid");
          config.headers["openid"] = openid;
          if( localStorage.getItem('flag')==1){
          config.headers["openid"] = "tangseng";
          }
          return config;
          }

          33.vue页面的轮询:一般轮询都会使用setInterval,但是单独使用它会使页面卡死

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          export default {
          data() {
          return {
          timer: null
          }
          },
          mounted() {
          this.getList();
          this.timer = window.setInterval(() => {
          setTimeout(() => {
          this.getList()
          },0)
          },3000)
          },
          methods: {
          getList() {
          // 发送接口
          }
          },
          destroyed() {
          window.clearInterval(this.timer)
          }

          }

          使用说明:setInterval不会清除定时器队列,每重复执行1次都会导致定时器叠加,会出现网页卡死现象。但是setTimeout是自带清除定时器的,两者结合使用将避免页面卡死。

          页面初始化,待开始轮询后,离开页面,通过生命周期destroyed钩子函数,销毁定时任务。

          如果不进行销毁,在vue页面中,即使离开了有定时器的页面,仍会执行定时任务,但是当关闭页面后,定时任务就没了。

          34.spring方法里写了mapper以后,记得上面加注解@comment将这个类注入到spring中。

          35.vue中:当选择了拒绝或者同意之后,this.radio可以正确取到,但是this.myform.reviewStatus显示为undefined

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
                <van-radio-group v-model="radio" direction="horizontal" style="font-size: 20px;display: flex;align-items: center;justify-content: center">
          <van-radio name="1">拒绝</van-radio>
          <van-radio name="2">同意</van-radio>
          </van-radio-group>

          data() {
          return {
          radio: '1',
          myform: {
          type: "绑定安全帽审核",
          position:this.$route.query.position,
          reviewStatus:this.radio,//2同意,1拒绝
          }
          };
          }

          因为data里的this是指父级作用域的上下文,所以this.radio并拿不到其本身作用域的radio
          需要的话可以通过mounted,将radio赋值给reviewStatus:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          export default {
          data () {
          return {
          radio: 1,
          reviewStatus: undefined
          }
          },
          moounted: function () {
          this.reviewStatus = this.radio
          console.log(this.reviewStatus) // 1
          }
          }

          36.BigDecimal 进行浮点数精确运算时

          利用 BigDecimal.valueOf 方法构造对象的方法,获得的浮点数发生了精度异常。(当 BigDecimal.valueOf(…) 的入参是 float 类型时,BigDecimal 会把入参强制转换成 double 类型。假如直接使用 new BigDecimal(double val) 构造函数来进行运算,则会发现计算结果发生来精度异常)

          利用 new BigDecimal(String val) 方法,运算正确

          37.LocalDateTime如果秒数是00的话,会自动去掉 比如00:00:00 转为LocalDateTime后变为00:00

          38.java的类不能多继承,但是接口可以多继承,却不能implements任何接口

          39.设计思路:对于订单的延时取消,可以在mysql的数据字段给个过期时间(也可以根据创建时间来判断是否过期),过期了就在查之前更新,将更新后的结果返回。没有过期就是正常查询。

          40.url(前后端接口)的命名规范:

          ​ URL请求采用小写字母,数字,部分特殊符号(非制表符)组成(尽量使用一个单词,如果非要用2个,加_即可 /module/tickets/recently_closed)

          ​ URL请求中不采用大小写混合的驼峰命名方式,尽量采用全小写单词,如果需要连接多个单词,则采用连接符“_”连接单词

          需要分级的话:

          ​ 第一级Pattern为模块,比如组织管理/orgz, 网格化:/grid

          ​ 第二级Pattern为资源分类或者功能请求,优先采用资源分类。

          ​ 如果为资源分类,则按照RESTful的方式定义第三级Pattern,RESTful规范中,资源必须采用资源的名词复数定义。

          例子:/orgz/members/120

          41.redis里的缓存还没到时间就自动消失:是因为redis作为docker容器运行在docker里,而当初创建容器的时候没有设置时间参数,导致使用了docker的默认时间UTC,导致用的是国际时间,而你往redis里存的时候使用的是本地时间即上海地区时间,存入的时候正常,在容器里检测过期了8小时就会自动删除。

          需要设置redis容器的时间为上海地区的时间。

          42.redis莫名其妙会有保存的数据丢失,查看日志:

          1
          Possible SECURITY ATTACK detected. It looks like somebody is sending POST or Host: commands to Redis. This is likely due to an attacker attempting to use Cross Protocol Scripting to compromise your Redis instance. Connection aborted.

          可能是被人攻击了,删除了,需要设置redis的密码

          43.把一个任意类型的值转换为布尔类型:用!! 一个!是取非 再一个!又取非 相当于把这个数据转换为boolen类型了

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          前端转换:
          // 强制转换为Boolean 用 !!
          var bool = !!"c";
          console.log(typeof bool); // boolean

          // 强制转换为Number 用 +
          var num = +"1234";
          console.log(typeof num); // number

          // 强制转换为String 用 ""+
          var str = ""+ 1234;
          console.log(typeof str); // string

          //强制转换为Number,用 - 0

          44.vant框架的list组件:

          页面加载完成后默认会自动加载一次,可以:immediate-check=”false”这样设置一下,页面加载完成后就不会自动加载一次了(此时可以在created方法或mounted方法中手动加载第一页的东西)

          45.手写的一个aop切面的日志注解,加在方法A上可以正常切面打印日志,如果方法A里调用了方法B,但是方法B上的切面日志注解就不会起作用。

          aop切面的执行正常顺序为:@Before ,@Around,@After,@AfterReturning,@AfterThrowing

          从Spring5.2.7开始,在相同@Aspect类中,通知方法将根据其类型按照从高到低的优先级进行执行:@Around,@Before ,@After,@AfterReturning,@AfterThrowing

          46.insert之后返回的返回值实际是增加的数据列数,并不是主键id。要想获取主键id,只需要在insert之后直接get该对象的主键id即可

          1
          2
          3
          4
          5
          6
          7
          //新增银行卡
          BankCard bankCard = new BankCard();
          bankCard.setBankCardRealName(bankCardRequest.getRealName());
          bankCard.setBankCardNum(bankCardRequest.getBankCardNum());
          bankCard.setBankName(bankCardRequest.getBankName());
          bankCardMapper.insert(bankCard);
          Long id = bankCard.getId();

          47.vue页面初始化时声明一个数组的属性,然后axios后端返回的值可以直接赋值。提交数据后将该数组置为{}会导致下一次赋值无法正确赋值,需要采用vue.set或者this.$set。也可以在清空数据的同时把原字段重新初始化

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          adminForm: {
          nodeNum: '',
          helmetColor: ''
          }

          1.用完之后清空 this.adminForm = {};

          再次使用时动态绑定上 this.$set(this.adminForm, 'helmetColor',res.data.data.helmetColor);

          2.清空的时候同时初始化,就可以直接赋值
          this.adminForm: {
          nodeNum: '',
          helmetColor: ''
          }
          this.bindForm.nodeNum = res.data.data.nodeNum

          48.设置数据库对应的entity的属性为null。1.设置字段填充策略,会产生影响。2.写一个sql语句,调用设置字段为null。

          49.

          1
          2
          3
          4
          String S= "1";
          Integer i = (Integer) s;//会报错:String不能强转Integer,因为string强转Integer时不能加括号强转
          需要:
          Integer i = Integer.parseInt(s);

          50.包装类为空的话,不能直接赋值给基础类,不然会报null。因为自动拆箱的时候报空。

          1
          2
          String S= null;
          int i = s;//会报错:string转int涉及到自动装箱和拆箱

          51.不能以名字作为参数判断条件,因为可能出现重名,要以id作为判断条件

          52.微信复制的内容可能会将空格替换。用postman复制别人微信发的post的body格式时踩到的坑。

          53.idea里代码大部分飘红,但是项目可以正常启动。重新载入maven依赖也没起作用。

          是因为IDEA有缓存,只需要刷新一下缓存就好了。解决方案:File -> Invalidate Caches / Restart 然后选择Invalidate and Restart。

          54.spring的事务和redis的“事务”

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          加@Transactional
          1.mysql没有插入,事务生效
          Company company=new Company();
          company.setName("1号公司1");
          companyMapper.insert(company);
          System.out.println(10/0);
          2.mysql没有插入,事务生效
          Company company = new Company();
          company.setName("1号公司2");
          companyMapper.insert(company);
          redisService.test("test");
          3.mysql没有插入,redis数据更改了
          Company company = new Company();
          company.setName("1号公司2");
          companyMapper.insert(company);
          redisService.set("test","aaa");
          System.out.println(10/0);
          4.redis数据更改,mysql没有插入(name设置为不能插入重复字段)
          redisService.set("test","ccc");
          Company company = new Company();
          company.setName("1号公司1");
          companyMapper.insert(company);
          结论:事务只对mysql生效,redis数据自己成功就是成功,失败就是失败

          redis的事务其实不是事务,因为redis操作都是原子性的。如果命令可以执行成功则一定被执行且无法回滚。而spring的事务只要抛出异常,就会将方法内所有内容(除mysql之外的东西是否回滚待测试)回滚,reids不会回滚。

          55.实体类的BigDecimal类型字段,在序列化后或者转换为json后,小数点后的0会被自动去掉

          1
          2
          3
          4
          5
          6
          DecimalFormat df = new DecimalFormat("#0.00");--指定保留两位小数
          p.setPrice(p.getPrice==null?null:df.format(new BigDecimal(p.getPrice)));

          或者在该对象的字段上
          @JsonFormat(shape = JsonFormat.Shape.STRING)
          private BigDecimal amt;

          上述方法会将BigDecimal类型转为String类型,其实没有达到解决的目的。

          56.前端金额显示为xx.00元:

          如果字段为String类型:return (parseInt(price*100)/100).toFixed(2);

          如果字段为Number类型:(price * 100 / 100).toFixed(2)

          57.redis的redisTemplate.boundValueOps(key).set(value);方法中,key为test:::则是在redis分了test一层, 二,三,四层,一共四层

          58.方法A调用异步方法B,如果方法A回滚了,方法B仍会执行,所以最好的办法就是在方法A执行结束确保没问题了,再去通知异步执行。

          59.Vue的async表示该方法是异步方法。await表示等待,表示是同步方法。.then(res => { }); 表示一个同步方法;但是当方法上有async时就是一个异步方法。

          await要放在异步方法里面使用,和promise或者async结合使用。无论是使用箭头函数,还是使用await,最终结果都是获得promise

          60.vue-amap获取地图实例:this.amap = this.$refs.map.$$getInstance();

          61.问题:

          • 后端在序列化json时,在BigDecimal长度大于17位(不包括小数点)会出现精度丢失,在Long长度大于17位时也会出现精度丢失的问题。

          • 前端请求后端接口获取BigDecimal类型字段数值时丢失精度,例如:5999.00变成5999、5999.50变成5999.5

            治标方法:(parseInt(price*100)/100).toFixed(2) (price * 100 / 100).toFixed(2) 将小数点后截断为保留两位

          解决:

          • 类属性直接定义成String类型,处理好之后再返回前端

          在代转化的字段上加上 @JsonFormat(shape = JsonFormat.Shape.STRING)

          在待转化的字段之上加上@JsonSerialize(using=ToStringSerializer.class),但是仍有问题:对BigDecima类型的属性进行转换之后发现,数据后尾会多个0,这是因为这个ToStringSerializer.class类直接对该属性进行了toString()操作

          正常来说对BigDecima类型的属性打印都是用:

          1
          decimalObject.stripTrailingZeros().toPlainString()

          针对BigDecima类型处理重新写一个专门的子类:

          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
          public class BigDecimalToStringSerializer extends ToStringSerializer {
          public static final BigDecimalToStringSerializer instance = new BigDecimalToStringSerializer();

          public BigDecimalToStringSerializer() {
          super(Object.class);
          }

          public BigDecimalToStringSerializer(Class<?> handledType) {
          super(handledType);
          }

          @Override
          public boolean isEmpty(SerializerProvider prov, Object value) {
          if (null == value) {
          return true;
          }
          String str = ((BigDecimal) value).stripTrailingZeros().toPlainString();
          return str.isEmpty();
          }

          @Override
          public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
          gen.writeString(((BigDecimal) value).stripTrailingZeros().toPlainString());
          }

          @Override
          public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
          return this.createSchemaNode("string", true);
          }
          }

          再在需转换字段上加上:
          @JsonSerialize(using = BigDecimalToStringSerializer.class)
          • 自定义消息转换器

          如果系统已经成型,上面两种做法会改动很多地方,所以可以添加拦截器将BigDecimal转为String

          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
          46
          47
          48
          49
          50
          51
          52
          53
          54
          55
          56
          57
          58
          59
          60
          61
          62
          63
          64
          65
          66
          67
          68
          69
          70
          71
          72
          73
          74
          75
          76
          77
          78
          79
          80
          81
          82
          83
          84
          85
          86
          87
          88
          89
          90
          91
          92
          93
          94
          95
          @Configuration
          public class WebMvcConfig extends WebMvcConfigurerAdapter {

          @Autowired
          private TokenInterceptor tokenInterceptor;


          @Override
          public void addInterceptors(InterceptorRegistry registry) {
          /**
          * 添加请求鉴权拦截器
          */
          registry.addInterceptor(tokenInterceptor) //注册拦截器
          .addPathPatterns(PathPattern.API_ENTRY_POINT)//添加已注册拦截器应用于的URL
          .excludePathPatterns(PathPattern.NO_AUTH_API_ENTRY_POINT);//添加注册拦截器不应该应用于的URL
          }

          @Override
          public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
          /**
          *ViewResolver使用所请求的媒体类型的一个实现
          */
          configurer.favorPathExtension(false);//是否应使用URL路径中的路径扩展来确定所请求的媒体类型
          }

          @Override
          public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
          /**
          *自定义转换器
          */
          converters.add(toStringConverter());//增加了序列化long及包装类Long需要使用ToStringSerializer转成String类型
          }

          /**
          * BigDecimal Long 转化为String
          * @return
          */
          @Bean
          public MappingJackson2HttpMessageConverter toStringConverter() {
          MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
          ObjectMapper mapper = new ObjectMapper();
          SimpleModule simpleModule = new SimpleModule();
          simpleModule.addSerializer(BigDecimal.class, BigDecimalToStringSerializer.instance);
          simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
          simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
          simpleModule.addSerializer(long.class, ToStringSerializer.instance);
          mapper.registerModule(simpleModule);
          converter.setObjectMapper(mapper);
          return converter;
          }

          @JacksonStdImpl
          static class BigDecimalToStringSerializer extends ToStringSerializer {
          /*
          *因为直接用ToStringSerializer,BigDecimal的值过小就会显示科学计数法,也不能去除末尾无效的零。这边自定义一个BigDecimalToStringSerializer继承自ToStringSerializer,修改里面的serialize、isEmpty方法
          */
          public final static BigDecimalToStringSerializer instance = new BigDecimalToStringSerializer();

          public BigDecimalToStringSerializer() {
          super(Object.class);
          }

          public BigDecimalToStringSerializer(Class<?> handledType) {
          super(handledType);
          }

          @Override
          public boolean isEmpty(SerializerProvider prov, Object value) {
          if (value == null) {
          return true;
          }
          String str = ((BigDecimal) value).stripTrailingZeros().toPlainString();
          return str.isEmpty();
          }

          @Override
          public void serialize(Object value, JsonGenerator gen, SerializerProvider provider)
          throws IOException {
          gen.writeString(((BigDecimal) value).stripTrailingZeros().toPlainString());
          }

          @Override
          public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
          return createSchemaNode("string", true);
          }

          @Override
          public void serializeWithType(Object value, JsonGenerator gen,
          SerializerProvider provider, TypeSerializer typeSer)
          throws IOException {
          // no type info, just regular serialization
          serialize(value, gen, provider);
          }
          }
          }

          62.商城项目里如果历史订单里的商品属性发生变化,已经生成的订单不能更改其属性,所以当时生成的订单需要一个表来当作快照保存

          63.数据库的查询字段不区分大小写:

          在创建数据库的时候,选择了utf8_general_ci排序规则

          创建数据库时,需要同时选择字符集和排序规则,排序规则:是指对指定字符集下不同字符的比较规则

          • 两个不同的字符集不能有相同的排序规则

          • 两个字符集有一个默认的排序规则

          • 有一些常用的命名规则:如 _ci 结尾表示大小写不敏感(case insensitive collation),_cs表示大小写敏感(case sensitive collation),_bin表示二进制的比较(binary case sensitive collation):对于CHAR、VARCHAR和TEXT类型,BINARY属性可以为列分配该列字符集的 校对规则。BINARY属性是指定列字符集的二元 校对规则的简写。排序和比较基于数值字符值。因此也就自然区分了大小写

          5.6版本的mysql是不支持utf8的cs排序规则,如果要想对大小写敏感,可以使用_bin的排序规则

          使用show COLLATION;查询当前版本的数据库支持的所有排序规则。

          使用 show charset like 'utf8%';进一步查看当前字符集的默认排序规则是 什么

          将其变为大小写敏感:

          • 创建数据库时:字符集设置为utf8,排序规则设置为utf8_bin

          • 对于已经建好的表,可以修改表的属性:在default前加binary。也可以写sql语句:

          ALTER TABLE TABLENAME MODIFY COLUMN COLUMNNAME VARCHAR(50) BINARY CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL;

          • 可以将查询条件用binary()括起来:
          1
          select * from TableA where binary columnA ='aaa';

          64.vue可以自动将<style></style>代码块内的样式代码px转换成rem。但是如果是写在html元素里的内联样式style=“ ”里的px就不会自动转换。所以需要将内联样式写成rem或者将样式写在对应的style选择器里才会保证移动端的不同机型不会出现样式错乱。

          65.v-if和v-else的问题:当页面加载时,会先将if和else的内容都加载到页面上,然后根据条件隐藏掉不用显示的,就会导致页面好像跳转了。解决方案:

          1
          2
          3
          4
          5
          6
          7
          网页端在style的代码块里加入:
          [v-cloak] {
          display:none;
          }
          再在if和else的代码块后面加入v-cloak
          但是此方案对移动端不生效,可以考虑加一个过渡状态:
          通过自定义一个变量 isLoading 在data里初始化为 false ,在数据请求成功之后将变量改为 true ,在 template 中通过变量来控制是否显示隐藏。这样会使页面进来时显示的是空,等请求到数据之后才会出现应该出现的页面。

          66.后端自定义打包后的文件名字:

          在pom.xml里:

          1
          2
          3
          <build>  
          <finalName>你的打包后的文件名字,例如:${project.artifactId}-${project.version}-company</finalName>
          </build>

          前端自定义打包后的文件名字:

          1
          2
          3
          module.exports = {
          outputDir: 'shop',//打包后生成的文件夹名字
          }

          67.ios只能识别/,不能识别-

          1
          createTime.replace(/-/g, "/")          /g 代表全局

          68.移动端Vant组件库REM适配:https://blog.csdn.net/jyn15159/article/details/109325998

          Vant 中的样式默认使用px作为单位,如果需要使用rem单位:

          • postcss-pxtorem 是一款 postcss 插件,用于将单位px转化为 rem。
          • lib-flexible 用于设置 rem 基准值,设置 font-size 基准值

          68.前端判断一个数组是否为空不能直接判断this.array。而应该判断this.array.length

          69.后端springboot项目使用ThreadLocal的一个坑:

          ThreadLocal里set值后由于没有清除操作(清除操作写在了Filter的destroy方法,该方法只有在服务器停止时才会执行),导致之前set的值一直在这个线程里。当有一个请求没有set值,但是get取值时,会取到上一个线程的set值,导致出错。

          原因:

          应用程序或(请求线程)容器使用线程池,这意味着线程不会死。如果需要,你需要自己处理线程局部。唯一干净的方法是调用ThreadLocal.remove()方法。

          有两个原因,你可能想清除线程本地线程的线程池中的线程:

          • 以防止记忆(或假设资源)泄漏

          • 以防止意外的信息从一个请求泄漏到另一个通过线程本地的另一个请求。

          线程本地内存泄漏通常不是有界线程池的主要问题,因为任何线程局部变量最终都可能被覆盖;即当线被重复使用时。但是,如果你犯了重复创建一个新的ThreadLocal实例的错误(而不是使用静态变量来保存单例实例),线程本地值将不会被覆盖,并将累积在每个线程的threadlocals映射。这可能导致严重的泄漏。

          假设你正在谈论在webapp处理HTTP请求时创建/使用的线程局部变量,那么避免线程局部泄漏的一种方法是使用webapp的ServletContext注册ServletRequestListener,并实现监听器的requestDestroyed方法来清除线程当前线程的本地化。

          70.项目的前端商城的图片是从数据库取图片在服务器上的地址字符串,页面拼接域名成为一个完整的url后请求图片。当我把服务器上的图片更新覆盖后,图片还是之前的图片:刷新网页、resin和tomcat都是这种情况。和浏览器缓存没有关系。路径也绝对正确

          因为浏览器首次读取服务端的图片之后,再次读取同名图片,会直接从临时文件中读取,不再请求服务端。如果清除浏览器缓存,则图片更新。

          解决方法:1.修改图片名,给图片加上时间戳。让图片名不一样。2.url上可以加版本号,用随机数、计数器或时间戳作为参数,对于浏览器来说就是一个新的uri,不会访问缓存,而是加载新的东西

          71.前端项目的依赖可以在node_modules文件夹下查找,这里面实际上是所依赖的具体项目所有内容。如果想查看官方文档而github打开又很慢的话,也可以在这里查找。

          72.修改mysql密码:%和localhost是两个东西,一个是远程链接,一个是本地链接

          73.关于mapper更新:如果一个实体类实例是new出来的,set一些字段,update时其他字段保持不动。而如果是查出来的实体类实例,set一些字段,update时其他字段也是保持不动,但是是当时查出来的字段。如果set某字段为null,那么update时会忽略这个字段,不用当时查出来的字段。(解决的问题:在查出一个用户的属性时,他有公司属性,但是通过mapper的自定义方法让他公司属性变为null之后,set值再update发现公司属性又被写上了。原因:update时的实例是当时查出来带有公司属性的字段,必须将其设置为null才可。)

          74.

          1
          2
          3
          4
          5
          6
          7
          8
          9
          int 在C和C++的占用2个字节,在java中4个字节
          char在C和C+中占一个字节,在Java中无论是汉字还是英文字母都是用Unicode编码来表示的,一个Unicode码是16位,每字节是8位,所以一个Unicode码占两字节。但是英文字母比较特殊,源自于8位(1字节)的ASCII吗,于是在Unicode码仅使用了低8位(1字节)就可以表示,高8位的话不使用也无所谓。所以

          char c='a';
          System.out.println(c.getBytes().lenth()),得到的是1(字节)

          但汉字就完整地使用了16位(2字节)的Unicode,所以
          char c='中';
          System.out.println(c.getBytes().lenth()),得到的是2(字节)

          75.工具类中不要打印日志,第三方日志,为了降低依赖:被人用你的工具类不需要再引入其他依赖。

          扫一扫,分享到微信

          微信分享二维码
          java中常用类
          初识java
          © 2021 swx
          Hexo Theme Yilia by Litten
          • 所有文章
          • 关于我

          tag:

          • 杂谈
          • 经历
          • 与人沟通
          • 项目合作
          • 搜索技巧
          • 百度
          • 谷歌
          • 书
          • 收藏
          • 打包
          • swagger
          • springfox
          • mybatis
          • Portainer.io
          • Typora
          • 快捷键
          • 压缩算法
          • PBOOTCMS
          • Eclipse
          • 新环境
          • 软件
          • 软件说明
          • WebRTC
          • 类型转换
          • JVM
          • 接口
          • 抽象类
          • 内部类
          • if
          • 逻辑判断
          • for循环
          • Tomcat
          • http
          • vite
          • WEB
          • servlet
          • JSP
          • JPA
          • json
          • 数据库
          • SQL
          • 小技巧
          • 小科普
          • git
          • Gitee
          • hexo
          • node.js
          • ElasticSearch
          • 基础概念
          • 面向对象
          • mysql
          • 索引
          • 锁
          • 事务
          • Navicat
          • JQuery
          • mqtt
          • 代理
          • 工具类代码
          • 轮子
          • 消息中间件
          • 设计规范
          • 编程规范
          • RESTful
          • HATEOAS
          • java概述
          • 历史版本
          • 安装
          • MySQL基础概念
          • HTML
          • 日志
          • PO-BO-VO,DTO-DAO与POJO
          • spring
          • maven
          • C
          • IDEA
          • 插件
          • 微信
          • Redis
          • ngnix
          • MyBatis-Plus
          • npm
          • vue
          • SpringBoot

            缺失模块。
            1、请确保node版本大于6.2
            2、在博客根目录(注意不是yilia根目录)执行以下命令:
            npm i hexo-generator-json-content --save

            3、在根目录_config.yml里添加配置:

              jsonContent:
                meta: false
                pages: false
                posts:
                  title: true
                  date: true
                  path: true
                  text: false
                  raw: false
                  content: false
                  slug: false
                  updated: false
                  comments: false
                  link: false
                  permalink: false
                  excerpt: false
                  categories: false
                  tags: true
            

          一起加油吧!