前言:本人毕业于2021年,刚刚接触java开发,在开发过程中发现自己什么都不懂,因此写下本文作为总结自己在初入开发时遇到的问题,希望有机会看到本文的家人们,避免踩我相同的坑(有些坑自己都觉得傻,本文等于小白成长日志)。本文包括一些java写法,规范,报错问题,常见问题等杂项,希望能帮助各位和我一样刚步入java开发的小伙伴。文中有什么不对的,也希望大家不吝指点。这也是我第一次尝试写博文,一些排版等问题日后也会向大家多多学习。星光不问赶路人 时光不负有心人(本文持续更新)
一、java 1.java8新特性 (1)stream流式操作 //使用流过滤
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.stream().filter(s->s.startsWith("张")).filter(s-> s.length() == 3).forEach(System.out::println);
//流map
//1.将流中的元素映射到另一个流中,这个是在后期经常用到的,比如方法所接收的返回值是A,但是接收的却是B
list.stream().map(item -> {
ProductInfoAttributes productInfoAttributes = new ProductInfoAttributes();
BeanUtils.copyProperties(item, productInfoAttributes);
productInfoAttributes.setProductId(productId);
productInfoAttributes.setAttributeId(IDGenerator.getId());
return productInfoAttributes;
}).collect(Collectors.toList());
//2.将String类型的流转换为Integer 类型
Stream stringStream = Stream.of("1", "2", "3", "4", "5", "6");
stringStream.map(str->Integer.parseInt(str)).forEach(System.out::println);
//3.方法需要返回的是List ,但是这里只有List,此时就要想到stream().map
public List queryNamesByIds(List ids){
List categories = this.categoryMapper.selectByIdList(ids);
return categories.stream().map(category ->
category.getName()).collect(Collectors.toList());
}
2.static使用(与属性注入的关系)
(1)static(主函数是static修饰)只能调用调用static方法
静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。
public abstract class A {
public static void func1(){
}
// public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static'
}
只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字,因为这两个关键字与具体对象关联。
public class A {
private static int x;
private int y;
public static void func1(){
int a = x;
// int b = y; // Non-static field 'y' cannot be referenced from a static context
// int b = this.y; // 'A.this' cannot be referenced from a static context
}
}
(2) 静态内部类(从类的加载顺序可知【加载原则先静态后非静态,先父类再子类】)
非静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才能用这个实例去创建非静态内部类。而静态内部类不需要。
public class OuterClass {
class InnerClass {
}
static class StaticInnerClass {
}
public static void main(String[] args) {
// InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
OuterClass outerClass = new OuterClass();
InnerClass innerClass = outerClass.new InnerClass();
StaticInnerClass staticInnerClass = new StaticInnerClass();
}
}
静态内部类不能访问外部类的非静态的变量和方法。不应该通过类实例访问静态成员,使用类名去访问
父类(静态变量、静态语句块)
子类(静态变量、静态语句块)
父类(实例变量、普通语句块)
父类(构造函数)
子类(实例变量、普通语句块)
子类(构造函数)
3.集合 (1)List中add和addAll的区别add是添加list对象,addAll是添加list中的每一个object
JDK1.8源码中文说明
http://blog.fondme.cn/apidoc/jdk-1.8-google
1.集合(工具类)ArrayList2.Arraysresult = Lists.newArrayList(); List list = lfCommodityInfoPlMapper.findTypeList(LfType.VIDEO); list.addAll(lfCommodityInfoPlMapper.findTypeList(LfType.GAME)); lists.newarraylist(): List list = new ArrayList (); new arraylist() : List list = Lists.newArrayList(); //Lists和Maps是两个工具类, Lists.newArrayList()其实和new ArrayList()几乎一模一样, 唯一它帮你做的(其实是javac帮你做的), 就是自动推导(不是"倒")尖括号里的数据类型.
//转string Arrays.toString3.Collections
//判断是否为空 Collections.isEmpty5.IO流
(1)读文件(输入流):
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("infilename")));
不管你从磁盘读,从网络读,或者从键盘读,读到内存,就是InputStream。
(2)写文件(输出流):
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("outfilename")));
不管你写倒磁盘,写到网络,或者写到屏幕,都是OuputStream
6.java自带util//CollectionUtils,集合工具类 https://blog.csdn.net/huangwenyi1010/article/details/53706297 //交并补差,是否相等,是否为空等等 CollectionUtils.isEmpty(childCategoryList); //StringUtils,字符工具类 StringUtils.isEmpty();二、Mysql 1)外连接查询/函数运用到相应的列
2)模糊查询写法select loi.order_no orderNo, loi.unit_price totalPrice, loi.account phone, loi.order_status status, lon.order_info cardInfo, lc.icon, lc.id categoryId, //三元表达式 IF(loi.sec_kill = 1, lcip.name, lci.name) commodityName, IF(loi.sec_kill = 1, lcip.type, lci.type) type, loi.created, //从该列的左边开始计算到第19位 LEFt(aon.gmt_payment, 19) payTime from lf_order_info loi left join lf_commodity_info lci on loi.code = lci.code left join lf_commodity_info_pl lcip on loi.code = lcip.code left join lf_category lc on lci.category = lc.id left join lf_order_notify lon on loi.order_no = lon.out_id left join alipay_order_notify aon on loi.order_no = aon.out_trade_no where loi.uid = #{uid} GROUP BY loi.id order by loi.created desc
name like concat(’%’,#{name},’%’);
3)foreach/trim区别//循环数组,开闭,分隔器#{id}
4)Mybatis插入list集合insert into lf_commodity_info code, name, value_str, type_code, member_type, price, category, underlined_price, selling_price, type, enable, created, updated, tax_rate, #{code}, #{name}, #{valueStr}, #{typeCode}, #{memberType}, #{price}, #{category}, #{underlinedPrice}, #{sellingPrice}, #{type}, #{enable}, #{created}, #{updated}, #{taxRate},
insert into lf_commodity_info(
id,
code,
name,
value_str,
type_code,
member_type,
category,
price,
underlined_price,
selling_price,
type,
enable,
tax_rate,
created,
updated)
values
(
#{item.id},
#{item.code},
#{item.name},
#{item.valueStr},
#{item.typeCode},
#{item.memberType},
#{item.category},
#{item.price},
#{item.underlinedPrice},
#{item.sellingPrice},
#{item.type},
#{item.enable},
#{item.taxRate},
#{item.created},
#{item.updated}
)
//sql语句
INSERT INTO student(name,age,sex)
VALUES("张三",12,"男"),("王三",12,"男")
5)关联查询
select icon
from lf_category a
right join lf_commodity_info b on a.type=b.type
where b.category = #{category}
5.1.sql左联右联的差别
左联接:已一边为主表保留其全部数据
6)嵌套查询select icon
from lf_category a
where a.type = (select type
from lf_commodity_info b
where b.category = #{category})
7)distinct关键字去重
//distinct去重查询
select icon
from lf_category a
where a.type = (select DISTINCT type from lf_commodity_info b where b.category = #{category})
//关联查询的去重
SELECt
a.id,
a.title,
a.total,
count( DISTINCT ( b.user_id ) ) AS 'read',//去重后计算该类别中的总数
a.time,
a.status
FROM
message a
RIGHT JOIN msg_read b ON a.id = b.msg_id
GROUP BY a.id//巧用分组,分组计算
三、Spring全家桶
1.注解
(1)Spring中异步注解@Async的使用、原理及使用时可能导致的问题
(2)
@requestBody(将json转成object对象,前端到后端交互时使用)
注解@RequestBody接收的参数是来自requestBody中,即请求体。一般用于处理非 Content-Type: application/x-www-form-urlencoded编码格式的数据,比如:application/json、application/xml等类型的数据。
@RequestParam():@RequestParam
注解@RequestParam接收的参数是来自HTTP请求体或请求url的QueryString中。
RequestParam可以接受简单类型的属性,也可以接受对象类型。
@RequestParam有三个配置参数:
required 表示是否必须,默认为 true,必须。
defaultValue 可设置请求参数的默认值。
value 为接收url的参数名(相当于key值)
(3)@PathVariable(接收类如aaa/{id})直接在url后的参数
(4)@Cacheable(用于缓存注解)
@Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,这个稍后会进行说明。需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable可以指定三个属性,value、key和condition。
(5)@Configration
@ConfigrationProperties(prefix=“lf”) //配置类的注解
(6)@RequestMapping请求和别的区别
@RequestMapping(name = " 支付宝 支付返回数据", value = "/aliPay/return_url", method = {RequestMethod.POST, RequestMethod.GET})
2.OpenFeign的使用
创建应用主类Application,并通过@EnableFeignClients注解开启Spring Cloud Feign的支持功能。
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients(basePackages = { "com.kyle.client.feign.inter" })
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
▪️定义HelloServiceFeign,接口@FeignClient注解指定服务名来绑定服务,然后再使用Spring MVC的注解来绑定具体该服务提供的REST接口。
@FeignClient(value = "hello-service-provider")
public interface HelloServiceFeign {
@RequestMapping(value = "/demo/getHost", method = RequestMethod.GET)
public String getHost(String name);
@RequestMapping(value = "/demo/postPerson", method = RequestMethod.POST, produces = "application/json; charset=UTF-8")
public Person postPerson(String name);
}
注意:这里服务名不区分大小写,所以使用hello-service-provider和HELLO-SERVICE-PROVIDER都是可以的。另外,在Brixton.SR5版本中,原有的serviceId属性已经被废弃,若要写属性名,可以使用name或value。
▪️接着,创建一个RestClientController来实现对Feign客户端的调用。使用@Autowired直接注入上面定义的HelloServiceFeign实例,并在postPerson函数中调用这个绑定了hello-service服务接口的客户端来向该服务发起/hello接口的调用。
@RestController
public class RestClientController {
@Autowired
private HelloServiceFeign client;
@RequestMapping(value = "/client/postPerson", method = RequestMethod.POST, produces = "application/json; charset=UTF-8")
public Person postPerson(String name) {
return client.postPerson(name);
}
@RequestMapping(value = "/client/getHost", method = RequestMethod.GET)
public String getHost(String name) {
return client.getHost(name);
MyDev
//添加注解,实现api接口
@RestController
public class IfClient implements LfHandleApi {
@Resource
private LfCommodityInfoMapper lfCommodityInfoMapper;
@Resource
private DocLfPayService lfPayService;
@Resource
private LfCategoryMapper lfCategoryMapper;
@Override
public void pay(LfOrderInfo lfOrderInfo) {
//查找商品
LfCommodityInfo commodityInfo = lfCommodityInfoMapper.findByCode(lfOrderInfo.getCode());
if (commodityInfo == null) {
throw new BizException(ErrorMessages.BASIC_0001, "商品不存在");
}
switch (commodityInfo.getType()) {
case LfType.VIDEO:
lfPayService.payVideo(commodityInfo, lfOrderInfo.getAccount(), lfOrderInfo.getOrderNo());
break;
case LfType.GAME:
case LfType.RECHARGE_CARD:
lfPayService.payGame(commodityInfo, lfOrderInfo.getAccount(), lfOrderInfo.getIp(), lfOrderInfo.getOrderNo());
break;
case LfType.PHONE_BILL:
lfPayService.payMobile(commodityInfo, lfOrderInfo.getAccount(), lfOrderInfo.getOrderNo());
break;
default:
throw new BizException(ErrorMessages.BASIC_0001, "商品类型不存在!");
}
}
@Override
public LfCommodityInfo findByCode(String code) {
//通过code查询
LfCommodityInfo lfCommodityInfo = lfCommodityInfoMapper.findByCode(code);
return lfCommodityInfo;
}
@Override
public String getIcon(Long category) {
return lfCategoryMapper.selectIcon(category);
}
@EnableFeignClients//远程调用入口
@SpringBootApplication
@MapperScan(value = "com.xfw.welfare.docking.lf.mapper")
public class DockingApplication {
public static void main(String[] args) {
SpringApplication.run(DockingApplication.class, args);
}
}
//api模块,去调用别的模块,定义api接口
@FeignClient(value = "${lf.namespace:}lf")
public interface LfHandleApi {
@GetMapping("/api/welfare-lf/pay")
void pay(@RequestBody LfOrderInfo lfOrderInfo);
@GetMapping("/api/welfare-lf/findByCode")
LfCommodityInfo findByCode(String code);
@GetMapping("/api/welfare-lf/getIcon")
String getIcon(@RequestParam("category") Long category);
}
理解feign的代码架构方式
三层架构的理解,lfclient,DocLfPayservice,DocLfPayserviceimpl 区别
一、远程调用类再主服务中,实现api中api接口方法(这里写url),需要注解@RestController,类似于实现类
3.@Scheduled定时任务cron表达式定时任务Scheduled
cron表达式详解https://help.eset.com/era_admin/65/zh-CN/cron_expression.html
@Component
public class Task
{
@Scheduled(cron="0/5 * * * * ? ") //每5秒执行一次
public void execute(){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //设置日期格式
System.out.println("欢迎访问 pan_junbiao的博客 " + df.format(new Date()));
}
//启动类添加注解
@SpringBootApplication
@EnableScheduling //开启定时任务
public class ScheduledDemoApplication
{
public static void main(String[] args)
{
SpringApplication.run(ScheduledDemoApplication.class, args);
}
4.配置文件的使用
server: //端口
port: ${APP_PORT:19970}
spring:
application:
name: ${order-server.namespace:}order
cloud:
nacos:
username: ${NACOS_SERVER_USERNAME:nacos}
password: ${NACOS_SERVER_PASSWORD:xfw1234}
server-addr: ${NACOS_SERVER_ADDR:172.16.9.159:8848}
config: //nacos中配置的前缀和yml后缀
prefix: order
enabled: true
file-extension: yml
discovery:
enabled: true
profiles:
active: @profiles.active@
main:
allow-bean-definition-overriding: true
四、Mybatis
1、传参问题
1.MyBatis BindingException: Parameter ‘a’ not found(mapper中的参数没有在xml里被使用)
2.解决
这里需要注意的是collection必须为array,否则会报错如下:
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [array]2、result问题
MyBatis.TooManyResultsException(封装的返回值不够)
3、异常java.sql.SQLIntegrityConstraintViolationException(违反完整性约束):
一、其实就是违反了数据库的唯一约束条件!也就是插入数据时,具有唯一约束条件的列值重复了。
根据我图中描述信息的展示可知,我的表中"test1"这一列是具有唯一约束的,现在插入列值时数据有重复了。所以务必确认插入数据的主键或者具有唯一性约束的列不要有重复数据!!!
二、缺少主键
4.sql片段的使用//5、trim剪切标签的使用标签和 标签的使用 select id, code, name, value_str, type_code, member_type, category, price, underlined_price, selling_price, type, enable, tax_rate, created, updated from lf_commodity_info where code=#{code}
//五、异常总结 1)数据截断异常insert into lf_commodity_info //separator分隔符//suffixOverrides后缀覆盖 code, name, value_str, type_code, member_type, price, category, underlined_price, selling_price, type, enable, created, updated, tax_rate, #{code}, #{name}, #{valueStr}, #{typeCode}, #{memberType}, #{price}, #{category}, #{underlinedPrice}, #{sellingPrice}, #{type}, #{enable}, #{created}, #{updated}, #{taxRate}, ( #{item.id}, #{item.code}, #{item.name}, #{item.valueStr}, #{item.typeCode}, #{item.memberType}, #{item.category}, #{item.price}, #{item.underlinedPrice}, #{item.sellingPrice}, #{item.type}, #{item.enable}, #{item.taxRate}, #{item.created}, #{item.updated} )
数据截断异常:(原因:导入的数据长度大于数据库长度)
Error updating database. Cause: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation
2)BadSqlGrammarException(只要报这个异常,直接将语句放到数据库调试) sql语句的问题,1.列名,2.sql语句的异常1.sql列数和参数不一样多
3)result问题MyBatis.TooManyResultsException(封装的返回值不够)
4)异常java.sql.SQLIntegrityConstraintViolationException(违反完整性约束):
一、其实就是违反了数据库的唯一约束条件!也就是插入数据时,具有唯一约束条件的列值重复了。
根据我图中描述信息的展示可知,我的表中"test1"这一列是具有唯一约束的,现在插入列值时数据有重复了。所以务必确认插入数据的主键或者具有唯一性约束的列不要有重复数据!!!
二、缺少主键
5)java.lang.IllegalArgumentException: Pattern cannot be null or empty个人经验,该传的参数没传,参数名称写错了,@requestBody/@requestParam 忘记注解
其他具体问题参考:https://blog.csdn.net/weixin_44259720/article/details/103185972
6)feign.FeignException$NotFound: [404] during [POST]feign调用失败,当代码确定没用问题的前提下,可能时分布式架构下,多服务启动造成的问题,解决方法在调用方服务中添加被调用方服务名
https://www.cnblogs.com/bowendown/p/11937159.html 下载安装地址
开发常用命令
tail -200f welfare-operation.nohup.out ()查看具体文件的后几行,查看部署环境下最后发生了什么问题
cd 进入文件
2.xxlJob分布式定时任务的使用官方文档:https://www.xuxueli.com/xxl-job/
项目结构:
快速入门:
1.建两个分布式事务模块
2.建数据库
3.导入依赖
4.xxlJob配置类(可将配置写入nocas等),必写不然通过代码是不能注册任务的
package com.xfw.welfare.message.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Slf4j
public class XxlJobConfig {
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.address}")
private String address;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
//执行器组件
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
log.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}
5.添加并启动任务为例(其实就是调用xxljob的接口)
//1.XxlJobInfo此参数其实理解上来就是数据库中的XxlJobInfo实体类,实体类见下方
//2.调用addAndStart见下
public Integer addAndStart(XxlJobInfo xxlJobInfo) {
//发送请求,到xxljobAdmin微服务的controller接口,详细结构见截图
HttpsRequest request = new HttpsRequest(addAndStartUrl + "/jobinfo/addAndStart");
// 任务参数
request.setJsonBody(JSONObject.toJSONString(xxlJobInfo));
if (request.executePostRequest()) {
String response = request.getResponse();
log.info("创建任务:response==={}", response);
JSONObject jo = JSON.parseObject(response);
if (!"200".equals(jo.getString("code"))) {
log.error("创建启动任务失败,jobId={}", xxlJobInfo.getId());
throw new RuntimeException(jo.getString("msg"));
// throw new BusinessException(jo.getString("msg"));
}
return jo.getInteger("content");
}
throw new RuntimeException("创建任务失败");
// throw new BusinessException("创建任务失败");
}
6.实体类(对标数据库中的xxljobinfo表)
package com.xfw.welfare.message.xxljob;
import lombok.Data;
import java.util.Date;
@Data
public class XxlJobInfo {
private int id; // 主键ID
private int jobGroup; // 执行器主键ID
private String jobDesc;
private Date addTime;
private Date updateTime;
private String author; // 负责人
private String alarmEmail; // 报警邮件
private String scheduleType; // 调度类型
private String scheduleConf; // 调度配置,值含义取决于调度类型
private String misfireStrategy; // 调度过期策略
private String executorRouteStrategy; // 执行器路由策略
private String executorHandler; // 执行器,任务Handler名称
private String executorParam; // 执行器,任务参数
private String executorBlockStrategy; // 阻塞处理策略
private int executorTimeout; // 任务执行超时时间,单位秒
private int executorFailRetryCount; // 失败重试次数
private String glueType; // GLUE类型 #com.xxl.job.core.glue.GlueTypeEnum
private String glueSource; // GLUE源代码
private String glueRemark; // GLUE备注
private Date glueUpdatetime; // GLUE更新时间
private String childJobId; // 子任务ID,多个逗号分隔
private int triggerStatus; // 调度状态:0-停止,1-运行
private long triggerLastTime; // 上次调度时间
private long triggerNextTime; // 下次调度时间
private String appName; // 执行器名称
}
7.调用上方5api
//创建并启动任务
XxlJobInfo xxlJobInfo = assemblyParam(msgDetRequest);
//添加并启用
Integer jobId = addAndStartApi.addAndStart(xxlJobInfo);
public XxlJobInfo assemblyParam(MsgDetRequest msgDetRequest) {
//将时间转化为cron表达式
String cron = DateUtil.getCron(msgDetRequest.getDate());
//生成jobId
Long jobId = IDGenerator.getId();
// jobGroup必须为 ( jobGroup必须为代表的是对应执行器,如果执行器不知道是多少就默认为 0,但是必须传执行器名称appName)
XxlJobInfo xxlJobInfo = new XxlJobInfo();
xxlJobInfo.setId(jobId.intValue());
xxlJobInfo.setJobGroup(0);
xxlJobInfo.setAppName("message-executor");
xxlJobInfo.setJobDesc("定时推送消息任务" + msgDetRequest.getTitle());
xxlJobInfo.setAddTime(new Date());
xxlJobInfo.setUpdateTime(new Date());
xxlJobInfo.setAuthor("admin");
xxlJobInfo.setScheduleType("CRON");
xxlJobInfo.setScheduleConf(cron);
xxlJobInfo.setMisfireStrategy("DO_NOTHING");
xxlJobInfo.setExecutorRouteStrategy("FIRST");
//TODO 设置jobHandler名称
xxlJobInfo.setExecutorHandler("messageHandler");
//TODO 任务参数 消息id
xxlJobInfo.setExecutorParam(String.valueOf(msgDetRequest.getMsgId()));
xxlJobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION");
xxlJobInfo.setExecutorTimeout(0);
xxlJobInfo.setExecutorFailRetryCount(3);
xxlJobInfo.setGlueType("BEAN");
xxlJobInfo.setGlueRemark("GLUE代码初始化");
xxlJobInfo.setGlueUpdatetime(new Date());
//TODO 调度状态:0-停止,1-运行 刚添加的调度状态为0(添加定时任务默认为开启)
xxlJobInfo.setTriggerStatus(1);
xxlJobInfo.setTriggerLastTime(0L);
xxlJobInfo.setTriggerNextTime(0L);
return xxlJobInfo;
}
3.redis使用
https://www.cnblogs.com/yechangzhong-826217795/p/11202316.html
使用安装redis 的坑
Can’t connect to redis-server解决 运行=》services.msc=》redis启用
4.Jrebel热部署(提高开发效率)https://www.haah.net/archives/6285.html
七、中间件的使用 1.nacos官方文档springCloud 引入nocas官方文档 https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
1.下载,
2.导入两个依赖
3.yml书写
server:
port: ${APP_PORT:19977}
spring:
application:
name: ${message.namespace:}message
cloud:
#重点:寻找nocas中的配置文件
nacos:
username: ${NACOS_SERVER_USERNAME:nacos}
password: ${NACOS_SERVER_PASSWORD:666}
server-addr: ${NACOS_SERVER_ADDR:172.16.9.149:666}
config:
prefix: message
enabled: true
file-extension: yml
discovery:
enabled: true
profiles:
active: dev
main:
allow-bean-definition-overriding: true
八、开发中Problem
1)spring中使用Junit测试
1、导入Junit依赖
2、注意测试类的路径必需与非测试类路径一样
可参考:https://blog.csdn.net/weixin_39800144/article/details/79241620
注解
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderTest {
@Resource
private OrderPremiumServiceImpl orderPremiumService;
@Resource
private baseInfoMapper baseInfoMapper;
@Resource
private baseProductItemsMapper productItemsMapper;
// @Test
// public void testCreateOrder(){
// CreatOrderRequest request = new CreatOrderRequest();
//
// orderPremiumService.createOrder()
// }
@Test
public void testSave() {
baseInfo baseInfo = new baseInfo();
baseInfo.setbaseInfoId(Long.valueOf(10));
baseInfoBuyerContact baseInfoBuyerContact = new baseInfoBuyerContact();
baseInfoBuyerContact.setBuyerId(Long.valueOf(20));
baseInfoSellerContact baseInfoSellerContact = new baseInfoSellerContact();
baseInfoSellerContact.setSellerId(Long.valueOf(30));
baseInfoReceInfo baseInfoReceInfo = new baseInfoReceInfo();
baseInfoReceInfo.setReceInfoId(Long.valueOf(50));
baseInfoLogistics baseInfoLogistics = new baseInfoLogistics();
baseInfoLogistics.setLogId(Long.valueOf(60));
baseInfoLogisticsItems baseInfoLogisticsItems = new baseInfoLogisticsItems();
baseInfoLogisticsItems.setLogItemsId(Long.valueOf(70));
baseProductItems baseProductItems = new baseProductItems();
baseProductItems.setId(Long.valueOf(80));
// orderPremiumService.saveOrder(baseInfo, baseInfoBuyerContact, baseInfoSellerContact, baseInfoReceInfo,
// baseInfoLogistics, baseInfoLogisticsItems, baseProductItems);
}
2)代码规范
分new ,get,set(分点组装从参数)
总new ,get,set(将分点组装进总参数)
类中定义方法,代码冗余,地区定义
// 2. 拼接省市区镇街道,调用1688api解析地址编码,调用内部类,并调用内部类中的方法(方法定义在类中) String districtCode = getDistrictCode(request.getDeliveryAddress().getAddressText());
字符串拼接,使用stringBuilder
public String getAddressText() {
StringBuilder text = new StringBuilder(); text.append(provinceText).append(cityText).append(areaText).append(townText).append(address);
return text.toString();}
3)分装json对象//jsonObject 返回json对象(如何封装json对象) public ArrayList4)发送Http请求/获取httpRequest和httpResponsephoneBill() { ArrayList result = Lists.newArrayList(); List list = lfCommodityInfoPlMapper.findTypeList(LfType.PHONE_BILL); list.forEach(LfCategory -> { JSONObject category = new JSONObject(); category.putOpt("id", LfCategory.getId()); category.putOpt("name", LfCategory.getName()); category.putOpt("icon", LfCategory.getIcon()); category.putOpt("rule", LfCategory.getRules()); category.putOpt("specifications", lfCommodityInfoPlMapper.findCommodityListByCategoryId(LfCategory.getId())); int dayOfMonth = DateUtil.thisDayOfMonth(); //每月的8-19号 有优惠 其他日期原价 category.putOpt("discount", dayOfMonth >= Constant.PHONE_BILL_DISCOUNT_START && dayOfMonth <= Constant.PHONE_BILL_DISCOUNT_END); result.add(category); }); return result; }
//1,hutool //发送http请求 HttpResponse response = HttpRequest.post(LFPayGameRequest.URL).form(BeanUtil.beanToMap(request)).timeout(10000).execute(); //2.HttpClient发送
//怎么获得httprequest和httpresponse
public abstract class baseHandler extends ResponseEntityExceptionHandler {
private static ServletRequestAttributes getRequestAttributes() {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
}
protected static HttpServletRequest getRequest() {
return getRequestAttributes().getRequest();
}
protected static HttpServletResponse getResponse() {
return getRequestAttributes().getResponse();
}
@ExceptionHandler(Throwable.class)
@ResponseBody
public Response> handleThrowable(Throwable ex) {
logger.error("baseHandler Error", ex);
return Response.error(new BusinessException(ErrorMessages.BASIC_0001));
}
}
5)封装对象,
//lf支付请求参数,封装方法
@Data
public class LFPayGameRequest {
//定义静态常量
public static final String URL = "http://open.jiaofei100.com/Api/PayGame.aspx";
private String APIID;
private String TradeType;
private String Account;
private String UnitPrice;
private String BuyNum;
private String TotalPrice;
private String OrderID;
private String CreateTime;
private String isCallBack;
private String GoodsID;
private String ClientIP;
private String Sign;
//定义类方法
public String generateSign(String appKey) {
String sb = "APIID=" + APIID +
"&Account=" + Account +
"&BuyNum=" + BuyNum +
"&ClientIP=" + ClientIP +
"&CreateTime=" + CreateTime +
"&GoodsID=" + GoodsID +
"&isCallBack=" + isCallBack +
"&OrderID=" + OrderID +
"&TotalPrice=" + TotalPrice +
"&TradeType=" + TradeType +
"&UnitPrice=" + UnitPrice +
"&APIKEY=" + appKey;
return MD5Util.md5Hex(sb).toUpperCase();
}
}
6)BigDecimal类型转化千万不能强制类型转换(精度丢失,详见阿里开发手册),转化(元,分,厘)【无非就是简单的乘除操作】
//BigDecimal
//单位 厘
BigDecimal price = infoModel.getUnderlinedPrice().multiply(new BigDecimal("1000")).setScale(0);
BigDecimal 分转元
BigDecimal feeNo = new BigDecimal(fee);//string 转 BigDecimal 分转元
feeNo = feeNo.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);//分转元
7)String 的正则表达式
fileName.matches("^.+\.(?i)(xlsx)$")//matches方法
8)pom文件引用的坑,用于抽取API模块时,依赖引用问题
主类中应用api即可,api中不能引用主类中的依赖
9)前后端文件传输https://blog.csdn.net/sdut406/article/details/85647982 MultipartFile与File的一些事,这两个词一般是Java中出现的吧,前者代表HTML中form data方式上传的文件,后者是文件系统的抽象,前者信息较少,只有二进制数据+文件名称
10)枚举类和属性类的区别属性类
public class LfType {
public final static int VIDEO = 1;
public final static int GAME = 2;
public final static int PHONE_BILL = 3;
public final static int RECHARGE_CARD = 4;
}
枚举类
package com.xfw.common.enums;
import lombok.Getter;
@Getter
public enum CodeResponseEnum {
PARAMETER_ERROR(10007, "参数错误"),
ORDER_TIMEOUT(10008, "订单超时"),
PARAMETER_VERIFICATION_ERROR(10009, "参数校验错误"),
AGENCY_ID_DOES_NOT_EXIST(10010, "代理商 ID 不存在"),
NUMBER_TO_LONG(10011, "订单号长度大于 36"),
AGENCY_STATUS_ERROR(10012, "代理商状态错误"),
INSUFFICIENT_BALANCE(10013, "账户余额不足"),
IP_ERROR(10014, "IP 地址验证失败"),
PHONE_ERROR(10015, "充值号码有误"),
NOT_SUPPORT(10016, "暂不支持该号码"),
BAN(10017, "禁止采购该商品"),
SUCCESS(10018, "订单提交成功"),
FAILED(10020, "订单提交失败"),
UNKNOWN(10021, "未知错误"),
DUPLICATE(10022, "订单号重复"),
NOT_SUPPORT_01(10024, "暂不支持该面值"),
PROCESSING(10025, "订单处理中"),
FAIL_TRANSACTION(10026, "交易失败"),
SUCCESS_TRANSACTION(10027, "交易成功"),
NOT_EXIST(10029, "订单不存在"),
LIMIT(10035, "限制5分钟内同一时间和同一个金额"),
MAINTENANCE(10036, "系统维护"),
NOT_STARTED(10037, "活动未开始"),
FINISH(10038, "活动已经结束"),
;
private final int code;
private final String msg;
CodeResponseEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public static CodeResponseEnum getEnum(int code) {
CodeResponseEnum result = null;
for (CodeResponseEnum s : values()) {//values 表示当前枚举类
if (s.getCode() == code) {
result = s;
break;
}
}
return result;
}
}
11)list.foreach的实际应用
productCategoryList.forEach(categoryDTO -> {
List childCategoryList = productCategoryFactory.getChild(allProductCategoryList,
categoryDTO.getId(), getAllCategoryList(), request.getQueryType());
if(Objects.nonNull(request.getQueryType()) && request.getQueryType() == 1){
//在商品分类界面调用,需要返回商品
int productNum = 0;
if(CollectionUtils.isEmpty(childCategoryList)){
List categoryIds = new ArrayList<>();
ProductCategoryRelWrapper.Selecter matcherSumProductList = new ProductCategoryRelWrapper.Selecter();
matcherSumProductList.categoryIdIn(categoryIds);
productNum = productCategoryRelMapper.selectCount(matcherSumProductList).intValue();
}else {
productNum = childCategoryList.stream().mapToInt(ProductCategoryResponse::getProductNum).sum();
}
categoryDTO.setProductNum(productNum);
}
categoryDTO.setCategoryChildList(childCategoryList);
});
12)巧用三元运算符
public int getInventory(String purchaseLimitCode) {
String key = getKey(purchaseLimitCode);
Integer inventory = (Integer) redisTemplate.opsForValue().get(key);
return inventory == null ? 0 : inventory;
}
13)Date格式
new DateTime().toString("yyyyMMddHHmmss")//??导入依赖,有问题
//java自带
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String s = dateFormat.format(date);
System.out.println(s);
14)excel导入导出
取excel的坑 https://blog.csdn.net/qq_35893120/article/details/80395080 导出excel模板 https://blog.51cto.com/u_15127658/433732115)DTO和VO的区别
http://www.tnblog.net/aojiancc2/article/details/2396
前后端交互使用request和response
VO(View Object):封装参数
视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
DTO(Data Transfer Object):网络传输,中间件的交互
数据传输对象,泛指用于展示层与服务层之间的数据传输对象。
PO(Persistent Object):
持久化对象,就是和数据库保持一致的对象
16)开发工具安装问题,程序扫描安装目录空格,查看报错信息,%(符号为百分号),将workspace拉到别的地方
17)配置文件绑定异常(修改配置文件),我们使用的时Mybatis-plus,实际我们的配置为mybatis,所以配置类读取错误
mybatis-plus:
type-aliases-package: com.xfw.welfare.docking
mapper-locations: classpath* */mapper/mapping/ *.xml
18)接不到参数为空,原因接前端参数,或者接远程调用的参数的为null时可能时因为@RequestBody注解忘记打了等注解没使用
19)为了再nacos中起不一样的服务,避免开发冲突(其中在调用方也需要配置调用方)//不然可以会报找不到服务
阿里java
转时间格式(在类属性中)
@ApiModelProperty(value = "操作时间,最近一次上下架的时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date operateDate;
22)在类中使用静态变量(定义常量)和方法(日常写法)
package com.xfw.welfare.docking.dto.request;
import cn.modousa.mds.common.tools.MD5Util;
import lombok.Data;
@Data
public class LFPayVideoRequest {
public static final String URL = "http://open.jiaofei100.com/Api/PayVideo.aspx";
private String APIID;
private String Account;
private String ProductCode;
private String BuyNum;
private String OrderID;
private String CreateTime;
private String IsCallBack;
private String CallBack;
private String Sign;
public String generateSign(String appKey) {
String sb = "APIID=" + APIID +
"&Account=" + Account +
"&BuyNum=" + BuyNum +
"&CreateTime=" + CreateTime +
"&IsCallBack=" + IsCallBack +
"&OrderID=" + OrderID +
"&ProductCode=" + ProductCode +
"&APIKEY=" + appKey;
return MD5Util.md5Hex(sb).toUpperCase();
}
}
23)private和public修饰属性的区别
private String a;//需要set get方法,new之后get出来 public String b;//可以new之后直接点出来24)数据库和mq的了解
1.数据库设计 https://github.com/jly8866/archer/blob/master/src/docs/mysql_db_design_guide.md
2.了解mq的使用场景 https://www.zhihu.com/question/34243607
25)MultipartFile文件流,前后端交互// MultipartFile转inputstream,前后端传文件流,不会传地址,传的是文件流
//例
@PostMapping("/upload")
@ApiOperation("上传文件到oss")
@ApiImplicitParam(name = "pathName", value = "参数,文件地址", required = true, dataType = "MultipartFile")
public Response uploadFile(@RequestParam MultipartFile pathName) throws IOException {
String filename = pathName.getOriginalFilename();//获得流中的文件名
InputStream inputStream = pathName.getInputStream();//将文件流转化为输入流
String url = ossUtil.uploadFile(inputStream,filename);//调用下面的方法
if (!StringUtils.isEmpty(url)) {
return Response.ok(url);
}
log.error("文件上传到Oss失败");
throw new BusinessException(ErrorMessages.BASIC_0001);
}
//oss通过流上传文件
public String uploadFile(InputStream inputStream,String fileName) throws IOException {
//首先判断是否有welfare的存储空间
createBucket();
//将本地文件上传到存储空间bucket中的目录中
//依次填写Bucket名称(例如examplebucket)、Object完整路径(例如exampledir/exampleobject.txt)和本地文件的完整路径。Object完整路径中不能包含Bucket名称。
//TODO bucket下的目录文件夹?
PutObjectRequest objectRequest = new PutObjectRequest(OssConfig.BUCKET_NAME, "common/" + fileName,inputStream);
//上传文件
PutObjectResult putObjectResult = ossClient.putObject(objectRequest);
//判断ETag(ETag用于标识一个Object的内容)
if (StringUtils.isEmpty(putObjectResult.getETag())) {
log.error("上传文件失败{}");
//关闭OssClient
ossClient.shutdown();
return "false";
}
log.info("上传文件成功{}");
//拼接访问url
String url = backUrl(fileName);
//关闭OssClient
ossClient.shutdown();
return url;
}
//示例,多态,没必要强行给fileinputstream,想的不全面,变通一点,在idea中查看java继承体系,ctrl+h
右键,最后一个,show继承体系
// 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
//多态
InputStream inputStream = new FileInputStream("D:\localpath\examplefile.txt");
26)jenkens部署,发送代码时,记得发布到测试环境
27)使用mybatisplus Wrapper层的作用
除了关联查询不行,基本上都可以操作
@Override
public AlipayOrderInfo findByOutTradeNo(String outTradeNo) {
AlipayOrderInfoWrapper.Selecter matcher = new AlipayOrderInfoWrapper.Selecter();
//通过以上对象进行具体的操作
matcher.outTradeNoEq(outTradeNo);//等于
matcher.orderByDesc(AlipayOrderInfo.cols().id().orderNo().toList());//降序
return alipayOrderInfoMapper.selectOne(matcher);//wrapper入参
}
@Override
public AlipayOrderInfo findByOrderNo(String orderId) {
AlipayOrderInfoWrapper.Selecter matcher = new AlipayOrderInfoWrapper.Selecter();
matcher.orderNoEq(orderId);//等于不同的id,通过点出不同的东西控制
return alipayOrderInfoMapper.selectOne(matcher);//将wrapper入参
}
28)经常需要的时间戳(13位),获取毫秒
#### 6.时间戳
//获得13位时间戳精确到毫秒
long date = System.currentTimeMillis();
//阿里开发手册强制推荐:
//1.获取毫秒数,必须使用
System.currentTimeMillis();
//2,而不是
new Date().getTime();
29)初识配置nacos
30)读取配置类中的值
package com.xfw.docking.proxy.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class XxlJobConfig {
private final Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl.job.admin.addresses}")//通过该注解,${},每一个点表示一层
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.address}")
private String address;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}
31)项目中的yml配置nacos中的配置
server:
port: ${APP_PORT:19000}
servlet:
context-path: /
spring:
application:
name: ${xxl-job-admin.namespace:}xxl-job-admin
cloud:
nacos:
username: ${NACOS_SERVER_USERNAME:nacos}
password: ${NACOS_SERVER_PASSWORD:xfw1234}
server-addr: ${NACOS_SERVER_ADDR:172.16.9.159:8848}
config:
prefix: xxl-job //nocos中配置的前缀
enabled: true //启用
file-extension: yml//nocos中配置的后缀
discovery:
enabled: true
profiles:
active: @profiles.active@
main:
allow-bean-definition-overriding: true
32)nacos中的配置,对标上面的配置
spring:
datasource:
url: jdbc:mysql://47.99.39.180:3306/welfare_dev_xxl_job?serverTimezone=CST&useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useInformationSchema=true
username: root
password: xinfengwei,2018
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
connect-properties:
config.decrypt: false
config.decrypt.key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKEDoKdvmsSWILeeRuCqu4yjlaPAoKJbrJw1JNoAP20YLZmvC8kl+/bYwLpmog97lO16PELsPMy/ovq5I2fot2cCAwEAAQ==
filter:
config:
enabled: true
xfw:
enabled: true
max-active: 64
initial-size: 10
min-idle: 10
max-wait: 6000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: select 'X'
test-while-idle: true
test-on-borrow: false
test-on-return: false
### xxl-job, access token
xxl:
job:
accessToken: y@Ylv2fOxX07SgYm
### xxl-job, i18n (default is zh_CN, and you can choose "zh_CN", "zh_TC" and "en")
i18n: zh_CN
## xxl-job, triggerpool max size
triggerpool:
fast:
max: 200
slow:
max: 100
logretentiondays: 365
redis:
database: 11
host: 47.99.39.180
port: 6379
password: 'xinfengwei,2018'
timeout: 1000
lettuce:
pool:
max-active: 2
max-idle: 2
min-idle: 1
max-wait: -1
servlet:
multipart:
max-file-size : 20MB
max-request-size: 100MB
cloud:
bus:
enabled: false
### xxl-job, log retention days
logretentiondays: 7
environment:
variable: DEV
33)postman怎么传json格式
{
'list':[1,1],//json传集合,数组
"flag":1,
"msgId": **null**,
"title": "测试新建6",
"content": "新建内容6",
"imgUrl": "https://img-bss.csdn.net/1640226649214.png",
"proId": 1,
"push": 1,
"date": "2020-05-01 01:01:09"//json时间格式
}
34)java后端强行转时间格式
@ApiModelProperty(value = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")//此注解
private Date created;
35)分页代码
//注意返回结果,注意传参 public PageInfo36)将字符串(string)转化为相应的数据类型的集合(List)queryMsgPage(Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize);//在这里控制就好了 List messagesList = messageMapper.queryMsg();//还是全查出来 return new PageInfo<>(messagesList);//将查出来的集合进行分页 }
//
int[] ids= Arrays.stream(str.split(",")).mapToInt(s -> Integer.parseInt(s)).toArray();
//转为请求方法对应参数类型
List collect = Arrays.stream(ids).boxed().collect(Collectors.toList());
//一个小坑,集合转为字符串之后,每个逗号后还是有空格的,所以在使用split分割的时候需要加空格
//集合转完有空格
long[] longs = Arrays.stream(substring.split(", ")).mapToLong(item -> Long.parseLong(item)).toArray();
集合转数组(阿里开发手册)
【强制】使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一 致、长度为 0 的空数组。 反例:直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现 ClassCastException 错误。 正例: List37)前端没有长整型,都是stringlist = new ArrayList<>(2); list.add("guan"); list.add("bao"); String[] array = list.toArray(new String[0]); 说明:使用 toArray 带参方法,数组空间大小的 length: 1) 等于 0,动态创建与 size 相同的数组,性能最好。 2) 大于 0 但小于 size,重新创建大小等于 size 的数组,增加 GC 负担。 3) 等于 size,在高并发情况下,数组创建完成之后,size 正在变大的情况下,负面影响与 2 相同。 4) 大于 size,空间浪费,且在 size 处插入 null 值,存在 NPE 隐患。
长整型都是使用string去接收,传long超过16位就会造成精度丢失
38)限制文件上传大小,添加配置文件servlet:
multipart:
max-file-size: 10MB
max-request-size: 20MB
enabled: true
39)全局版本号的定义(父子工程pom文件的书写)
POM标签大全详解 - Youpeng - 博客园 (cnblogs.com)
//父工程例子4.0.0 pom org.springframework.boot spring-boot-starter-parent 2.3.8.RELEASE com.xfw welfare 0.0.1-SNAPSHOT welfare 描述 //版本号统一管理//父工程模块的管理子模块工程,与删除添加同步 UTF-8 UTF-8 1.8 1.8 1.8 1.0.15 0.9.1 2.3.8.RELEASE Hoxton.SR9 1.8.8.Final 2.2.6.RELEASE 1.2.66 1.18.16 3.8.2 3.4.3.2 1.9.4 0.0.1-SNAPSHOT 2.9.2 2.0.9 1.5.22 2.0.0 //dependencyManagement:依赖管理,这是父工程需要使用的,有时内部依赖可能会爆红,可以把 dependencyManagement删除重新down依赖即可,然后重新添加标签 welfare-gateway welfare-product welfare-product-api welfare-order welfare-order-api welfare-lf welfare-lf-api welfare-common welfare-generator welfare-docking-proxy xxl-job-admin xxl-job-core welfare-operation welfare-enterprise welfare-system welfare-system-api welfare-message welfare-message-api welfare-authentication welfare-auth welfare-auth-api welfare-push //依赖引入 org.springframework.boot spring-boot-dependencies ${springboot.version} //引入版本号的格式pom import org.springframework.cloud spring-cloud-dependencies ${springcloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring-cloud-alibaba-dependencies.version} pom import com.aliyun.openservices ons-client ${aliyun.openservices.version} io.jsonwebtoken jjwt ${jjwt.version} com.alibaba fastjson ${fastjson.version} org.projectlombok lombok ${lombok.version} provided org.redisson redisson ${redisson.version} true org.redisson redisson-spring-boot-starter ${redisson.version} com.baomidou mybatis-plus-boot-starter ${mybatisplus.version} com.baomidou mybatis-plus ${mybatisplus.version} commons-beanutils commons-beanutils ${commons-beanutils.version} com.alibaba druid-spring-boot-starter 1.1.21 com.alibaba druid 1.1.23 com.google.code.gson gson ${gson.version} io.springfox springfox-swagger2 ${swagger2.version} io.swagger swagger-annotations io.swagger swagger-models io.swagger swagger-models ${swagger-models.version} io.swagger swagger-annotations ${swagger-models.version} io.springfox springfox-swagger-ui ${swagger2.version} org.projectlombok lombok 1.18.16 provided
//子工程例子42)拼接固定参数url,java Api(StrSubstitutor)指定字符拼接,${}符号中的占位符//父工程标签 welfare com.xfw 0.0.1-SNAPSHOT 4.0.0 welfare-message 8 8 org.springframework.cloud spring-cloud-starter-openfeign archaius-core com.netflix.archaius HdrHistogram org.hdrhistogram commons-io commons-io org.springframework.boot spring-boot-starter-logging com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config com.alibaba.nacos nacos-client com.alibaba druid com.xfw welfare-common ${welfare-version} //去除依赖中的某个依赖com.xfw.framework xfw-framework-leopard-auth com.xfw welfare-authentication ${welfare-version} com.xfw welfare-message-api ${welfare-version} com.aliyun dingtalk 1.2.5 compile com.xfw.framework xfw-framework-leopard-dingtalk-base ${xfw-framework.version} com.xuxueli xxl-job-core 0.0.1-SNAPSHOT compile maven-central maven-central http://47.99.39.180:8781/repository/maven-releases/ true true dev dev true test test pro pro pre pre src/main/resources/pre org.springframework.boot spring-boot-maven-plugin build-info repackage true org.apache.maven.plugins maven-assembly-plugin false src/main/assembly/assembly.xml make-assembly package single org.apache.maven.plugins maven-resources-plugin 2.6 UTF-8 ttf maven-antrun-plugin copy package run src/main/java ***.yml false src/main/resources false fonts public String advanceBody(String msgType, String title, String image, String content, String messageUrl) { Map body = Maps.newHashMap(); body.put("msgType", msgType); body.put("title", title); body.put("image", image); body.put("content", content); body.put("messageUrl", messageUrl); return JSON.toJSONString(body);//需要string,就把map转成string,不能硬拼接需要使用map }
//${agentId}必需以${}的形式存在,占位符
public static void main(String[] args) {
String url="dingtalk://dingtalkclient/action/open_micro_app?corpId=${corpId}&agentId=${agentId}&miniAppId=%s&pVersion=1&packageType=1&page=%2Fpages%2Fgoods%2Fgoods%3FproductsId%3D";
HashMap
43)SpringCloud Feign 传参问题及传输Date类型参数的时差(未能解决,换了方法)
44)sql语句优化(数据库优化,待学习)
1.查看sql执行过程
//sql执行过程 EXPLAIN关键字 EXPLAIN select `id`, `corp_id`, `agent_id`, `suite_id`, `ding_user_id_added`, `create_time`, `update_time`, `auth_status`, `service_status`, `is_delete` from sys_corp_suite GROUP BY corp_id EXPLAIN select id from sys_corp_suite where id='123456789'2.结果 45)java8新特性对比(流的方式拿出集合中对应的指定属性,写法上的优化)
//循环拿出userId //所要操作的集合 List46)JAVA中常量使用常量类或者常量接口,还是使用枚举的区别userList = sysCorpSuiteApi.findAddUser(item.getCorpId()); //1。lambda表达式 List useridList = userList.stream().map(user -> { String dingUserId = user.getDingUserIdAdded(); return dingUserId; }).collect(Collectors.toList()); //2。优化写法 List useridList = userList.stream().map(SysCorpSuite::getDingUserIdAdded).collect(Collectors.toList());
(具体情况具体分析,参考阿里巴巴开发手册)建议使用枚举类 https://blog.csdn.net/chenpeng19910926/article/details/76210117
47)浮点数之间的等值判断,不能使用==或equals使用方法https://www.jianshu.com/p/dfc3ec4350e6(参考博客) 详见阿里巴巴开发手册
【强制】浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals
来判断。
说明:浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进
制无法精确表示大部分的十进制小数,具体原理参考《码出高效》。
反例:
float a = 1.0F - 0.9F;
float b = 0.9F - 0.8F;
if (a == b) {
// 预期进入此代码块,执行其它业务逻辑
// 但事实上 a==b 的结果为 false
}
Float x = Float.valueOf(a);
Float y = Float.valueOf(b);
if (x.equals(y)) {
// 预期进入此代码块,执行其它业务逻辑
// 但事实上 equals 的结果为 false
}
正例:
(1) 指定一个误差范围,两个浮点数的差值在此范围之内,则认为是相等的。
float a = 1.0F - 0.9F;
float b = 0.9F - 0.8F;
float diff = 1e-6F;
if (Math.abs(a - b) < diff) {
System.out.println("true");
}
(2) 使用 BigDecimal 来定义值,再进行浮点数的运算操作。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
if (x.compareTo(y) == 0) {
System.out.println("true");
}
48)各种数据类型之间的比较方法选用【==和equals并不是全部适用的】(阿里巴巴见开发手册)
49)遍历map集合的(4+1种)【还有一个事java8:map.forEach】
尽量使用entrySet ,java8中推荐是红map.forEach((key,value)->{}), 详情参照阿里开发手册
50)一边循环一边删除集合中的数据(必须使用迭代器,不能使用forEach) while (iterator.hasNext()) {
String id = iterator.next();
if (!sysUserProductSettingApi.getMsgUserProductSetting(Long.valueOf(id))) {
iterator.remove();
}
}
51)递归拿捏(迷宫问题,理解小测试)
package com.example.l220109.my;
import org.junit.Test;
public class Rucursion {
@Test
public void demo() {
// 构建一个 8 行 7 列的地图
int[][] map = initMap(8, 7);
printMap(map);
// 一次性没有回溯的路线
setWay(map, 1, 1, 6, 5);
// 这个点,是有回溯的路线
// setWay(map, 1, 1, 4, 1);
System.out.println("路线查找完毕");
printMap(map);
}
public boolean setWay(int[][] map, int startX, int startY, int endX, int endY) {
// 如果当结束点已经走过,表示已经到达了出口
// System.out.println();
// printMap(map); // 打开这个可以看到每一步的探索路径
if (map[endX][endY] == 2) {
return true;
}
// 那么开始我们的策略探索
// 如果该点还没有走过,则可以尝试探索
if (map[startX][startY] == 0) {
// 先假定该点标可以通过,因为要去探索四周的点是否可以走
map[startX][startY] = 2;
// 下 -> 右 -> 上 -> 左
// 根据策略:先往下走,如果可以走则返回 true
if (setWay(map, startX + 1, startY, endX, endY)) {
return true;
}
// 如果走不通,则继续往右边探索
else if (setWay(map, startX, startY + 1, endX, endY)) {
return true;
}
// 如果走不通,则继续往上边探索
else if (setWay(map, startX - 1, startY, endX, endY)) {
return true;
}
// 如果走不通,则继续往左边探索
else if (setWay(map, startX, startY - 1, endX, endY)) {
return true;
}
// 都走不通,表示改点是一个死点,四周都无法出去
else {
map[startX][startY] = 3;
return false;
}
} else {
// 如果不为 0,可能的情况是:1,2,3,这三种表示都表示不可以走
return false;
}
}
private int[][] initMap(int row, int cloum) {
// 构建一个 8 行 7 列的地图
int[][] map = new int[row][cloum];
// 数字 1 表示挡板,构建一个有挡板的地图
for (int i = 0; i < map[0].length; i++) {
map[0][i] = 1; // 顶部增加挡板
map[map.length - 1][i] = 1; // 底部增加挡板
}
for (int i = 0; i < map.length; i++) {
map[i][0] = 1; // 左侧增加挡板
map[i][map[0].length - 1] = 1; // 右侧增加挡板
}
// 中间的其他固定挡板
map[3][1] = 1;
map[3][2] = 1;
return map;
}
public void printMap(int[][] map) {
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[0].length; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
int i = 7;
// test(7);
test2(7);
}
public static void test(int s) {
// 是递归
if (s > 1) {
test(s - 1);
}
System.out.println(s);
}
public static void test2(int j) {
// 是递归
if (j == 1) {
return;
} else {
test2(j - 1);
System.out.println(j);
}
}
}
52)专业词汇类目,spu,sku
类目:类目是一个树状结构的系统,大体上可以分成4-5级。如手机->智能手机->苹果手机类目,在这里面,手机是一级类目,苹果手机是三级类目,也是叶子类目。
SPU:苹果6(商品聚合信息的最小单位),如手机->苹果手机->苹果6,苹果6就是SPU。
SKU:土豪金 16G 苹果6 (商品的不可再分的最小单元)。
从广义上讲,类目>SPU>SKU。
53)sql模糊搜索,索引问题索引与优化like查询
- like %keyword 索引失效,使用全表扫描。但可以通过翻转函数+like前模糊查询+建立翻转函数索引=走翻转函数索引,不走全表扫描。like keyword% 索引有效。like %keyword% 索引失效,也无法使用反向索引。
https://juejin.cn/post/6854573219089907720
55)xml多条件模糊查询56)url拼接SELECT a.user_name, a.mobile, a.create_time, b.state FROM cb_uc_user_register a LEFT JOIN cb_uc_user_register_extends b ON a.id = b.user_id AND a.user_name like concat('%', #{userManagerListDto.userName},'%') AND b.state = #{userManagerListDto.state}
dingtalk://dingtalkclient/action/open_micro_app?corpId=KaTeX parse error: Expected 'EOF', got '&' at position 9: {corpId}&̲agentId={agentId}&miniAppId=%s&pVersion=1&packageType=1&page=%2Fpages%2Fgoods%2Fgoods%3FproductsId%3D
dingtalk://dingtalkclient/action/open_micro_app?corpId=%s&agentId=%s&miniAppId=%s&pVersion=1&packageType=1&page=%s
57)MyBatisPlus多条件查询,组合查询https://www.cnblogs.com/BNTang/articles/14584735.html
58)前后端交互Long和string【强制】对于需要使用超大整数的场景,服务端一律使用 String 字符串类型返回,禁止使用 Long 类型
说明:Java 服务端如果直接返回 Long 整型数据给前端,JS 会自动转换为 Number 类型(注:此类型为双
精度浮点数,表示原理与取值范围等同于 Java 中的 Double)。Long 类型能表示的最大值是 2 的 63 次方
-1,在取值范围之内,超过 2 的 53 次方 (9007199254740992)的数值转化为 JS 的 Number 时,有些数
值会有精度损失。扩展说明,在 Long 取值范围内,任何 2 的指数次整数都是绝对不会存在精度损失的,所
以说精度损失是一个概率问题。若浮点数尾数位与指数位空间不限,则可以精确表示任何整数,但很不幸,
双精度浮点数的尾数位只有 52 位。
反例:通常在订单号或交易号大于等于 16 位,大概率会出现前后端单据不一致的情况,比如,“orderId”:
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[0].length; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
int i = 7;
// test(7);
test2(7);
}
public static void test(int s) {
// 是递归
if (s > 1) {
test(s - 1);
}
System.out.println(s);
}
public static void test2(int j) {
// 是递归
if (j == 1) {
return;
} else {
test2(j - 1);
System.out.println(j);
}
}
}
#### 52)专业词汇类目,spu,sku 类目:类目是一个树状结构的系统,大体上可以分成4-5级。如手机->智能手机->苹果手机类目,在这里面,手机是一级类目,苹果手机是三级类目,也是叶子类目。 SPU:苹果6(商品聚合信息的最小单位),如手机->苹果手机->苹果6,苹果6就是SPU。 SKU:土豪金 16G 苹果6 (商品的不可再分的最小单元)。 从广义上讲,类目>SPU>SKU。 #### 53)sql模糊搜索,索引问题 索引与优化like查询 1. like %keyword 索引失效,使用全表扫描。但可以通过翻转函数+like前模糊查询+建立翻转函数索引=走翻转函数索引,不走全表扫描。 2. like keyword% 索引有效。 3. like %keyword% 索引失效,也无法使用反向索引。 #### 54)说说count(*)、count(1)、count(列名)有什么区别? https://juejin.cn/post/6854573219089907720 #### 55)xml多条件模糊查询 ```java56)url拼接SELECT a.user_name, a.mobile, a.create_time, b.state FROM cb_uc_user_register a LEFT JOIN cb_uc_user_register_extends b ON a.id = b.user_id AND a.user_name like concat('%', #{userManagerListDto.userName},'%') AND b.state = #{userManagerListDto.state}
dingtalk://dingtalkclient/action/open_micro_app?corpId=KaTeX parse error: Expected 'EOF', got '&' at position 9: {corpId}&̲agentId={agentId}&miniAppId=%s&pVersion=1&packageType=1&page=%2Fpages%2Fgoods%2Fgoods%3FproductsId%3D
dingtalk://dingtalkclient/action/open_micro_app?corpId=%s&agentId=%s&miniAppId=%s&pVersion=1&packageType=1&page=%s
57)MyBatisPlus多条件查询,组合查询https://www.cnblogs.com/BNTang/articles/14584735.html
58)前后端交互Long和string【强制】对于需要使用超大整数的场景,服务端一律使用 String 字符串类型返回,禁止使用 Long 类型
说明:Java 服务端如果直接返回 Long 整型数据给前端,JS 会自动转换为 Number 类型(注:此类型为双
精度浮点数,表示原理与取值范围等同于 Java 中的 Double)。Long 类型能表示的最大值是 2 的 63 次方
-1,在取值范围之内,超过 2 的 53 次方 (9007199254740992)的数值转化为 JS 的 Number 时,有些数
值会有精度损失。扩展说明,在 Long 取值范围内,任何 2 的指数次整数都是绝对不会存在精度损失的,所
以说精度损失是一个概率问题。若浮点数尾数位与指数位空间不限,则可以精确表示任何整数,但很不幸,
双精度浮点数的尾数位只有 52 位。
反例:通常在订单号或交易号大于等于 16 位,大概率会出现前后端单据不一致的情况,比如,“orderId”:
362909601374617692,前端拿到的值却是: 362909601374617660



