• [技术干货] element-ui 表单校验,el-select校验失效问题
    Element是一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的组件库,提供了配套设计资源,帮助你的网站快速成型。由饿了么公司前端团队开源。form表单由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据组成。但是随便提交数据肯定是会出问题的,所以我们在里面经常使用校验,element-ui中给我提供了一套校验的方法,但是不同元素,比如输入框、选择器、单选框的校验是不一样的,因为一些历史原因,我们在使用的过程中,明明按照规范已经编写了校验逻辑,但是由于加载先后顺序,未初始化找不到元素等原因会出现各种各样的校验失败的情况。本文中主要是针对表单提交过程中出现el-select选择器的表单校验失效问题,下面我们详细说明如何处理这种失效的两种方式。form表单验证时,遇到了校验失效问题,有值的情况下,校验规则并没有对应消失,检查了几个容易出错的地方:1、:model="addForm" 用model绑定表单,而不是v-model,没问题   <el-form           :model="addForm"           class="addForm"           ref="addForm"           :rules="rules"           label-position="right"           label-width="130px"           >  </el-form> 2、需要添加校验的对应字段,是否添加了prop属性,ok没问题   <el-form-item :label="`对应字段:`" prop="fieldName">                 <el-select                   v-model="addForm.fieldName"                   placeholder="请选择对应字段"                   clearable                   style="width: 100%;max-width: 100%;"                   @change="changeField"                   :disabled="operType == 2"                 >                   <el-option                     v-for="item in fieldList"                     v-bind:key="item.fieldName"                     :label="item.fieldName"                     :value="item.fieldName"                   ></el-option>                 </el-select> 3、检查rules,里面校验提示的事件是否正确,发现了问题,之前el-select,trigger时候用的“blur”,导致失效,现在 el-select 统一改为"change"    rules: {                ruleCode: [           { required: true, message: `规则编码不能为空`, trigger: "blur" }         ],         menuCode: [           { required: true, message: `对应菜单不能为空`, trigger: "change" }         ],         tableName: [           { required: true, message: `对应表不能为空`, trigger: "change" }         ],         fieldName: [           { required: true, message: `对应字段不能为空`, trigger: "change" }         ]       }, 奇怪的是,我表单里面有三个el-select,其他两个生效了,只有另外一个没有生效,也是醉了,于是我请教了大佬,大佬告诉我,form表单里面,el-select会出现失效的情况,尤其是我这个el-foem-item 里面又嵌套了el-row     <el-col :span="12">               <el-form-item :label="`对应表:`" prop="tableName">                 <el-select                   v-model="addForm.tableName"                   placeholder="请选择对应表"                   clearable                   style="width: 100%;max-width: 100%;"                   @change="change"                   :disabled="operType == 2"                 >                   <el-option                     v-for="item in tableList"                     :key="item.tableName"                     :label="item.tableName"                     :value="item.tableName"                   ></el-option>                 </el-select>               </el-form-item>             </el-col> 层级太深了,form检测不到addform的变化,这个时候要么change事件里面强制更新dom触发(视图更新) change(val){ this.$set(this.addForm,'tableName',val) this.$forceUpdate() } 要么再change事件里面对form表单绑定的数据进行拷贝 this.addForm = JSON.parse(JSON.stringify(this.addForm));或者像这样描述下    this.addForm = { ...this.addForm };  change(val){ this.addForm={...this.addForm} } 我遇到的问题,通过第二种方式:拷贝或者描述生效的,以上是我近期遇到的bug修复的方法,大家可以借鉴一下,如果大家有更好的方法,可以分享哦 ———————————————— 原文链接:https://blog.csdn.net/m0_66899315/article/details/124494296 
  • [技术干货] SpringBoot - MyBatis-Plus - UpdateWrapper、LambdaUpdateWrapper和LambdaUpdateChainWrapper的用法
     写在前面 和SpringBoot - MyBatis-Plus - QueryWrapper、LambdaQueryWrapper和LambdaQueryChainWrapper的用法(二)的用法类似,另外UpdateWrapper、LambdaUpdateWrapper和LambdaUpdateChainWrapper还提供了:set、setSql和setEntity的方法。  UpdateWrapper // 1. 查询条件:商品标题中包含'学生白色丝袜'并且状态为上架的商品 UpdateWrapper<MallxSpu> updateWrapper = new UpdateWrapper<>(); updateWrapper.like("title","学生白色丝袜").eq("statue",1);  // 2. 将满足条件的商品添加标签 MallxSpu spu = new MallxSpu(); spu.setTag("丝袜");  // 3. 执行更新 int result = mallxSpuMapper.update(spu, updateWrapper); LambdaUpdateWrapper 方式一: // 1. 查询条件:商品标题中包含'学生白色丝袜'并且状态为上架的商品 LambdaUpdateWrapper<MallxSpu> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.like(MallxSpu::getTitle,"学生白色丝袜").eq(MallxSpu::getStatus,1);  // 2. 将满足条件的商品添加标签 MallxSpu spu = new MallxSpu(); spu.setTag("丝袜");  // 3. 执行更新 int result = mallxSpuMapper.update(spu, updateWrapper); 方式二(使用UpdateWrapper的set方法直接设置字段为待修改的值): // 1. 查询条件:商品标题中包含'学生白色丝袜'并且状态为上架的商品,并将符合条件的商品打上标签。 LambdaUpdateWrapper<MallxSpu> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.like(MallxSpu::getTitle,"学生白色丝袜").eq(MallxSpu::getStatus,1).set(MallxSpu::getTag, "丝袜");  // 2. 执行更新 int result = mallxSpuMapper.update(null, updateWrapper); 方式三(使用UpdateWrapper的set方法和对象混合使用,设置字段为待修改的值): // 1. 查询条件:商品标题中包含'学生白色丝袜'并且状态为上架的商品,并将符合条件的商品打上标签。 LambdaUpdateWrapper<MallxSpu> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.like(MallxSpu::getTitle,"学生白色丝袜").eq(MallxSpu::getStatus,1).set(MallxSpu::getTag, "丝袜");  // 2. 将满足条件的商品的库存改为10000 MallxSpu spu = new MallxSpu(); spu.setStock(10000);  // 3. 执行更新 int result = mallxSpuMapper.update(spu, updateWrapper); 方式四(使用UpdateWrapper的setSql方法设置字段为待修改的值): // 1. 查询条件:商品标题中包含'学生白色丝袜'并且状态为上架的商品,并将符合条件的商品打上标签,同时修改库存。 LambdaUpdateWrapper<MallxSpu> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.like(MallxSpu::getTitle,"学生白色丝袜").eq(MallxSpu::getStatus,1).setSql("tag = '丝袜'").setSql("stock = 10000");  // 2. 执行更新 int result = mallxSpuMapper.update(null, updateWrapper); LambdaUpdateChainWrapper 方式一: // 查询条件:商品标题中包含'学生白色丝袜'并且状态为上架的商品,并将符合条件的商品打上标签,同时修改库存,并执行更新操作。 boolean flag = new LambdaUpdateChainWrapper<>(mallxSpuMapper)         .like(MallxSpu::getTitle,"学生白色丝袜")         .eq(MallxSpu::getStatus,1)         .setSql("tag = '丝袜'")         .setSql("stock = 10000")         .update(); 方式二: // 1. 将满足条件的商品的库存改为10000 MallxSpu spu = new MallxSpu(); spu.setStock(10000);  // 2. 查询条件:商品标题中包含'学生白色丝袜'并且状态为上架的商品,并将符合条件的商品打上标签,同时修改库存,并执行更新操作。 boolean flag = new LambdaUpdateChainWrapper<>(mallxSpuMapper)         .like(MallxSpu::getTitle,"学生白色丝袜")         .eq(MallxSpu::getStatus,1)         .setSql("tag = '丝袜'")         .update(spu); ———————————————— 原文链接:https://blog.csdn.net/goodjava2007/article/details/125158194 
  • [技术干货] mybatis-plus update更新操作的三种方式(小结)
    1.@ 根据id更新1234User user = new User();user.setUserId(1);user.setAge(29); userMapper.updateById(user);2.@ 条件构造器作为参数进行更新123456//把名字为rhb的用户年龄更新为18,其他属性不变UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();updateWrapper.eq("name","rhb");User user = new User();user.setAge(18);userMapper.update(user, updateWrapper);@ 假设只更新一个字段在使用updateWrapper 的构造器中也需要构造一个实体对象,这样比较麻烦。可以使用updateWrapper的set方法1234//只更新一个属性,把名字为rhb的用户年龄更新为18,其他属性不变UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();updateWrapper.eq("name","rhb").set("age", 18);userMapper.update(null, updateWrapper);3.@ lambda构造器123LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();lambdaUpdateWrapper.eq(User::getName, "rhb").set(User::getAge, 18);Integer rows = userMapper.update(null, lambdaUpdateWrapper);mybatisplus update语句为null时没有拼接上去我有一个设置页面,数据库就相当于是key和value的样子,当value为空的时候用updatebyId就变成了1update param where key=?就没有set就会报语法错误这个出现的场景是如果数据库本来改自己有值更新 null时不会有问题,当数据库也是null时更新就不会拼接set数据库有值时update null数据库也为空时的更新然后查解决方案:mybatisplus为null的时候不会拼接,可配置一个策略updateStrategy = FieldStrategy.IGNORED无论是啥都会拼接 但是还是会有问题,指定下类型就可以了 最后经测试有两种方案可行123@TableField(value = "PARAMVAL",updateStrategy = FieldStrategy.IGNORED,jdbcType = JdbcType.VARCHAR)//@TableField(value = "PARAMVAL",jdbcType = JdbcType.VARCHAR, fill = FieldFill.UPDATE)    private String paramVal;以上两种方案均可到此这篇关于mybatis-plus update更新操作的三种方式(小结)的文章就介绍到这了原文链接:https://www.jb51.net/article/225802.htm
  • [技术干货] BigDecimal四舍五入保留两位小数
     Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberFormat; public class NumberFormatDemo {     public static void main(String[] args) {         // BigDecimal         // 保留两位小数         System.out.println(new BigDecimal(0.2).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());// 0.2         System.out.println(new BigDecimal(0.235).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());// 0.23         System.out.println(new BigDecimal(0.2351).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());// 0.24         System.out.println(new BigDecimal(42).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());// 42.0          // NumberFormat         // 保留两位小数,个位无数字填充0         NumberFormat nformat = NumberFormat.getNumberInstance();         nformat.setMaximumFractionDigits(2);         System.out.println(nformat.format(0.2));// 0.2         System.out.println(nformat.format(0.235));// 0.23         System.out.println(nformat.format(0.2351));// 0.24         System.out.println(nformat.format(42));// 42          // DecimalFormat,是NumberFormat的具体实现子类         // 保留两位小数,对应位上无数字填充0         DecimalFormat df = new DecimalFormat("#0.00");         System.out.println(df.format(0.2));// 0.20         System.out.println(df.format(0.235));// 0.23         System.out.println(df.format(0.2351));// 0.2, 因为0.2351在0.23-0.24之间,距离0.24更近,所以输出0.24         System.out.println(df.format(42));// 42.00          DecimalFormat df4 = new DecimalFormat();         // #:位置上无数字不显示         df4.applPattern("#.##");         System.out.println(df4.format(345235.0));// 345235         // 0:位置上无数字显示0         df4.applPattern("0.00");         System.out.println(df4.format(345235.0));// 345235.00         // 加负数显示         df4.applPattern("-0.00");         System.out.println(df4.format(345235.34567));// -345235.35         // 逗号分隔         df4.applPattern("-0,000.00");         System.out.println(df4.format(345235.34567));// -345,235.35         // 百分位         df4.applPattern("0.00%");         System.out.println(df4.format(0.34567));// 34.57%         // 千分位         df4.applPattern("0.00\u2030");         System.out.println(df4.format(0.34567));// 345.67‰         // 科学计数法,E之前是底数的格式,E之后的是指数的格式         df4.applPattern("0.00E00");         System.out.println(df4.format(2342.444));// 2.34E03         // 格式后面加单位符号         df4.applPattern("0.00 KG");         System.out.println(df4.format(2342.444));// 2342.44 KG         df4.applPattern("0.00 QA");         System.out.println(df4.format(2342.444));// 2342.44 QA         // 使用舍入模式:ROUND_HALF_EVEN,         // 保留位数是奇数,使用ROUND_HALF_DOWN         // 保留位数是偶数,使用ROUND_HALF_UP         System.out.println(df4.format(2342.435));// 2342.43 QA         System.out.println(df4.format(2342.445));// 2342.45 QA          // String.format         // 保留两位小数,个位数及小数点后两位无数字填充0,四舍五入         System.out.println(String.format("%.2f", 0.2));// 0.20         System.out.println(String.format("%.2f", 0.235));// 0.24         System.out.println(String.format("%.2f", 0.236));// 0.24         System.out.println(String.format("%.2f", 42.0));// 42.00     } } BigDecimal工具类总所周知,java在浮点型运算时是非精确计算,如下demoSystem.out.println(0.05 + 0.01);// 0.060000000000000005System.out.println(1.0 - 0.42);// 0.5800000000000001System.out.println(4.015 * 100);// 401.49999999999994System.out.println(123.3 / 100);// 1.2329999999999999在商业运算中,这点微小的误差有可能造成非常严重的后果。所以在商业应用开发中,涉及金额等浮点数计算的数据,全部使用BigDecimal进行加减乘除计算BigDecimal工具类代码:import java.math.BigDecimal;public class BigDecimalUtil {// 默认除法运算精度private static final int DEFAULT_DIV_SCALE = 5;/*** 提供精确的加法运算** @param v1* @param v2* @return*/public static double add(double v1, double v2) {BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.add(b2).doubleValue();}public static String add(String v1, String v2) {BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.add(b2).toString();}/*** 提供精确的减法运算** @param v1* @param v2* @return*/public static double subtract(double v1, double v2) {BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.subtract(b2).doubleValue();}public static String substract(String v1, String v2) {BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.subtract(b2).toString();}/*** 提供精确的乘法运算** @param v1* @param v2* @return*/public static double multipl(double v1, double v2) {BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.multipl(b2).doubleValue();}public static String multipl(String v1, String v2) {BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.multipl(b2).toString();}/*** 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入,舍入模式采用ROUND_HALF_UP** @param v1* @param v2* @return 两个参数的商*/public static double divide(double v1, double v2) {return divide(v1, v2, DEFAULT_DIV_SCALE);}/*** 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。舍入模式采用ROUND_HALF_UP** @param v1* @param v2* @param scale* 表示需要精确到小数点以后几位。* @return 两个参数的商*/public static double divide(double v1, double v2, int scale) {return divide(v1, v2, scale, BigDecimal.ROUND_HALF_UP);}/*** 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。舍入模式采用用户指定舍入模式** @param v1* @param v2* @param scale* 表示需要精确到小数点以后几位* @param round_mode* 表示用户指定的舍入模式* @return 两个参数的商*/public static double divide(double v1, double v2, int scale, int round_mode) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.divide(b2, scale, round_mode).doubleValue();}/*** 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入,舍入模式采用ROUND_HALF_EVEN** @param v1* @param v2* @return 两个参数的商,以字符串格式返回*/public static String divide(String v1, String v2) {return divide(v1, v2, DEFAULT_DIV_SCALE);}/*** 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。舍入模式采用ROUND_HALF_UP** @param v1* @param v2* @param scale* 表示需要精确到小数点以后几位* @return 两个参数的商,以字符串格式返回*/public static String divide(String v1, String v2, int scale) {return divide(v1, v2, scale, BigDecimal.ROUND_HALF_UP);}/*** 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。舍入模式采用用户指定舍入模式** @param v1* @param v2* @param scale* 表示需要精确到小数点以后几位* @param round_mode* 表示用户指定的舍入模式* @return 两个参数的商,以字符串格式返回*/public static String divide(String v1, String v2, int scale, int round_mode) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.divide(b2, scale, round_mode).toString();}/*** 提供精确的小数位四舍五入处理,舍入模式采用ROUND_HALF_EVEN** @param v* 需要四舍五入的数字* @param scale* 小数点后保留几位* @return 四舍五入后的结果*/public static double round(double v, int scale) {return round(v, scale, BigDecimal.ROUND_HALF_UP);}/*** 提供精确的小数位四舍五入处理** @param v* 需要四舍五入的数字* @param scale* 小数点后保留几位* @param round_mode* 指定的舍入模式* @return 四舍五入后的结果*/public static double round(double v, int scale, int round_mode) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b = new BigDecimal(Double.toString(v));return b.setScale(scale, round_mode).doubleValue();}/*** 提供精确的小数位四舍五入处理,舍入模式采用ROUND_HALF_UP** @param v* 需要四舍五入的数字* @param scale* 小数点后保留几位* @return 四舍五入后的结果,以字符串格式返回*/public static String round(String v, int scale) {return round(v, scale, BigDecimal.ROUND_HALF_UP);}/*** 提供精确的小数位四舍五入处理** @param v* 需要四舍五入的数字* @param scale* 小数点后保留几位* @param round_mode* 指定的舍入模式* @return 四舍五入后的结果,以字符串格式返回*/public static String round(String v, int scale, int round_mode) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b = new BigDecimal(v);return b.setScale(scale, round_mode).toString();}}BigDecimal 舍入模式(Rounding mode)介绍:ROUND_CEILING向正无穷方向舍入ROUND_DOWN向零方向舍入ROUND_FLOOR向负无穷方向舍入ROUND_HALF_DOWN向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入, 例如1.55 保留一位小数结果为1.5ROUND_HALF_EVEN向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用: : ROUND_HALF_DOWN如果是偶数,使用ROUND_HALF_UPROUND_HALF_UP向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入, 1.55保留一位小数结果为1.6ROUND_UNNECESSARY计算结果是精确的,不需要舍入模式ROUND_UP向远离0的方向舍入原文链接:https://www.cnblogs.com/shy1766IT/p/10262325.html
  • [技术干货] idea导入项目不显示右侧maven的解决办法
    有的时候,当我们从git或者gitee上clone下来一个项目,然后通过idea打开的时候,右侧的maven不显示(前提是这是一个maven项目),这种原因一般是由于读取项目出错,没有正确加载到pom文件造成的。  解决方案一: 1. 进入到该项目的工作目录文件夹,比如你的项目名称叫XXX, 就进入 (idea工作目录)/XXX 2. 关闭idea 3 删除 .idea文件夹,要注意的是,这个文件夹是隐藏的,如果你没有设置显示隐藏文件可能看不见它,设置一下即可 4. 重新打开这个项目,右侧maven就可以正确显示了 解决方案二: 在新版本的idea中,可以直接添加。方法是打开pom.xml文件,在文件上右键 - >Add as Maven Project 这时候就出来了,推荐使用这种方案。方式三: 如果你之前都是有的,突然之间没有了,可以试试:view -> Tool windows -> Maven 都出来了。———————————————— 原文链接:https://blog.csdn.net/lsqingfeng/article/details/105042123 
  • [技术干货] lombok 基础注解之 @Builder
    一、注解介绍 @Builder 注解为类生成相对略微复杂的构建器 API 它作用于类,将其变成建造者模式 可以以链的形式调用 初始化实例对象生成的对象是不可以变的,可以在创建对象的时候进行赋值 如果需要在原来的基础上修改可以加 set 方法,final 字段可以不需要初始化 它会生成一个全参的构造函数 二、属性介绍 @Builder.Default:非 final 的字段可以有默认值 builderMethodName:指定创建内部静态类的方法名,默认值为 builder buildMethodName:指定创建实体类的方法名,默认值为 build builderClassName:指定内部静态的类名,默认值为 “”,默认创建的类名为 thisclassBuilder toBuilder:设置为 true 可以对这个对象进行拷贝生成新的对象,可以再修改,默认为 false access:设置 builderMethodName 的访问权限修饰符,默认为 public 共有 PUBLIC、MODULE、PROTECTED、PACKAGE、PRIVATE,其中 MODULE 是 Java 9 的新特性 setterPrefix:设置 setter 方法的前缀,默认为 “” 三、实战演练 @Builder(     builderMethodName = "builder", buildMethodName = "build", builderClassName = "",     toBuilder = true, access = AccessLevel.PUBLIC, setterPrefix = "" ) public class Yifei {     @Builder.Default     private String name = "刘亦菲";          private String sex;          private final Integer age = 18;    // final 字段加不加 Default 都可以初始化成功          public static void main(String[] args) {         Yifei yifei = Yifei.builder().build();    // 如果没有加 Default,那么输出的 name 结果是 null。         /**          * 类似于拷贝,修改了 name 值,age 值还是原来的          * 如果 toBuilder = false,则没有 toBuilder 方法          */         yifei = yifei.toBuilder().name("刘亦菲").build();     } } 我们在平时开发中经常会用到泛型,而 @Builder 也支持指定泛型构建 @Builder public class Yifei<T> {     public void yiyang(Yifei<T> yi) {         yi = Yifei.<T>builder().builder();     } } @Builder 会生成一个全参构造方法,因此就没有了无参构造方法,但当我们遇到需要无参构造方法时就会发生问题,这个时候手写或者加上 @NoArgsConstructor 都会报错,两种解决方案 一、加上 @AllArgsConstructor @Builder @AllArgsConstructor public class Yifei {     public Yifei() {         /**          * 手写或者 @NoArgsConstructor 都会使 @Builder 生成的全参构造函数失效          * 这个时候就被报错,加上 @AllArgsConstructor 或者手写一个全参构造函数即可          */     } } 二、使用 @Tolerate 注解 四、属性拓展 lombok 基础注解之 @Builder.ObtainVia  五、@Builder 内部做了什么 创建一个名为 ThisClassBuilder 的内部静态类,并具有和实体类相同的属性(称为构建器) 在构建器中:对于目标类中的所有的属性和未初始化的 final 字段,都会在构建器中创建对应属性 在构建器中:创建一个无参的 default 构造函数 在构建器中:实体类中的每个参数,都会对应创建类似于 setter 的方法,方法名与该参数名相同。 并且返回值是构建器本身(便于链式调用) 在构建器中:会创建一个 build 方法,调用 build 方法,就会根据设置的值进行创建实体对象 在构建器中:会生成一个 toString 方法 在实体类中:会创建一个 builder 方法,它的目的是用来创建构建器 @Builder public class User {     private String username;     private String password; } 编译后 public class User {     private String username;     private String password;          User(String username, String password) {         this.username = username;         this.password = password;     }          public static User.UserBuilder builder() {         return new User.UserBuilder();     }          public static class UserBuilder {         private String username;         private String password;                  UserBuilder() {}                  public User.UserBuilder username(String username) {             this.username = username;             return this;         }                  public User.UserBuilder password(String password) {             this.password = password;             return this;         }                  public User build() {             return new User(this.username, this.password);         }                  public String toString() {             return "User.UserBuilder(username=" + this.username + ", password=" + this.password + ")";         }     } }  ———————————————— 原文链接:https://blog.csdn.net/qq_39249094/article/details/120881578 
  • [技术干货] 最全的 lombok 注解详情
     一、安装插件,eclipse 对于 lombok 的支持二、引入依赖 <dependency>     <groupId>org.projectlombok</groupId>     <artifactId>lombok</artifactId>     <version>1.18.20</version> <!-- Spring Boot 项目此部分可以不写 --> </dependency> 三、基本注解的使用(注解属性非必选) @AllArgsConstructor:作用于类,生成参数为所有实例变量的构造函数 @Builder:作用于类,将其变成建造者模式 @Cleanup:作用于变量,自动关闭资源,针对实现了 java.io.Closeable 接口的对象有效 @CustomLog:自定义日志类,生成 log 对象 @Data:作用于类,是以下注解的集合:@ToString @EqualsAndHashCode @Getter @Setter @RequiredArgsConstructor @EqualsAndHashCode:作用于类,覆盖默认的 equals 和 hashCode1 @Generated:用于标记类、变量、方法是自动生成的,没什么大用 @Getter:作用于类,生成该类所有的实例变量的 getter 方法。作用于变量,生成变量的 getter 方法 @NoArgsConstructor:作用于类,生成无参构造方法 @NonNull:作用于成员变量和参数中,标识不能为空,否则抛出空指针异常 @RequiredArgsConstructor:作用于类,生成包含 final 和 @NonNull 注解的成员变量的构造方法 @Setter:作用于类,生成该类所有的实例变量的 setter 方法。作用于变量,生成该变量的 setter 方法 @Singular:作用于集合字段,需要配合 @Builder 使用2 @SneakyThrows:作用于方法,对异常进行捕捉并抛出 @Synchronized:作用于方法,可以替换 synchronized 关键字或 lock 锁 @ToString:作用于类,覆盖默认的 toString() 方法 @val:作用于类、变量,主要用于声明变量的类型,注解将从初始化程序表达式中推断类型,生成的变量是 final 不可以变 @Value:作用于类,是以下注解的集合:@ToString @EqualsAndHashCode @Getter @RequiredArgsConstructor @var:和 @val 一样,两者区别在于 var 不加 final @With:作用于类、变量,生成 with + 变量名的方法,返回当前对象 四、外部注解的使用 @CommonsLog,@Log,@JBossLog,@Log4j,@Log4j2,@Slf4j,@XSlf4j:日志注解,作用于类 五、实验性注解的使用 @Accessors:类似于 @Builder 支持链式调用,需要配合 @Setter、@Getter 等注解使用,作用于类、变量 @Delegate:作用于容器变量,为该变量生成一堆常用的方法,这些方法都是容器中的方法 @ExtensionMethod:作用于类,向类添加方法,无需创建新的子类 @FieldDefaults:作用于类,定义变量的访问修饰符以及是否加 final @FieldNameConstants:作用于类,生成一个包含所有成员变量的内部类或者内部枚举,内部类中每个字段值即为字段名并且值不可变 @Helper:作用于方法内部类,使内部类中的方法暴露在外面可以被直接调用,不建议使用 @NonFinal:作用于类、变量,表示变量不加 final @PackagePrivate:作用于类和变量,相当于访问修饰符的 default,没什么用 @SuperBuilder:支持对于基类成员变量赋值,算是 @Builder 的升级版 @Tolerate:实现对冲突的兼容,作用于方法上,没什么大用,可以配合 @Builder 使用 @UtilityClass:作用于类,将类标记为 final,并且类、内部类中的方法、字段都标记为 static @WithBy3 六、基于 v1.18.22 版本的实验性注解 @StandardException:自定义异常类 七、Lombok 的优缺点 优点: 能通过注解的形式自动生成构造器、getter / setter、equals、hashcode、toString 等方法,提高了一定的开发效率 让代码变得简洁,不用过多的去关注相应的方法 属性做修改时,也简化了维护为这些属性所生成的 getter / setter 方法等 缺点: 不支持多种参数构造器的重载 虽然省去了手动创建一系列方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度  八、待完善的 @EqualsAndHashCode.Include(replaces = “”) @Singular 中的 ignoreNullCollections 属性待完善 @WithBy ———————————————— 原文链接:https://blog.csdn.net/qq_39249094/article/details/107313582 
  • [技术干货] BlockingQueue take()用法及代码示例
    BlockingQueue接口的take()方法用于检索和删除此队列的头。如果队列为空,则它将等待直到元素可用。如果在线程上工作并在该过程中使用BlockingQueue,则此方法效率更高。因此,如果没有可用的元素,则最初调用take()的线程将进入睡眠状态,从而让其他线程执行所需的任何操作。  用法:  public E take() throws InterruptedException  返回值:此方法在此BlockingQueue的开头返回值。如果队列为空,则它将等待直到元素可用。  异常:此方法引发以下异常:  InterruptedException–如果队列为空,则在等待元素变为可用时发生中断。  注意:BlockingQueue的take()方法已从Java中的Queue类继承。  以下示例程序旨在说明BlockingQueue接口的take()方法:  示例1:  // Java Program Demonstrate take()  // method of BlockingQueue  import java.util.concurrent.LinkedBlockingQueue;  import java.util.concurrent.BlockingQueue;  public class GFG {  public static void main(String[] args)  throws InterruptedException  {  // define capacity of BlockingQueue  int capacityOfQueue = 4;  // create object of BlockingQueue  BlockingQueue BQ  = new LinkedBlockingQueue(capacityOfQueue);  // Add element to BlockingQueue  BQ.add("Ravi");  BQ.add("Suraj");  BQ.add("Harsh");  BQ.add("Sayan");  // print elements of queue  System.out.println("Items in Queue are " + BQ);  // remove two elements from queue from head  // Appling take() method on queue to remove element  String removedItem1 = BQ.take();  // print removedItem and queue  System.out.println("Removed Item from head is "  + removedItem1);  // print elements of queue after removing first item  System.out.println("Remaining Items in Queue are "  + BQ);  // Appling take() method on queue to remove another element  String removedItem2 = BQ.take();  // print removedItem and queue  System.out.println("Removed Item from head is "  + removedItem2);  // print elements of queue after removing first item  System.out.println("Remaining Items in Queue are "  + BQ);  }  }  输出:  Items in Queue are [Ravi, Suraj, Harsh, Sayan]  Removed Item from head is Ravi  Remaining Items in Queue are [Suraj, Harsh, Sayan]  Removed Item from head is Suraj  Remaining Items in Queue are [Harsh, Sayan]  示例2:  // Java Program Demonstrate take()  // method of BlockingQueue  import java.util.concurrent.LinkedBlockingQueue;  import java.util.concurrent.BlockingQueue;  public class GFG {  public void takeDemo() throws InterruptedException  {  // define capacity of BlockingQueue  int capacityOfQueue = 5;  // create object of BlockingQueue  BlockingQueue BQ  = new LinkedBlockingQueue(capacityOfQueue);  // Add element to BlockingQueue  Employee emp1 = new Employee("Ravi", "Tester", "39000");  Employee emp2 = new Employee("Sanjeet", "Manager", "98000");  // Add Employee Objects to BQ  BQ.add(emp1);  BQ.add(emp2);  // remove elements from the queue  // and follow this process again and again  // until the queue becomes empty  while (BQ.size() != 0) {  // Remove Employee item from BlockingQueue  // using take()  Employee removedEmp = BQ.take();  // print removedItem  System.out.println("Removed Item is :");  System.out.println("Employee Name - "  + removedEmp.name);  System.out.println("Employee Position - "  + removedEmp.position);  System.out.println("Employee Salary - "  + removedEmp.salary);  // find size of BQ  int size = BQ.size();  // print remaining capacity value  System.out.println("\nSize of list :" + size + "\n");  }  }  // create an Employee Object with name,  // position and salary as an attribute  public class Employee {  public String name;  public String position;  public String salary;  Employee(String name, String position, String salary)  {  this.name = name;  this.position = position;  this.salary = salary;  }  @Override  public String toString()  {  return "Employee [name=" + name + ", position="  + position + ", salary=" + salary + "]";  }  }  // Main Method  public static void main(String[] args)  {  GFG gfg = new GFG();  try {  gfg.takeDemo();  }  catch (Exception e) {  e.printStackTrace();  }  }  }  输出:  Removed Item is :  Employee Name - Ravi  Employee Position - Tester  Employee Salary - 39000  Size of list :1  Removed Item is :  Employee Name - Sanjeet  Employee Position - Manager  Employee Salary - 98000  Size of list :0 ———————————————— 原文链接:https://blog.csdn.net/weixin_39956009/article/details/114159183 
  • [专题汇总] 2023年4月论坛问题合集
    Codearts:开发者认证考试券如何兑换?cid:link_1码豆过期cid:link_2请问为何去年12月底兑换的东西今年4月份还没到呀 cid:link_3应用服务自己网站如何设置只能是特定用户登录cid:link_4小程序的文本能复制吗cid:link_5数据库【执行报错】CCN排队导致执行超时canceling statement due to user requestcid:link_0【时区问题】MySQL到DWS数据迁移时间相差8小时cid:link_6openGauss配置文件的备份与恢复cid:link_7DWS JDBC 开启SSL认证连接cid:link_8【驱动】现网局点通过中间件读取dws报错cid:link_9有关GaussDB(DWS)数据库服务器磁盘空间问题cid:link_10GaussDB(DWS)在哪个版本会支持analyze单分区?cid:link_11Iot:现在华为云没有基础版,只有标准版嘛cid:link_12看去年大佬的应用编程接口无法获取华为云的数据了cid:link_13华为云的物联网改版了吗cid:link_14应用侧接入物联网平台的证书问题cid:link_15LOS_start()任务无法调度的问题cid:link_16目前的版本如何进行非接管中断方式的配置cid:link_17为什么我的华为云创建的centors7.4中命令无效 https://bbs.huaweicloud.com/forum/thread-0224115956094821001-1-1.html
  • [技术干货] JAVA如何实现文件的压缩和解压
    前言:最近开发一个文件中转管理系统,需要先把PowerBI数据湖中的数据生成Excel后放中转系统,待消费方(SAP系统)消费完以后再把这些数据删除掉。因为从PowerBI生成的Excel文件有点大,数据传输经常会出现超时的情况,于是便决定先把生成的Excel压缩成.rar文件后再传输。所以这里需要使用到文件的压缩和解压技术。JAVA压缩解压文件代码:pom.xml codeduidaima.com<dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.11</version></dependency>GZipUtil.java codeduidaima.compackage com.app.core.util;import lombok.extern.log4j.Log4j2;import org.apache.commons.codec.CharEncoding;import org.apache.commons.codec.binary.Base64;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.util.zip.GZIPInputStream;import java.util.zip.GZIPOutputStream;@Log4j2public class GZipUtil {/*** 压缩GZip* 堆代码 duidaima.com* @return String*/public static String gZip(String input) {byte[] bytes = null;GZIPOutputStream gzip = null;ByteArrayOutputStream bos = null;try {bos = new ByteArrayOutputStream();gzip = new GZIPOutputStream(bos);gzip.write(input.getBytes(CharEncoding.UTF_8));gzip.finish();gzip.close();bytes = bos.toByteArray();bos.close();} catch (Exception e) {log.error("压缩出错:", e);} finally {try {if (gzip != null)gzip.close();if (bos != null)bos.close();} catch (final IOException ioe) {log.error("压缩出错:", ioe);}}return Base64.encodeBase64String(bytes);}/*** 解压GZip** @return String*/public static String unGZip(String input) {byte[] bytes;String out = input;GZIPInputStream gzip = null;ByteArrayInputStream bis;ByteArrayOutputStream bos = null;try {bis = new ByteArrayInputStream(Base64.decodeBase64(input));gzip = new GZIPInputStream(bis);byte[] buf = new byte[1024];int num;bos = new ByteArrayOutputStream();while ((num = gzip.read(buf, 0, buf.length)) != -1) {bos.write(buf, 0, num);}bytes = bos.toByteArray();out = new String(bytes, CharEncoding.UTF_8);gzip.close();bis.close();bos.flush();bos.close();} catch (Exception e) {log.error("解压出错:", e);} finally {try {if (gzip != null)gzip.close();if (bos != null)bos.close();} catch (final IOException ioe) {log.error("解压出错:", ioe);}}return out;}}总结:以上就是基于JAVA实现的文件压缩和解压的代码,谢谢阅读。转载自cid:link_0Group/Topic/JAVA/10372
  • [技术干货] 剖析JAVA中Stream流的实现原理
    Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。本文会对Stream的实现原理进行剖析。Stream的组成与特点Stream(流)是一个来自数据源的元素队列并支持聚合操作:1.元素是特定类型的对象,形成一个队列。Java中的Stream并_不会_向集合那样存储和管理元素,而是按需计算2.数据源流的来源可以是集合Collection、数组Array、I/O channel, 产生器generator 等3.聚合操作类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等和以前的Collection操作不同, Stream操作还有两个基础的特征:1.Pipelining: 中间操作都会返回流对象本身。这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。这样做可以对操作进行优化, 比如延迟执行(laziness evaluation)和短路( short-circuiting)2.内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。Stream提供了内部迭代的方式, 通过访问者模式 (Visitor)实现。和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 ForkJoin 框架(JSR166y)来拆分任务和加速处理过程。Java 的并行 API 演变历程基本如下:1.0-1.4 中的 java.lang.Thread5.0 中的 java.util.concurrent6.0 中的 Phasers 等7.0 中的 Fork/Join 框架8.0 中的 LambdaStream具有平行处理能力,处理的过程会分而治之,也就是将一个大任务切分成多个小任务,这表示每个任务都是一个操作: codeduidaima.comList<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);numbers.parallelStream().forEach(out::println);可以看到一行简单的代码就帮我们实现了并行输出集合中元素的功能,但是由于并行执行的顺序是不可控的所以每次执行的结果不一定相同。如果非得相同可以使用forEachOrdered方法执行终止操作: codeduidaima.comList<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);numbers.parallelStream().forEachOrdered(out::println);这里有一个疑问,如果结果需要有序,是否和我们的并行执行的初衷相悖?是的,这个场景下明显无需使用并行流,直接用串行流执行即可, 否则性能可能更差,因为最后又强行将所有并行结果进行了排序。OK,下面我们先介绍一下Stream接口的相关知识。BaseStream接口Stream的父接口是BaseStream,后者是所有流实现的顶层接口,定义如下: codeduidaima.compublic interface BaseStream<T, S extends BaseStream<T, S>>extends AutoCloseable {Iterator<T> iterator();Spliterator<T> spliterator();boolean isParallel();S sequential();S parallel();S unordered();S onClose(Runnable closeHandler);void close();}其中,T为流中元素的类型,S为一个BaseStream的实现类,它里面的元素也是T并且S同样是自己: codeduidaima.comS extends BaseStream<T, S>是不是有点晕?其实很好理解,我们看一下接口中对S的使用就知道了:如sequential()、parallel()这两个方法,它们都返回了S实例,也就是说它们分别支持对当前流进行串行或者并行的操作,并返回「改变」后的流对象。“如果是并行一定涉及到对当前流的拆分,即将一个流拆分成多个子流,子流肯定和父流的类型是一致的。子流可以继续拆分子流,一直拆分下去…”也就是说这里的S是BaseStream的一个实现类,它同样是一个流,比如Stream、IntStream、LongStream等。Stream接口再来看一下Stream的接口声明: codeduidaima.compublic interface Stream<T> extends BaseStream<T, Stream<T>>参考上面的解释这里不难理解:即Stream<T>可以继续拆分为Stream<T>,我们可以通过它的一些方法来证实: codeduidaima.comStream<T> filter(Predicate<? super T> predicate);<R> Stream<R> map(Function<? super T, ? extends R> mapper);<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);Stream<T> sorted();Stream<T> peek(Consumer<? super T> action);Stream<T> limit(long maxSize);Stream<T> skip(long n);...这些都是操作流的中间操作,它们的返回结果必须是流对象本身。关闭流操作BaseStream 实现了 AutoCloseable 接口,也就是 close() 方法会在流关闭时被调用。同时,BaseStream 中还给我们提供了onClose()方法: codeduidaima.comS onClose(Runnable closeHandler);当AutoCloseable的close()接口被调用的时候会触发调用流对象的onClose()方法,但有几点需要注意:1. onClose() 方法会返回流对象本身,也就是说可以对改对象进行多次调用2.如果调用了多个onClose() 方法,它会按照调用的顺序触发,但是如果某个方法有异常则只会向上抛出第一个异常3.前一个 onClose() 方法抛出了异常不会影响后续 onClose() 方法的使用4.如果多个 onClose() 方法都抛出异常,只展示第一个异常的堆栈,而其他异常会被压缩,只展示部分信息并行流和串行流BaseStream接口中分别提供了并行流和串行流两个方法,这两个方法可以任意调用若干次,也可以混合调用,但最终只会以最后一次方法调用的返回结果为准。参考parallel()方法的说明:“Returns an equivalent stream that is parallel. May return itself, either because the stream was already parallel, or because the underlying stream state was modified to be parallel.”所以多次调用同样的方法并不会生成新的流,而是直接复用当前的流对象。下面的例子里以最后一次调用parallel()为准,最终是并行地计算sum: codeduidaima.comstream.parallel().filter(...).sequential().map(...).parallel().sum();ParallelStream背后的男人:ForkJoinPoolForkJoin框架是从JDK7中新特性,它同ThreadPoolExecutor一样,也实现了Executor和ExecutorService 接口。它使用了一个「无限队列」来保存需要执行的任务,而线程的数量则是通过构造函数传入, 如果没有向构造函数中传入希望的线程数量,那么当前计算机可用的CPU数量会被设置为线程数量作为默认值。ForkJoinPool主要用来使用分治法(Divide-and-Conquer Algorithm) 来解决问题,典型的应用比如_快速排序算法_。这里的要点在于,ForkJoinPool需要使用相对少的线程来处理大量的任务。比如要对1000万个数据进行排序,那么会将这个任务分割成两个500 万的排序任务和一个针对这两组500万数据的合并任务。以此类推,对于500万的数据也会做出同样的分割处理,到最后会设置一个阈值来规定当数据规模到多少时,停止这样的分割处理。比如,当元素的数量小于10时,会停止分割,转而使用插入排序对它们进行排序。那么到最后,所有的任务加起来会有大概2000000+个。“问题的关键在于,对于一个任务而言,只有当它所有的子任务完成之后,它才能够被执行,想象一下归并排序的过程。”所以当使用ThreadPoolExecutor时,使用分治法会存在问题,因为ThreadPoolExecutor中的线程无法向 任务队列中再添加一个任务并且在等待该任务完成之后再继续执行。而使用ForkJoinPool时,就能够让其中的线程创建新的任务,并挂起当前的任务,此时线程就能够从队列中选择子任务执行。那么使用ThreadPoolExecutor或者ForkJoinPool,会有什么性能的差异呢?首先,使用ForkJoinPool能够使用数量有限的线程来完成非常多的具有「父子关系」的任务,比如使用4个线程来完成超过200万个任务。使用ThreadPoolExecutor 时,是不可能完成的,因为ThreadPoolExecutor中的Thread无法选择优先执行子任务,需要完成200万个具有父子关系的任务时,也需要200万个线程,显然这是不可行的。Work Stealing原理:1.每个工作线程都有自己的工作队列WorkQueue;2.这是一个双端队列dequeue,它是线程私有的;3.ForkJoinTask中fork的子任务,将放入运行该任务的工作线程的队头,工作线程将以LIFO的顺序来处理工作队列中的任务,即堆栈的方式;4.为了最大化地利用CPU,空闲的线程将从其它线程的队列中「窃取」任务来执行5.但是是从工作队列的尾部窃取任务,以减少和队列所属线程之间的竞争;6.双端队列的操作:push()/pop()仅在其所有者工作线程中调用,poll()是由其它线程窃取任务时调用的;7.当只剩下最后一个任务时,还是会存在竞争,是通过CAS来实现的;用ForkJoinPool的眼光来看ParallelStreamJava 8为ForkJoinPool添加了一个通用线程池,这个线程池用来处理那些没有被显式提交到任何线程池的任务。它是ForkJoinPool类型上的一个静态元素,它拥有的默认线程数量等于运行计算机上的CPU数量。当调用Arrays 类上添加的新方法时,自动并行化就会发生。比如用来排序一个数组的并行快速排序,用来对一个数组中的元素进行并行遍历。自动并行化也被运用在Java 8新添加的Stream API中。比如下面的代码用来遍历列表中的元素并执行需要的操作: codeduidaima.comList<UserInfo> userInfoList =DaoContainers.getUserInfoDAO().queryAllByList(new UserInfoModel());userInfoList.parallelStream().forEach(RedisUserApi::setUserIdUserInfo);对于列表中的元素的操作都会以并行的方式执行。forEach方法会为每个元素的计算操作创建一个任务,该任务会被前文中提到的ForkJoinPool中的commonPool处理。以上的并行计算逻辑当然也可以使用ThreadPoolExecutor完成,但是就代码的可读性和代码量而言,使用ForkJoinPool明显更胜一筹。对于ForkJoinPool通用线程池的线程数量,通常使用默认值就可以了,即运行时计算机的处理器数量。也可以通过设置系统属性:-Djava.util.concurrent .ForkJoinPool.common.parallelism=N (N为线程数量),来调整ForkJoinPool的线程数量。值得注意的是,当前执行的线程也会被用来执行任务,所以最终的线程个数为N+1,1就是当前的主线程。这里就有一个问题,如果你在并行流的执行计算使用了_阻塞操作_,如I/O,那么很可能会导致一些问题: codeduidaima.compublic static String query(String question) {// 堆代码 duidaima.comList<String> engines = new ArrayList<String>();engines.add("http://www.google.com/?q=");engines.add("http://duckduckgo.com/?q=");engines.add("http://www.bing.com/search?q=");// get element as soon as it is availableOptional<String> result = engines.stream().parallel().map((base) - {String url = base + question;// open connection and fetch the resultreturn WS.url(url).get();}).findAny();return result.get();}这个例子很典型,让我们来分析一下:这个并行流计算操作将由主线程和JVM默认的ForkJoinPool.commonPool()来共同执行。map中是一个阻塞方法,需要通过访问HTTP接口并得到它的response,所以任何一个worker线程在执行到这里的时候都会阻塞并等待结果。所以当此时再其他地方通过并行流方式调用计算方法的时候,将会受到此处阻塞等待的方法的影响。目前的ForkJoinPool的实现并未考虑补偿等待那些阻塞在等待新生成的线程的工作worker线程,所以最终ForkJoinPool.commonPool()中的线程将备用光并且阻塞等待。“正如我们上面那个列子的情况分析得知,lambda的执行并不是瞬间完成的,所有使用parallel streams的程序都有可能成为阻塞程序的源头, 并且在执行过程中程序中的其他部分将无法访问这些workers,这意味着任何依赖parallel streams的程序在什么别的东西占用着common ForkJoinPool时将会变得不可预知并且暗藏危机。”小结:1.当需要处理递归分治算法时,考虑使用ForkJoinPool。2.仔细设置不再进行任务划分的阈值,这个阈值对性能有影响。3. Java 8中的一些特性会使用到ForkJoinPool中的通用线程池。在某些场合下,需要调整该线程池的默认的线程数量4.lambda应该尽量避免副作用,也就是说,避免突变基于堆的状态以及任何IO5.lambda应该互不干扰,也就是说避免修改数据源(因为这可能带来线程安全的问题)6.避免访问在流操作生命周期内可能会改变的状态并行流的性能并行流框架的性能受以下因素影响:1.数据大小:数据够大,每个管道处理时间够长,并行才有意义;2.源数据结构:每个管道操作都是基于初始数据源,通常是集合,将不同的集合数据源分割会有一定消耗;3.装箱:处理基本类型比装箱类型要快;4.核的数量:默认情况下,核数量越多,底层forkjoin线程池启动线程就越多;5.单元处理开销:花在流中每个元素身上的时间越长,并行操作带来的性能提升越明显;源数据结构分为以下3组:性能好:ArrayList、数组或IntStream.range(数据支持随机读取,能轻易地被任意分割)性能一般:HashSet、TreeSet(数据不易公平地分解,大部分也是可以的)性能差:LinkedList(需要遍历链表,难以对半分解)、Stream.iterate和BufferedReader.lines(长度未知,难以分解)注意:下面几个部分节选自:Streams 的幕后原理,顺便感谢一下作者_Brian Goetz_,写的太通透了。NQ模型要确定并行性是否会带来提速,需要考虑的最后两个因素是:可用的数据量和针对每个数据元素执行的计算量。在我们最初的并行分解描述中,我们采用的概念是拆分来源,直到分段足够小,以致解决该分段上的问题的顺序方法更高效。分段大小必须依赖于所解决的问题,确切的讲,取决于每个元素完成的工作量。例如,计算一个字符串的长度涉及的工作比计算字符串的 SHA-1 哈希值要少得多。为每个元素完成的工作越多,“大到足够利用并行性” 的阈值就越低。类似地,拥有的数据越多, 拆分的分段就越多,而不会与 “太小” 阈值发生冲突。一个简单但有用的并行性能模型是 NQ 模型,其中 N 是数据元素数量,Q 是为每个元素执行的工作量。乘积 N*Q 越大,就越有可能获得并行提速。对于具有很小的 Q 的问题,比如对数字求和,您通常可能希望看到 N > 10,000 以获得提速;随着 Q 增加,获得提速所需的数据大小将会减小。并行化的许多阻碍(比如拆分成本、组合成本或遇到顺序敏感性)都可以通过 Q 更高的操作来缓解。尽管拆分某个 LinkedList 特征的结果可能很糟糕,但只要拥有足够大的 Q,仍然可能获得并行提速。遇到顺序遇到顺序指的是来源分发元素的顺序是否对计算至关重要。一些来源(比如基于哈希的集合和映射)没有有意义的遇到顺序。流标志 ORDERED 描述了流是否有有意义的遇到顺序。JDK 集合的 spliterator 会根据集合的规范来设置此标志;一些中间操作可能注入 ORDERED (sorted()) 或清除它 (unordered())。如果流没有遇到顺序,大部分流操作都必须遵守该顺序。对于顺序执行,会「自动保留遇到顺序」,因为元素会按遇到它们的顺序自然地处理。甚至在并行执行中,许多操作(无状态中间操作和一些终止操作(比如 reduce())),遵守遇到顺序不会产生任何实际成本。但对于其他操作(有状态中间操作,其语义与遇到顺序关联的终止操作,比如 findFirst() 或 forEachOrdered()), 在并行执行中遵守遇到顺序的责任可能很重大。如果流有一个已定义的遇到顺序,但该顺序对结果没有意义, 那么可以通过使用 unordered() 操作删除 ORDERED 标志,加速包含顺序敏感型操作的管道的顺序执行。作为对遇到顺序敏感的操作的示例,可以考虑 limit(),它会在指定大小处截断一个流。在顺序执行中实现 limit() 很简单:保留一个已看到多少元素的计数器,在这之后丢弃任何元素。但是在并行执行中,实现 limit() 要复杂得多;您需要保留前 N 个元素。此要求大大限制了利用并行性的能力;如果输入划分为多个部分,您只有在某个部分之前的所有部分都已完成后,才知道该部分的结果是否将包含在最终结果中。因此,该实现一般会错误地选择不使用所有可用的核心,或者缓存整个试验性结果,直到您达到目标长度。如果流没有遇到顺序,limit() 操作可以自由选择任何 N 个元素,这让执行效率变得高得多。知道元素后可立即将其发往下游, 无需任何缓存,而且线程之间唯一需要执行的协调是发送一个信号来确保未超出目标流长度。遇到顺序成本的另一个不太常见的示例是排序。如果遇到顺序有意义,那么 sorted() 操作会实现一种稳定 排序 (相同的元素按照它们进入输入时的相同顺序出现在输出中),而对于无序的流,稳定性(具有成本)不是必需的。distinct() 具有类似的情况:如果流有一个遇到顺序,那么对于多个相同的输入元素,distinct() 必须发出其中的第一个, 而对于无序的流,它可以发出任何元素 — 同样可以获得高效得多的并行实现。在使用 collect() 聚合时会遇到类似的情形。如果在无序流上执行 collect(groupingBy()) 操作, 与任何键对应的元素都必须按它们在输入中出现的顺序提供给下游收集器。此顺序对应用程序通常没有什么意义,而且任何顺序都没有意义。在这些情况下,可能最好选择一个并发 收集器(比如 groupingByConcurrent()),它可以忽略遇到顺序, 并让所有线程直接收集到一个共享的并发数据结构中(比如 ConcurrentHashMap),而不是让每个线程收集到它自己的中间映射中, 然后再合并中间映射(这可能产生很高的成本)。转载自cid:link_0Group/Topic/JAVA/10470
  • [技术干货] JAVA中response对象和request对象的用法
    概念request:获取请求数据response:设置响应数据Requestrequest继承体系ServletRequest——Java提供的请求对象根接口HttpServletRequest——Java提供的对http协议封装的请求对象接口RequestFacade——tomcat实现定义类request获取数据 codeduidaima.compackage com.itheijma.web;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.BufferedReader;import java.io.IOException;@WebServlet("/req1")public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//堆代码 duidaima.com//String method() 获取请求方式String method = req.getMethod();System.out.println(method);//String getContextPath() 获取虚拟目录(项目访问路径)String contextPath = req.getContextPath();System.out.println(contextPath);//StringBuffer getRequestURL() 获取URL(统一资源定位符)StringBuffer url = req.getRequestURL();System.out.println(url.toString());//String getRequestURI() 获取URI(统一资源标识符)String uri = req.getRequestURI();System.out.println(uri);//String getQueryString() 获取请求参数(get方式)String queryString = req.getQueryString();System.out.println(queryString);//------------//获取请求头String agent = req.getHeader("user-agent");System.out.println(agent);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取post请求体:请求参数// 1. 获取字符输入流BufferedReader br = req.getReader();// 2. 读取数据String line = br.readLine();System.out.println(line);}}request通用的方式获取数据 codeduidaima.compackage com.itheijma.web;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.Map;@WebServlet("/req2")public class RequestDemo2 extends HttpServlet{@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//get请求逻辑// System.out.println("get...");// 1.获取所有参数的map集合Map<String, String[]> map = req.getParameterMap();for (String key : map.keySet()) {//username:zhangsanSystem.out.print(key+":");//获取值String[] values = map.get(key);for (String value : values) {System.out.print(value + " ");}System.out.println();}System.out.println("----------");// 2.根据key获取参数值,数组String[] hobbies = req.getParameterValues("hobby");for (String hobby : hobbies) {System.out.println(hobby);}// 3. 根据key获取单个参数值String username = req.getParameter("username");String password = req.getParameter("password");System.out.println(username);System.out.println(password);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//post请求逻辑this.doGet(req,resp);// System.out.println("post...");// // 1.获取所有参数的map集合// Map<String, String[]> map = req.getParameterMap();// for (String key : map.keySet()) {// //username:zhangsan// System.out.print(key+":");//// //获取值// String[] values = map.get(key);// for (String value : values) {// System.out.print(value + " ");// }//// System.out.println();// }//// System.out.println("----------");//// // 2.根据key获取参数值,数组// String[] hobbies = req.getParameterValues("hobby");// for (String hobby : hobbies) {//// System.out.println(hobby);// }//// // 3. 根据key获取单个参数值// String username = req.getParameter("username");// String password = req.getParameter("password");// System.out.println(username);// System.out.println(password);}}request请求参数中文乱码解决方法 codeduidaima.compackage com.itheijma.web;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.nio.charset.StandardCharsets;/*** 中文乱码问题解决方案*/@WebServlet("/req3")public class RequestDemo3 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1. 解决乱码 POST 获取参数方式:getReader()读取数据流request.setCharacterEncoding("utf-8"); //设置字符输入流的编码// 2. 获取usernameString username = request.getParameter("username");System.out.println("解决乱码前:"+username);// 3. 解决乱码 GET 获取参数方式:getQueryString// 乱码原因:tomcat进行url解码,默认的字符集ISO-8859-1// // 3.1 先对乱码数据进行编码:转为字节数组// byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);// // 3.2 字节数组解码// username = new String(bytes, StandardCharsets.UTF_8);username = new String(username.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);System.out.println("解决乱码后"+username);}}request请求转发请求转发(forward):一种在服务器内部的资源跳转方式。请求转发特点:1.浏览器地址栏路径不发生变化2.只能转发到当前服务器的内部资源3.一次请求,可以在转发的资源间使用request共享数据req4 codeduidaima.compackage com.itheijma.web;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.nio.charset.StandardCharsets;/*** 请求转发*/@WebServlet("/req4")public class RequestDemo4 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("demo4");//存储数据request.setAttribute("msg","hello");//请求转发request.getRequestDispatcher("/req5").forward(request,response);}}req5 codeduidaima.compackage com.itheijma.web;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/req5")public class RequestDemo5 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("demo5");//获取数据Object msg = request.getAttribute("msg");System.out.println(msg);}}responseResponse完成重定向重定向:一种资源跳转的方式resp1 codeduidaima.compackage com.itheima.web.response;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/resp1")public class ResponseDemo1 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("resp1...");// // 重定向// // 1. 设置状态码 302// response.setStatus(302);// // 2. 设置响应头 Location// response.setHeader("Location","/tomcat-demo2/resp2");// 简化方式完成重定向response.sendRedirect("/tomcat-demo2/resp2");}}resp2 codeduidaima.compackage com.itheima.web.response;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/resp2")public class ResponseDemo2 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("resp2...");}}重定向特点:1.浏览器地址栏路径发生变化2.可以重定向到任意位置的资源(服务器内、外部均可)3.两次请求,不能在多个资源使用request共享数据4.资源路径问题(什么时候加虚拟目录)5.浏览器使用:需要加虚拟目录6.服务端使用:不需要加虚拟目录动态获取虚拟目录 codeduidaima.com//动态获取虚拟目录String contextPath = request.getContextPath();response.sendRedirect(contextPath + "/resp2");Response响应字符数据package com.itheima.web.response;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;/*** 响应字符数据:设置字符数据的响应体*/@WebServlet("/resp3")public class ResponseDemo3 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");//获取字符输出流PrintWriter writer = response.getWriter();//content-type//response.setHeader("content-type","text/html");writer.write("你好");writer.write("<h1>aaa</h1>");//细节:流不需要关闭}}Response响应字节数据 codeduidaima.compackage com.itheima.web.response;import org.apache.commons.io.IOUtils;import javax.servlet.ServletException;import javax.servlet.ServletOutputStream;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.FileInputStream;import java.io.IOException;import java.io.PrintWriter;/*** 响应字节数据:设置字节数据的响应体*/@WebServlet("/resp4")public class ResponseDemo4 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1. 读取文件FileInputStream fis = new FileInputStream("e://a.jpg");// 2. 获取response字节输出流ServletOutputStream os = response.getOutputStream();// 3. 完成流的copy// byte[] buff = new byte[1024];// int len = 0;// while ((len = fis.read(buff)) != -1){// os.write(buff,0,len);// }IOUtils.copy(fis,os);//使用工具类fis.close();}}pom文件导入工具类 codeduidaima.com<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency>转载自cid:link_0Group/Topic/JAVA/10558
  • [技术干货] JAVA使用多线程技术实现支持断点续传的超大文件下载功能
    前言:文件下载是我们开发系统时非常常见的一种的需求,对于小的文件,使用基本的超链接下载就可以了。但是对于一些大型文件的下载我们必须使用一些特别的技术,比如,多线程,断点续传等,今天我们就分享一段使用多线程支持断点续传的超大文件下载方式。实现代码: codeduidaima.comimport java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;import javax.print.attribute.standard.Finishings;public class MultiDownload {// 堆代码 duidaima.comstatic int ThreadCount = 3; //代表3个进程static int finishedThread = 0; //记录文件下完的数字//确定下载地址static String path = "http://192.168.13.13:8080/QQPlayer.exe";public static void main(String[] args) {//发送get请求,请求这个地址的资源try {URL url = new URL(path);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(5000);conn.setReadTimeout(5000);if(conn.getResponseCode() == 200){//拿到所请求资源文件的长度int length = conn.getContentLength();File file = new File("QQPlayer.exe");//生成临时文件RandomAccessFile raf = new RandomAccessFile(file, "rwd");//设置临时文件的大小raf.setLength(length);raf.close();//计算出每个线程应该下载多少字节int size = length / ThreadCount;for (int i = 0; i < ThreadCount; i++) {//计算线程下载的开始位置和结束位置int startIndex = i * size;int endIndex = (i + 1) * size - 1;//如果是最后一个线程,那么结束位置写死if(i == ThreadCount - 1){endIndex = length - 1;}// System.out.println("线程" + i + "的下载区间是:" + startIndex + "---" + endIndex);new DownLoadThread(startIndex, endIndex, i).start();}}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}class DownLoadThread extends Thread{int startIndex;int endIndex;int threadId;public DownLoadThread(int startIndex, int endIndex, int threadId) {super();this.startIndex = startIndex;this.endIndex = endIndex;this.threadId = threadId;}@Overridepublic void run() {//再次发送http请求,下载原文件try {File progressFile = new File(threadId + ".txt");//判断进度临时文件是否存在if(progressFile.exists()){FileInputStream fis = new FileInputStream(progressFile);BufferedReader br = new BufferedReader(new InputStreamReader(fis));//从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置startIndex += Integer.parseInt(br.readLine());fis.close();}System.out.println("线程" + threadId + "的下载区间是:" + startIndex + "---" + endIndex);HttpURLConnection conn;URL url = new URL(MultiDownload.path);conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(5000);conn.setReadTimeout(5000);//设置本次http请求所请求的数据的区间conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);//请求部分数据,相应码是206if(conn.getResponseCode() == 206){//流里此时只有1/3原文件的数据InputStream is = conn.getInputStream();byte[] b = new byte[1024];int len = 0;int total = 0;//拿到临时文件的输出流File file = new File("QQPlayer.exe");RandomAccessFile raf = new RandomAccessFile(file, "rwd");//把文件的写入位置移动至startIndexraf.seek(startIndex);while((len = is.read(b)) != -1){//每次读取流里数据之后,同步把数据写入临时文件raf.write(b, 0, len);total += len;// System.out.println("线程" + threadId + "下载了" + total);//生成一个专门用来记录下载进度的临时文件RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd");//每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中progressRaf.write((total + "").getBytes());progressRaf.close();}System.out.println("线程" + threadId + "下载完毕-------------------小志参上!");raf.close();//把记录进程的文件删除 必须3个进程全部都下载完成后才可以删除MultiDownload.finishedThread++;synchronized (MultiDownload.path) { //有线程安全问题,静态变量为唯一的if(MultiDownload.finishedThread == MultiDownload.ThreadCount){for (int i = 0; i < MultiDownload.ThreadCount; i++) {File f = new File(i + ".txt");f.delete();}MultiDownload.finishedThread = 0;}}}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}总结:以上就是基于JAVA技术实现的使用多线程支持断点续传的代码下载实现方式。使用多线程可以提高文件的下载速度,而断点续传解决了超大文件下载时间长,中间容易出现断网,关机等意外造成的下载中断的问题。如果你们对于超大文件的下载有更多好的实现方式,欢迎大家在评论区留言讨论。转载自cid:link_0Group/Topic/JAVA/10616
  • [技术干货] 在JAVA中使用com.google.zxing实现生成二维码功能
    前言:最近在开发个电商系统,需要有个自动生成二维码图片的功能,本文主要使用基于com.google.zxing实现生成二维码功能。具体实现功能如下:实现代码:pom支持: codeduidaima.com<!-- 二维码支持包 start--><dependency><groupid>com.google.zxing</groupid><artifactid>core</artifactid><version>3.2.0</version></dependency><dependency><groupid>com.google.zxing</groupid><artifactid>javase</artifactid><version>3.2.0</version></dependency><!-- 二维码支持包 end-->工具类支持: codeduidaima.compackage com.example.demo.dao.qrcode;import com.google.zxing.BarcodeFormat;import com.google.zxing.EncodeHintType;import com.google.zxing.MultiFormatWriter;import com.google.zxing.WriterException;import com.google.zxing.client.j2se.MatrixToImageWriter;import com.google.zxing.common.BitMatrix;import com.google.zxing.qrcode.QRCodeWriter;import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;import lombok.extern.slf4j.Slf4j;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;import javax.imageio.ImageIO;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.awt.*;import java.awt.image.BufferedImage;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.IOException;import java.util.Hashtable;/*** @ 堆代码 duidaima.com* @Title: springbootdemo* @Package com.example.demo.dao.qrcode* @Description: java web二维码生成器*/@Slf4jpublic class QRCodeUtil {/*** 方式一:流的方式直接生成web版本二维码* 默认的二维码外观(可设置高宽)* @param url 要生成二维码的路径* @param response response对象* @param width 二维码宽度* @param height 二维码高度* @throws IOException*/public static void getTwoDimension(String url, HttpServletResponse response, int width, int height) throws IOException {if (url != null && !"".equals(url)) {ServletOutputStream stream = null;try {stream = response.getOutputStream();QRCodeWriter writer = new QRCodeWriter();BitMatrix m = writer.encode(url, BarcodeFormat.QR_CODE, height, width);MatrixToImageWriter.writeToStream(m, "png", stream);} catch (WriterException e) {e.printStackTrace();log.error("生成二维码失败!");} finally {if (stream != null) {stream.flush();stream.close();}}}}/*** 方式二:返回ResponseEntity的方式显示二维码* 可设置二维码外观颜色,logo图片* @param url 二维码对于URL* @param width 二维码宽* @param height 二维码高* @param format 二维码格式* @param logPath logo图片路径* @param needLogo 二维码是否需要生成logo* @return* @throws WriterException* @throws IOException*/// 直接在页面显示public static ResponseEntity<byte[]> getResponseEntity(String url, String logPath, boolean needLogo,String format,int width,int height)throws Exception {BufferedImage image = QRCodeUtil.createImage(url, logPath,needLogo,width,height);ByteArrayOutputStream out = new ByteArrayOutputStream();ImageIO.write(image, format, out);//将BufferedImage转成out输出流HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);return new ResponseEntity<byte[]>(out.toByteArray(),headers, HttpStatus.CREATED);}/*** 方式三:以流的形式直接显示二维码* 可设置二维码外观颜色,logo图片*/public static void showQrcode(String url, String logPath,HttpServletResponse response, boolean needLog,String format,int width,int height)throws Exception {BufferedImage image = QRCodeUtil.createImage(url, logPath,needLog,width,height);boolean bl = ImageIO.write(image, format, response.getOutputStream());System.out.println(" boolean is " + bl);}/** 方式四:生成二维码文件存储* 可设置二维码外观颜色,logo图片* @param logPath* @param destPath 存储路径* @param fileName 存储文件名称(不需要后缀)* @param needLog* @param format* @param width* @param height* @return* @throws Exception*/public static boolean SaveQrCode(String url, String logPath,boolean needLog,String format,int width,int height,String destPath, String fileName) throws Exception {boolean flag = false;BufferedImage image;try {image = QRCodeUtil.createImage(url, logPath,needLog,width,height);File file = new File(destPath);// 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常)if (!file.exists() && !file.isDirectory()) {file.mkdirs();}String fileUrl = fileName + "."+format;ImageIO.write(image, format, new File(destPath + "/" + fileUrl));flag = true;} catch (Exception e) {e.printStackTrace();}return flag;}/*** 生成二维码图片私有方法*/private static BufferedImage createImage (String url, String imgPath,boolean needCompress,int width, int height) throws Exception {Hashtable hints = new Hashtable();// 二维码纠错级别:由高到低 H、Q、M、Lhints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);hints.put(EncodeHintType.CHARACTER_SET, "utf-8");// 二维码边界空白大小由大到小 4、3、2、1(默认为4)hints.put(EncodeHintType.MARGIN, 1);BitMatrix bitMatrix = new MultiFormatWriter().encode(url,BarcodeFormat.QR_CODE, width, height, hints);int H = bitMatrix.getHeight();int W = bitMatrix.getWidth();int L = getFinderPatternWidth(bitMatrix) + 3;int[] pixels = new int[W * H];// 二维码角颜色,依次为左上、左下、右上Color redColor = new Color(182, 0, 5);int redColorInt = redColor.getRGB();Color greenColor = new Color(0, 124, 54);int greenColorInt = greenColor.getRGB();Color blueColor = new Color(0, 64, 152);int blueColorInt = blueColor.getRGB();for (int y = 0; y < H; y++) {for (int x = 0; x < W; x++) {// 颜色渐变程度,RGB(158,255,158)int num1 = (int) (158 - (158.0 - 30.0) / bitMatrix.getHeight()* (y + 1));int num2 = (int) (255 - (255.0 - 80.0) / bitMatrix.getHeight()* (y + 1));int num3 = (int) (158 - (158.0 - 130.0) / bitMatrix.getHeight()* (y + 1));Color color = new Color(num1, num2, num3);int colorInt = color.getRGB();// 此处可以修改二维码的颜色,可以分别制定二维码和背景的颜色;pixels[y * W + x] = bitMatrix.get(x, y) ? colorInt : 0xffffff;}}BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);image.getRaster().setDataElements(0, 0, W, H, pixels);if (imgPath == null || "".equals(imgPath)) {return image;}// 插入图片QRCodeUtil.insertImage(image, imgPath, needCompress,width,height);return image;}private static int getFinderPatternWidth(BitMatrix matrix) {int W = matrix.getWidth();int H = matrix.getHeight();int length = 0;boolean flag = false;for (int y = 0; y < H; y++) {for (int x = 0; x < W; x++) {if (matrix.get(x, y) == true) {flag = true;length++;} else {if (flag != false) {return x;}}}}return length;}/*** 在二维码中插入图片*/private static void insertImage(BufferedImage source, String imgPath,boolean needCompress,int wid,int hei) throws Exception {File file = new File(imgPath);if (!file.exists()) {System.err.println("" + imgPath + " 该文件不存在!");return;}Image src = ImageIO.read(new File(imgPath));int width = src.getWidth(null);int height = src.getHeight(null);if (needCompress) {// 压缩LOGOif (width > wid) {width = wid;}if (height > hei) {height = hei;}Image image = src.getScaledInstance(width, height,Image.SCALE_SMOOTH);BufferedImage tag = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);// 绘制缩小后的图Graphics g = tag.getGraphics();g.drawImage(image, 0, 0, null);g.dispose();src = image;}// 插入LOGO位置Graphics2D graph = source.createGraphics();int x = (wid - width) / 2;int y = (hei - height) / 2;/** int x = QRCODE_SIZE - width - 20; int y = QRCODE_SIZE - height - 20;*/graph.drawImage(src, x, y, width, height, null);// logo边框/** Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);* graph.setStroke(new BasicStroke(3f)); graph.draw(shape);*/graph.dispose();}controller中调用: codeduidaima.compackage com.example.demo.controller;import com.example.demo.dao.qrcode.QRCodeUtil;import lombok.extern.slf4j.Slf4j;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** @堆代码 duidaima.com* @Title: springbootdemo* @Package com.example.demo.controller* @Description: ${todo}*/@Slf4j@Controllerpublic class QRCodeController {/*** 获得二维码* @param request* @param response*/@RequestMapping(value = "phoneversion/getTwoDimension",method={RequestMethod.POST,RequestMethod.GET})public void getTwoDimensionForIOSs(HttpServletRequest request, HttpServletResponse response){try {QRCodeUtil.getTwoDimension("https://bbs.hupu.com/bxj", response, 300, 300);} catch (IOException e) {e.printStackTrace();}}@RequestMapping("/downloadIOSAPPQRCode")public ResponseEntity<byte[]> downloadIOSAPPController(/*@RequestParam(required = true)String type*/)throws Exception{// InputStream is = this.getClass().getClassLoader().getResourceAsStream("app.properties");// Properties props = new Properties();// props.load(is);// String appId = (String)props.get(type);// String url = "" + appId;return QRCodeUtil.getResponseEntity("https://www.zhibo8.cc", "F:\\logo.png",true,"png", 300, 300);}@RequestMapping("/showQrcode")public void showQrcode(HttpServletRequest request, HttpServletResponse response)throws Exception{QRCodeUtil.showQrcode("https://www.zhibo8.cc", "",response,false,"png", 300, 300);}@RequestMapping("/SaveQrCode")public void SaveQrCode(HttpServletRequest request, HttpServletResponse response)throws Exception{boolean flag=QRCodeUtil.SaveQrCode("https://www.zhibo8.cc", "",false,"png", 300, 300,"D:\\","qrcode");log.info("flag=="+flag);}}前端显示: codeduidaima.com<meta charset="UTF-8"><title>首页</title>二维码图片1:<div><img src="/phoneversion/getTwoDimension" alt=""></div>二维码图片2:<img src="/downloadIOSAPPQRCode"><a href="/downloadIOSAPPQRCode">下载</a>二维码图片3:<img src="/showQrcode"><a href="/showQrcode">下载</a>总结:在二维码图片随处可见的今天,如何生成图片二维码是我们作为程序员在开发程序时非常常见的功能,本文主要分享了基于com.google.zxing实现生成二维码功能,希望对大家有所帮助。转载自cid:link_0Group/Topic/JAVA/10617
  • [集成开发] 【开发样例】hdfs-springboot
    springboot调用hdfs1 HDFS简介HDFS(Hadoop Distribute File System)是一个适合运行在通用硬件之上,具备高度容错特性,支持高吞吐量数据访问的分布式文件系统,非常适合大规模数据集应用2 样例背景HDFS的业务操作对象是文件,代码样例中所涉及的文件操作主要包括创建文件夹写文件追加文件内容读文件删除文件/文件夹HDFS还有其他的业务处理,例如设置文件权限等,其他操作可以在掌握本代码样例之后,再扩展学习。3 Windows环境样例调用步骤环境准备https://bbs.huaweicloud.com/forum/thread-88552-1-1.html比对时间,与集群时间误差不能超过5分钟检查 C:\Windows\System32\drivers\etc\hosts文件中是否包含所有集群节点的域名IP映射信息在IDEA打开样例代码中的hdfs-springboot目录,默认会自动下载依赖,如未下载,选中该目录下的pom.xml文件,右键点击“Add As Maven Project”后等待项目自动将依赖下载完毕从Manager界面下载用户认证凭据后,解压缩获取秘钥文件user.keytab和krb5.conf从客户端 /opt/client/HDFS/hadoop/etc/hadoop 目录中获取core-site.xml和hdfs-site.xml把上面获取的user.keytab\krb5.conf\core-stie.xml\hdfs-site.xml四个文件放到统一目录下配置application.properties中的用户名和配置文件存放目录(第7步文件的目录)打开测试类 HDFApplication.java, 文件右键执行Run 运行代码调用接口 POST cid:link_0创建hdfs目录4 Linux环境调试步骤完成Windows环境样例调用步骤在windows环境中执行打包检查linux环境时间与集群误差不超过5分钟检查linux环境的JDK版本为1.8检查linux环境的/etc/hosts文件中包含所有集群节点的域名IP映射信息创建样例执行路径,例如/opt/hdfstest上传windows环境打包后生成的target目录下的 hdfd-springboot-1.0-SNAPSHOT.jar 包到/opt/hdfstest目录上传windows环境中调试通过后的配置文件到/opt/hdfstest/conf目录配置application.properties中的用户名和配置文件存放目录(/opt/hdfstest/conf/)执行如下命令启动服务java –jar hdfs-springboot-1.0-SNAPSHOT.jar
总条数:2294 到第
上滑加载中