java类型转换
下面代码如果不加L,将导致int类型超范围,不会自动升级为Long,从而导致结果与实际不符。
public static boolean isValidity(String faceResultTime, int internalHour) {
boolean valid = false;
if (StringUtils.isNotEmpty(faceResultTime)) {
valid = (System.currentTimeMillis() - DateUtils.parseDateTime(faceResultTime).getTime()) > internalHour * 3600 * 1000L ? false : true;
}
return valid;
}
请求头过大
解决方式
#端口配置
server:
port: 8090
tomcat:
# 配置tomct的基本目录(用于存放临时文件等数据)
basedir: /home/credit/xx/runtime
#配置http传输支持压缩
compression:
enabled: true
mime-types: application/json,application/xml,text/html,text/xml,text/plai
max-http-header-size: 10000000
公司固话
/**
* 公司固定电话
* 0-9的数字或-,且11位或12位
*/
@NotBlank(message = "公司固定电话不能为空")
@Pattern(regexp = "(0\\d{2,3}-[1-9]\\d{6,7})|(^[1][3,4,5,7,8][0-9]{9}$)", message = "公司固定电话不满足电话格式")
private String companyLandline;
关于方法是抛出异常还是包装后返回
在京东时,针对已知的异常,比如参数校验错误、用户操作错误、登录密码错误。这些通过校验可以知道的异常,是通过在service封装返回的。如果是不知道的异常,则会报错,只要报错,就会通过切面的方式将错误信息推送给我们开发人员手机上。所以,报错就是我们未知的错误或者是代码的BUG。这也是alibaba编码规范中的要求。
但是在美的,很多人习惯用throw new Exception()的方式进行抛出异常,只要不满足要求,就会抛出来。
基本类型包装类比较
包装类对象不可使用“==”符做比较运算,如果要进行比较运算时,最好使用java类库中的compareTo方法。
注意不要用不同类型的数值比较。
如下:
String a = new String("1");
Integer b = new Integer(1);
System.out.println(a.equals(b));
jdk8中groupingBy的用法
/**
*
*
* @param clientId
* @param applyNo
* @return
*/
private VoucherInfo2Vo getVoucherInfo(String clientId, String applyNo) {
VoucherInfo2Vo voucherInfoVo = new VoucherInfo2Vo();
XdImageFile voucherImage = ymImageStoreService.getApplyFileByType(clientId, applyNo, ImageFileType.IMG_CONSUMER_VOUCHERS.getCode());
List<XdImageFile> otherMaterialListFile = getApplyFileListByType(clientId, applyNo, TCLDict.FILE_STYLE_OTHER_MATERIAL);
if (voucherImage != null) {
ConsumeProofVo consumeProofVo = new ConsumeProofVo();
consumeProofVo.setFileId(voucherImage.getId());
consumeProofVo.setFileType(voucherImage.getFileType());
consumeProofVo.setImageThumbUrl(ymImageStoreService.getImgUrl(voucherImage.getThumbBatchNo(), voucherImage.getThumbFileName()));
consumeProofVo.setImageUrl(ymImageStoreService.getImgUrl(voucherImage.getOriginBatchNo(), voucherImage.getOriginFileName()));
voucherInfoVo.setConsumeProof(consumeProofVo);
}
if (CollectionUtils.isNotEmpty(otherMaterialListFile)) {
Map<String, List<XdImageFile>> map = otherMaterialListFile.stream().collect(
Collectors.groupingBy(e -> e.getFileType()));
List<OtherProofVo> otherProofVoList = new ArrayList<>();
for (Map.Entry<String, List<XdImageFile>> entry : map.entrySet()) {
OtherProofVo otherProofVo = new OtherProofVo();
otherProofVo.setProofType(entry.getKey());
otherProofVo.setProofTypeDesc(ImageFileType.parse(entry.getKey()).getDesc());
List<ConsumeProofVo> consumeProofVoList = new ArrayList<>();
List<XdImageFile> xdImageFileList = entry.getValue();
for (XdImageFile f : xdImageFileList) {
ConsumeProofVo consumeProofVo = new ConsumeProofVo();
consumeProofVo.setFileId(f.getId());
consumeProofVo.setFileType(f.getFileType());
consumeProofVo.setImageThumbUrl(ymImageStoreService.getImgUrl(f.getThumbBatchNo(), f.getThumbFileName()));
consumeProofVo.setImageUrl(ymImageStoreService.getImgUrl(f.getOriginBatchNo(), f.getOriginFileName()));
consumeProofVoList.add(consumeProofVo);
}
otherProofVo.setProofFileList(consumeProofVoList);
otherProofVoList.add(otherProofVo);
}
voucherInfoVo.setOtherProof(otherProofVoList);
}
return voucherInfoVo;
}
null的输出
String ss = null;
System.out.println(ss);//null
System.out.println(ss + "") ;//null
System.out.println(ss + " ") ;//null (前面有空格)
关于java包装类型比较
低于128的时候,IntegerCache会缓存。
Integer a = null;
System.out.println(a == 512);//NullPointerException
Integer b = new Integer(1024);
Integer c = new Integer(1024);
System.out.println(b == c);//false
System.out.println(b.equals(c));//true
为什么equals可以,请看Integer类equals源码。
Integer.java
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
身份证尾号为x
#face++人脸时,忽略身份证大小写对比
xdBasecust.getCertNo().equalsIgnoreCase(idCardORCResult.getString("id_card_number")
联系人姓名不能含有表情符及个人邮箱
/**
* 联系人姓名
*/
@NotBlank(message = "联系人姓名不能为空")
@Size(max = 20)
@Pattern(regexp = "(?!([\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]))", message = "居住地址中不能含有表情符")
private String name;
/**
* 联系人手机号
*/
@NotBlank(message = "联系人手机号不能为空")
@Pattern(regexp = "^1[0-9]{10}$", message = "联系人手机号不满足手机格式")
private String mobileNo;
/**
* 公司固定电话
* 0-9的数字或-,且11位或12位
*/
@NotBlank(message = "公司固定电话不能为空")
@Pattern(regexp = "(^0([0-9]|[-]){6,13}$)|(^[1][3,4,5,7,8,9][0-9]{9}$)", message = "请输入正确的公司电话")
private String companyLandline;
/**
* 个人邮箱
*/
@NotBlank(message = "个人邮箱不能为空")
@Pattern(regexp = "^[A-Za-z0-9(\\.)+\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$", message = "个人邮箱不满足邮箱格式")
private String personalMailbox;
在大多数JVM中,Object.hashCode()与内存位置无关
对于OpenJDK和HotSpot JVM,hashCode()是按需生成的,并存储在对象的标头中。 使用Unsafe,您可以查看是否已设置hashCode。
重写equals方法的时候为什么需要重写hashcode
equals()方法和hashcode()方法是Object类的两个方法。从下面的代码中可以看到equals方法比较的是内存地址是否相等,因此一般情况下也无法满足两个对象值的比较。
- 如果要满足两个对象的值是否相等,需要重写equals方法。
- 如果在使用HashMap等容器类时,用对象当做key进行比较时,如下代码,此时,需要重写hashCode是为了让同一个Class对象的两个具有相同值的对象的Hash值相等。
- 同时重写hashCode()与equals()是为了满足HashSet、HashMap等此类集合的相同对象的不重复存储。
//Object.java
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
//Test.java
Student s1 = new Student(1,"June");
Student s2 = new Student(1,"June");
Map<Object,String> testMap = new HashMap<Object,String>();
testMap.put(s1, "美国学生");
System.out.println(s1.equals(s2));//对象比较
那该如何重写该对象呢?如下仅仅是个示例。大家要根据自己的对象进行改写。
public class Student {
private int age;
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
}
double的使用
double a = 0.0d;
double b = 0d;
if(a == b) {//这两者是相等的
System.out.println("test");
}
java代码不能实现对jar包中的配置文件改写
原本打算读取IO流后写入流的方式进行重写jar包中的配置文件。但是这种方式在jar包中配置文件根本就不支持写。
@PostConstruct
public void initEAP() throws Exception {
org.springframework.core.io.Resource resource = new ClassPathResource("/conf/eap-ssapi.conf");
InputStream is = resource.getInputStream();
Properties configs = new Properties();
configs.load(is);
Config.getInstance().load("/conf/eap-ssapi.conf");
Config.getInstance().get("test");
configs.setProperty("loan", loanAddr);
String key = "loan";
MyProperties myProps = new MyProperties(configs);
FileOutputStream out = null;
FileLock lock = null;
is.close();
try {
out = new FileOutputStream(resource.getFile());
FileChannel channel = out.getChannel();
lock = channel.tryLock();
if (lock == null) {
throw new Exception("file is being edited: " + resource.getFile());
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
Date date = new Date();
myProps.store(out, "Last edit time: " + format.format(date));
} finally {
try {
out.close();
} catch (Exception var28) {
}
try {
lock.release();
} catch (Exception var27) {
}
}
Util.storeProperties2(resource.getURI().getPath(), configs, "loan");
}
mybatis标签selectkey无法返回主键值
too many connections 解决方法
-
show processlist;
查看连接数,可以发现有很多连接处于sleep状态,这些其实是暂时没有用的,所以可以kill掉 -
show variables like “max_connections”;
查看最大连接数,应该是与上面查询到的连接数相同,才会出现too many connections的情况 -
set GLOBAL max_connections=1000;
修改最大连接数,但是这不是一劳永逸的方法,应该要让它自动杀死那些sleep的进程。 -
show global variables like ‘wait_timeout’;
这个数值指的是mysql在关闭一个非交互的连接之前要等待的秒数,默认是28800s -
set global wait_timeout=300;
修改这个数值,这里可以随意,最好控制在几分钟内 -
set global interactive_timeout=500;
修改这个数值,表示mysql在关闭一个连接之前要等待的秒数,至此可以让mysql自动关闭那些没用的连接,但要注意的是,正在使用的连接到了时间也会被关闭,因此这个时间值要合适 -
批量kill之前没用的sleep连接,在网上搜索的方法对我都不奏效,因此只好使用最笨的办法,一个一个kill
select concat('KILL ',id,';') from information_schema.processlist where user='root';
(1)先把要kill的连接id都查询出来
(2)复制中间的kill id;内容到word文档
(3)替换掉符号“|”和回车符(在word中查询^p即可查询到回车符)
(4)把修改过的内容复制回终端,最后按回车执行!
项目中建议不使用mybatis plus插件
因Mybatis-plus存在SQL注入漏洞,不建议使用,如果要使用,请使用如下代码进行处理。
解决方案
@Configuration(proxyBeanMethods = false)
public class MybatisAutoConfiguration implements WebMvcConfigurer {
/**
* SQL 过滤器避免SQL 注入
* @param argumentResolvers
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new SqlFilterArgumentResolver());
}
/**
* 分页插件, 对于单一数据库类型来说,都建议配置该值,避免每次分页都去抓取数据库类型
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
过滤核心代码:
/**
*
* 解决Mybatis Plus Order By SQL注入问题
*/
@Slf4j
public class SqlFilterArgumentResolver implements HandlerMethodArgumentResolver {
private final static String[] KEYWORDS = { "master", "truncate", "insert", "select", "delete", "update", "declare",
"alter", "drop", "sleep" };
/**
* 判断Controller是否包含page 参数
* @param parameter 参数
* @return 是否过滤
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(Page.class);
}
/**
* @param parameter 入参集合
* @param mavContainer model 和 view
* @param webRequest web相关
* @param binderFactory 入参解析
* @return 检查后新的page对象
* <p>
* page 只支持查询 GET .如需解析POST获取请求报文体处理
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String[] ascs = request.getParameterValues("ascs");
String[] descs = request.getParameterValues("descs");
String current = request.getParameter("current");
String size = request.getParameter("size");
Page<?> page = new Page<>();
if (StrUtil.isNotBlank(current)) {
page.setCurrent(Long.parseLong(current));
}
if (StrUtil.isNotBlank(size)) {
page.setSize(Long.parseLong(size));
}
List<OrderItem> orderItemList = new ArrayList<>();
Optional.ofNullable(ascs).ifPresent(s -> orderItemList.addAll(
Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::asc).collect(Collectors.toList())));
Optional.ofNullable(descs).ifPresent(s -> orderItemList.addAll(
Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::desc).collect(Collectors.toList())));
page.addOrder(orderItemList);
return page;
}
/**
* 判断用户输入里面有没有关键字
* @return Predicate
*/
private Predicate<String> sqlInjectPredicate() {
return sql -> {
for (String keyword : KEYWORDS) {
if (StrUtil.containsIgnoreCase(sql, keyword)) {
return false;
}
}
return true;
};
}
}
本知识点参考:http://www.hackdig.com/05/hack-362820.htm
提交springboot启动速度
Spring 5.2.0+的版本,建议你的配置类均采用Lite模式去做,即显示设置proxyBeanMethods = false。Spring Boot在2.2.0版本(依赖于Spring 5.2.0)起就把它的所有的自动配置类的此属性改为了false,即@Configuration(proxyBeanMethods = false),提高Spring启动速度。
@FeignClient注解 中属性 contextId使用
比如我们有个user服务,但user服务中有很多个接口,我们不想将所有的调用接口都定义在一个类中。这个记得要升级下springboot的依赖包,低版本不支持。
Client 1
@FeignClient(name = "optimization-user")
public interface UserRemoteClient {
@GetMapping("/user/get")
public User getUser(@RequestParam("id") int id);
}
Client 2
@FeignClient(name = "optimization-user")
public interface UserRemoteClient2 {
@GetMapping("/user2/get")
public User getUser(@RequestParam("id") int id);
}
解决方案
解决方案可以增加下面的配置,作用是允许出现beanName一样的BeanDefinition。(不建议使用)
spring.main.allow-bean-definition-overriding=true
另一种解决方案就是为每个Client手动指定不同的contextId,这样就不会冲突了。
上面给出了Bean名称冲突后的解决方案,下面来分析下contextId在Feign Client的作用,在注册Feign Client Configuration的时候需要一个名称,名称是通过getClientName方法获取的:
本知识点参考:http://www.imooc.com/article/details/id/299213
Java获取菜单树
不建议嵌套层级超过4层。即建议最多3层。可以参考如下代码进行改写。
/**
* 获取树结构
*
* @param words
* @return
*/
public List<SoftwareTypeVO> getSwClassifyList(String words) {
//获取所有满足条件的行业
List<SwClassify> swClassifyList = swClassifyMapper.querySwClassifyByWords(words);
if (CollectionUtils.isEmpty(swClassifyList)) {
return null;
}
// 转化不含嵌套层级的对象
List<SoftwareTypeVO> softwareTypeVOList = new ArrayList<>();
swClassifyList.stream().forEach(e -> {
SoftwareTypeVO v = new SoftwareTypeVO();
v.setId(e.getSwTypeId());
v.setLevel(e.getCurLevel());
v.setPId(e.getParentNo());
v.setTypeName(e.getSwTypeName());
softwareTypeVOList.add(v);
});
//返回的树列表
List<SoftwareTypeVO> softwareTypeVOListRet = new ArrayList<>();
// 先找到所有的一级列表,放入树列表中
softwareTypeVOList.forEach(e -> {
if (e.getLevel().equals(new Integer(1))) {
softwareTypeVOListRet.add(e);
}
});
//遍历一级,开始查找子层级
softwareTypeVOListRet.forEach(er -> {
er.setChildren(getChildren(er.getId(), softwareTypeVOList));
});
//屏蔽掉第一级菜单
return softwareTypeVOListRet;
}
private List<SoftwareTypeVO> getChildren(Integer id, List<SoftwareTypeVO> softwareTypeList) {
// 查找二级子层级
List<SoftwareTypeVO> childList = new ArrayList<>();
softwareTypeList.forEach(e -> {
if (e.getPId().compareTo(id.longValue()) == 0) {
childList.add(e);
}
});
//递归查找查找二级的子层级
childList.forEach(e -> {
e.setChildren(getChildren(e.getId().intValue(), softwareTypeList));
});
return childList;
}