# java工具集 **Repository Path**: xiaozichen/jl ## Basic Information - **Project Name**: java工具集 - **Description**: 项目停止,模块太多,导致文档很乱,正在拆分模块,每个模块做成单独项目。 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2025-10-16 - **Last Updated**: 2025-10-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # java开发工具集 #### 介绍 这个项目东西多而杂,该项目不是为了实现某个功能,而是包含开发编码能用到的一些封装工具,提升开发效率以及代码优雅性,主要功能含: 1. 基于cloud微服务组建整合,因我内置了yml文件和抽象了配置属性,可达到最少配置搭建cloud框架且无需更改任何配置一键切换注册中心。 2. 自动生成spring.factories。 3. mybatis-plus代码生成器、条件构造器、舍弃xml实现基于对象连表查询、对象同步修改表数值等。 4. mongo条件构造器、更简单的Mapper。 5. es条件构造器、增强实现Mapper、sql查询工具。 6. hazelcast工具。 7. redis以及redisson分布式锁工具。 8. 二维码工具。 9. 线程池。 10. 增强Map。 11. 增强List。 12. 抽象判空封装,让任意数据类型判空只需使用一个方法即可,代码可读性更强。 13. 元祖。 14. 注解实现aes或rsa自动加解密。 15. 根据接口+方法名自动生成请求url的mvc方式,无需再配置springmvc注解(可与springmvc混用),让开发更无脑。 16. 简化版的@validated,一个注解实现99%项目所需的校验功能,基本思路和使用同@validated,但是更简单好用。 17. 纯反射实现bean的拷贝,性能是BeanUtils的10-20倍,也解决BeanCopier无法读取有返回值的set方法而导致无法使用@Accessors编程,还实现了常用数据类型的转换。 18. 递归生成树工具方法。 19. lambda引用解析工具,可以根据lambda引用获取类class、方法名、属性名,避免硬编码是开发首要的第一步。 20. spring接口代理bean封装,抽象共性,极大减少代码量。 21. 更多如:反射工具、集合操作工具、io工具等等请查看项目源码。 #### 使用 自行pull项目,把需要的模块用maven打成jar引入到自己的项目使用。 #### 软件架构 ```   jl ------concise-mvc #自动生成mvc模块 ------hazelcast #hazelcast jvm级缓存 ------mongo #mongo ------mybatis-plus #mybatis-plus ------elasticsearch #es ------redis #redis ------security #安全相关 ------spring-cloud #微服务相关 ------------consul #consul注册中心 ------------eureka #eureka注册中心 ------------nacos #nacos注册中心 ------------gateway #网关 ------spring-factories #自动生成spring.factories文件工具 ------tools #其他杂项工具 ``` ### spring-factories 解决不同项目之间依赖而包名不同无法注册sring bean的问题,maven insall时自动生成模块下bean的spring.factories文件,不需要写任何代码和配置,把spring-factories模块打成jar依赖进自己的项目即可,写工具类以及多模块开发必备神器。 ### concise-mvc 自动生成请求url,无需再配置@RestController、@RequestMapping、@GetMapping、@PostMapping等等之类的mvc注解(但是兼容,可与springmvc混用),让开发更无脑,兼容swagger,适合单体小型项目使用,不建议微服务使用(并不是不支持)。 1. 请求url为 /接口类名/方法名。 2. 请求参数有@RequestBody为post请求,无则为get请求。 3. @JLMvcApp添加了basePackages参数则把该路径下所有实现注册为url,否则把标注@JLMvc的接口、实现类注册为url。 4. 如果非要空返回或基本数据类型返回,那么controller需加上@RestController。 ``` @JLMvcApp //启动类需加该注解 @SpringBootApplication public class Test { public static void main(String[] args) { SpringApplication.run(Test.class, args); } /** * 接口 */ interface DemoService { DemoEntity test(DemoEntity demoEntity); DemoEntity demo(DemoEntity demoEntity); DemoEntity dev(DemoEntity demoEntity); } /** * 实现 * 例子使用@JLMvc方式,另可使用扫包方式从而省略@JLMvc注解,启动类配置@JLMvcApp basePackages参数即可 */ @JLMvc @Service @RestController //如果不混用springmvc可省略该注解,此处为例子 public static class DemoServiceImpl implements DemoService { /** * get请求 请求url为: /DemoService/test */ @Override public DemoEntity test(DemoEntity demoEntity) { return demoEntity; } /** * post请求 请求url为: /DemoService/demo */ @Override public DemoEntity demo(@RequestBody DemoEntity demoEntity) { return demoEntity; } /** * springmvc混用例子 请求url为: /dev */ @Override @GetMapping("dev") public DemoEntity dev(DemoEntity demoEntity) { return null; } } @Data public static class DemoEntity { private Integer id; private String name; } } ``` ### hazelcast #### JLHazelcast 注入JLHazelcast类,参考类上注释使用即可,这里只举一个使用hazelcast发布订阅的例子: ```         //只需加上@JLTopicName("订阅名")注解即可被扫描订阅类 @JLTopicName("test") @Service public class Test implements MessageListener { @Override public void onMessage(Message message) { System.out.println(message.getMessageObject()); } } ``` ### mongo #### 1. JLMongoMapper 搭配[JLQuery](https://gitee.com/laoshirenggo/jl/blob/master/README.md#2-jlquery)使用。基于MongoTemplate封装的一个父Mapper,使用spring接口代理技术,使api更面向对象,我个人比较反感MongoRepository的方式,CURD就该和mybatis-plus一样基于条件对象而不是增加方法。 方法列表: ``` save(T) 新增 saveOrUpdate(T) 存在修改,不存在新增 update(Query, T) 修改 remove(T) 删除 remove(Query) 删除 count(Query) 统计行数 getById(Object) 根据id查询 getAndUpdate(Query, T) 查询并修改 getAndRemove(Query) 查询并删除 getOne(Query) 查询并返回对象 list(Query) 查询并返回集合 page(Query, Pageable) 查询并返回分页 aggregate(Criteria) 分组聚合查询 ``` 举例: ``` public interface TestMapper extends JLMongoMapper { } ``` ``` @Service public class TestService { @Autowired private TestMapper testMapper; public void test() { //分页查询 PageRequest pageRequest = PageRequest.of(0, 10); PageImpl page = testMapper.page(new Query(), pageRequest); } } ``` 分组查询举例: ``` @Service public class TestService { @Autowired private StJcysVMongoMapper stJcysVMongoMapper; public void test(StJcysVMongo stJcysVMongo, int page, int size) { //查询分页 Page pages = stJcysVMongoMapper.aggregate(new Criteria()) //根据stcd属性分组 .group(StJcysVMongo::getStcd, StJcysVMongo::getCount) //分组数量统计到count字段 //根据stcd属性正序排序 .asc(StJcysVMongo::getStcd) .select() .page(page, size); //查询集合 List list = stJcysVMongoMapper.aggregate(new Criteria()) .group(StJcysVMongo::getStcd) .desc(StJcysVMongo::getStcd) .select() .list(); //统计行数 long count = stJcysVMongoMapper.aggregate(new Criteria()) .group(StJcysVMongo::getStcd) .select() .count(); //更多查看源码... } } ``` #### 2. JLQuery 搭配[JLMongoMapper](https://gitee.com/laoshirenggo/jl/blob/master/README.md#1-jlmongomapper)使用。使用实体匹配条件生成Query对象,类似mybatis-plus的实体查询构造器,会使用mybatis-plus即可。 方法列表: ``` 注:以下所有方法均为对象属性的默认条件匹配方式,即入参对象属性不为空并且未指定特定的匹配方式才会拼入查询条件 getQuery() 默认使用eq eqQuery() 默认使用eq ltQuery() 默认使用lt gtQuery() 默认使用gt leQuery() 默认使用le geQuery() 默认使用ge likeQuery() 默认使用like neQuery() 默认使用ne inQuery() 默认使用in notInQuery() 默认使用notIn ``` 举例1: ```         public class Test { public static void main(String[] args) { MongoEntity mongoEntity = new MongoEntity(); //默认使用eq进行匹配 id=匹配,name模糊匹配,age<=匹配 Query eqQuery = new JLQuery().getQuery(mongoEntity) //name使用 模糊 匹配 .like(MongoEntity::getName) //age使用 <= 匹配 .le(MongoEntity::getAge) .build(); //默认使用like进行匹配 name模糊匹配,id=匹配,age<=匹配 Query likeQuery = new JLQuery().likeQuery(mongoEntity) //id使用 = 匹配 .eq(MongoEntity::getId) //age使用 <= 匹配 .le(MongoEntity::getAge) .build(); ...... } /** * mongo实体类 */ @Data @Accessors(chain = true) public static class MongoEntity { private String id; private String name; private Integer age; } } ``` 举例2: ```  public class Test { public static void main(String[] args) { MongoEntityDto mongoEntityDto = new MongoEntityDto(); //默认使用eq进行匹配 id=匹配,name模糊匹配,age<=匹配 Query eqQuery = new JLQuery().getQuery(mongoEntityDto, MongoEntity.class) //name使用 模糊 匹配 .like(MongoEntity::getName) //age使用 <= 匹配 .le(MongoEntity::getAge) .build(); //默认使用like进行匹配 name模糊匹配,id=匹配,age<=匹配 Query likeQuery = new JLQuery().likeQuery(mongoEntityDto, MongoEntity.class) //id使用 = 匹配 .eq(MongoEntity::getId) //age使用 <= 匹配 .le(MongoEntity::getAge) .build(); ...... } /** * 入参类 */ @Data @Accessors(chain = true) public static class MongoEntityDto { private String id; private String name; private Integer age; } /** * mongo实体类 */ @Data @Accessors(chain = true) public static class MongoEntity { private String id; private String name; private Integer age; } } ``` ### mybatis-plus #### 1. JLJoin mybatis-plus没有对象联表查询,写xml繁琐且硬编码不便于维护,特别是在表列变动频繁的情况下,所以扩展了一下对象连表查询,能满足基本需求,支持常用的count,object,list,page查询: 注:实体类需要有@TableName注解,实体类属性需要驼峰命名,表名及列名需要下横线命名 举例: ``` public class Test { @Autowired private JLJoin jlJoin; public void test() { //等同于sql:select * from user u join user_info ui on(u.id = ui.user_id) join burse b on(u.id = b.user_id) where u.age >= 18 List list = jlJoin.from(User.class).inner(UserInfo.class).inner(Burse.class) .on(User::getId, UserInfo::getUserId) .on(User::getId, Burse::getUserId) .ge(User::getAge, 18) .list(UserAndInfoAndBurse.class); } /** * 用户实体 */ @Data @TableName("user") public static class User { private Integer id; private String name; private Integer age; } /** * 用户详情 */ @Data @TableName("user_info") public static class UserInfo { private Integer id; private Integer userId; private String address; } /** * 钱包实体 */ @Data @TableName("burse") public static class Burse { private Integer id; private Integer userId; private BigDecimal money; } /** * 查询返回结果类 */ @Data public static class UserAndInfoAndBurse extends User { //外键对象属性名需与@TableName的名称转驼峰后相同 private Burse burse; //如不相同,则可以使用@JLTableName注解指定@TableName的名称 @JLTableName("user_info") private UserInfo info; } } ``` #### 2. JLSyncUpdate 同步阻塞更新数值,手写xml硬编码繁琐且不便于维护,用plus先查再修改则会遇到数值被其他事物更改的窘境,所以增加了该类,实现同步阻塞更新数值表列: 注:实体类需要有@TableName注解,实体类属性需要驼峰命名,表名及列名需要下横线命名 举例: ``` public class Test { @Autowired private JLSyncUpdate jlSyncUpdate; public void test() { //等同于sql:update burse set money = money + 1,integral = integral - 1 where user_id = 1 and money + 1 > 0 and integral - 1 > 0 boolean exec = jlSyncUpdate.update(Burse.class) .set(Burse::getMoney, new BigDecimal(1)) .set(Burse::getIntegral, new BigDecimal(-1)) .eq(Burse::getUserId, 1) .exec(); } /** * 钱包实体 */ @Data @TableName("burse") public static class Burse { private Integer id; private Integer userId; private BigDecimal money; private BigDecimal integral; } } ``` #### 3. JLWrapper 使用实体匹配条件生成LambdaQueryWrapper对象,区别于plus自带的实体查询匹配,plus自带的不支持自定义入参对象,且对实体对象注解侵入,而且不灵活,无法做到既给了构造对象又自定义规则,所以封装了该类,使用方法和设计思想同上方mongo的JLQuery几乎一致,参考上方[JQuery](https://gitee.com/laoshirenggo/jl/blob/master/README.md#2-jlquery)使用即可。 方法列表: ``` 注:以下所有方法均为对象属性的默认条件匹配方式,即入参对象属性不为空并且未指定特定的匹配方式才会拼入查询条件 queryWrapper() 默认使用eq eqWrapper() 默认使用eq ltWrapper() 默认使用lt gtWrapper() 默认使用gt leWrapper() 默认使用le geWrapper() 默认使用ge likeWrapper() 默认使用like neWrapper() 默认使用ne inWrapper() 默认使用in notInWrapper() 默认使用notIn ``` #### 4. JLMybatisGenerator mybatis代码生成器,封装了过程,只需要简单操作一个对象即可生成代码,使用更方便,例子: ``` public class Test { public static void main(String[] args) { //仅支持mysql JLMybatisGenerator.Generator.builder() .author("lijin") //作者 .username("root") //账号 .password("root") //密码 .database("zhsw") //连接库 .ip("127.0.0.1") //ip //.clasz(Test.class) //文件生成的位置(不能跨模块),默认为运行类(main方法)的同级 //.tablePrefix("t_") //剔除表名前缀 //.baseEntity(DemoBaseEntity.class) //实体父类 //.baseEntityPropertys(new String[]{"add_time"}) //写于实体父类的表列,注意:"是表列名" //.baseController(DemoBaseController.class) //控制器父类 .build() .create(); //开始生成代码 } } ``` ### elasticsearch #### 1. JLElasticsearchServiceImpl 搭配[JLEsQuery](https://gitee.com/laoshirenggo/jl#2-jlesquery)使用。基于ElasticsearchRepository的二次封装,所做的事情和mybatisplus的ServiceImpl是一模一样的,所以开发者还是需要新建一个mapper继承ElasticsearchRepository,然后把这个mapper交给该类,实现更少的代码、更面向对象、提供更便捷的es操作。 方法列表: ``` existsById(String) id查询是否存在 getById(String) id查询 count(NativeSearchQuery) 查询数量 get(NativeSearchQuery) 查询对象 list(NativeSearchQuery) 查询集合 page(NativeSearchQuery, Pageable) 查询分页 save(E) 保存 save(Iterable) 批量保存 remove(String) id删除 remove(Iterable) id批量删除 remove() 删除全部 ``` 使用例子: 创建一个es实体 ``` @Data @Document(indexName = "test") public class Test { @Id private String id; private String name; private Integer age; } ``` 创建mapper ``` public interface TestMapper extends ElasticsearchRepository { } ``` 创建实现: ``` @RestController public class TestService extends JLElasticsearchServiceImpl { /** * 分页查询 */ @GetMapping("page") public Page page(Test test) { //这是一个工具类,用以快速获取查询条件对象,下方会有详细说明 NativeSearchQuery nativeSearchQuery = new JLEsQuery().geQuery(test).getNativeSearchQuery(); //分页查询 Page page = page(nativeSearchQuery, PageRequest.of(0, 10)); return page; } /** * 当api不够用时,也可用原mapper */ @GetMapping("findAll") public void findAll() { //原调用mapper的方法 Iterable all = baseMapper.findAll(); } } ``` #### 2. JLEsQuery 搭配[JLElasticsearchServiceImpl](https://gitee.com/laoshirenggo/jl#1-jlelasticsearchserviceimpl)使用。使用实体匹配条件生成NativeSearchQuery对象,使用方法和设计思想同上方mongo的JLQuery几乎一致,参考上方[JQuery](https://gitee.com/laoshirenggo/jl/blob/master/README.md#2-jlquery)使用即可。 方法列表: ``` 注:以下所有方法均为对象属性的默认条件匹配方式,即入参对象属性不为空并且未指定特定的匹配方式才会拼入查询条件 getQuery() 默认使用eq eqQuery() 默认使用eq ltQuery() 默认使用lt gtQuery() 默认使用gt leQuery() 默认使用le geQuery() 默认使用ge likeQuery() 默认使用like neQuery() 默认使用ne inQuery() 默认使用in notInQuery() 默认使用notIn ``` #### 3. JLEsSearchSql 使用sql查询es工具类,以控制台输入sql的方式查询es,就像连接ssh操作linux一般,较为方便,举例: ``` public static void main(String[] args) { JLEsSearchSql.connect("127.0.0.1", 9200); } ``` ### redis #### 1. JLRedisTemplate RedisTemplate的二次封装,提供更便捷的操作,注入JLRedisTemplate根据注释使用即可。 #### 2. @JLRedisson 注解版redis分布式锁,以方法为维度获取锁,没获取到锁则抛出JLRedissonError异常类,自行捕捉实现业务即可,举例: ``` public class Test { @JLRedisson(value = "test", type = JLRedisson.TRY_LOCK, message = "获取锁失败") public void test() { System.out.println("成功执行方法"); } } ``` ### security #### 1. @JLDecode 私钥解密注解,请求入参自动用私钥aes或rsa解密数据而开发不用做任何解密操作: 注:启动类上需加上@JLEnableSecurity,请求方式必须为body入参,即入参对象必须有@RequestBody修饰,yml需配置aes密钥或rsa私钥公钥 #### 2. @JLEncode 私钥加密注解,返参自动用私钥aes或rsa加密数据而开发不用做任何加密操作: 注:启动类上需加上@JLEnableSecurity,返参结果必须为json,yml需配置aes密钥或rsa私钥公钥 yml配置举例: ``` encrypt: #是否开启加解密 默认true open: true #是否将加解密日志输出到控制台 默认false showLog: false #aes密钥 aes: #获取密钥可使用类 JLAES key: GLfx0TH5zhQpnHQnNtrW1w== #rsa公钥私钥 #rsa: #获取密钥可使用类 JLRSA #publicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCv370hnpEizK5IUtTxCQVFsEPz2ZFuys4x6zR/2mpS/MooLecSyqsiF8kLUs+ZLqnZrtTRzuMZ5TowP6QeYqsWiAI9tv7x/Pahxi7BEYx/CQx3T7RZRZsnEuprxbmyCkW5WWjrF7NitMSGMSq8fi8DANUeV0aLV8lVz4b3LhCPqwIDAQAB #privateKey: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOz+7RPUvKUGfWnsWN+p6N27uzkLOuQyAiaMhxVN+IpMR6A+BHzGPSM+je/xLdoJLlaqoZXLhmtyH9EjQlIOcrPlP933OuTGwNDa9rXxjRI9r1nsfhorvVxmWk8Lq2EEP9pSsj4c83ilcoYIMB4dzPf/XN2DEhozNbth+sPZfIE/AgMBAAECgYBbqtr+camp1xHJV66kjG7S3Rs0nEBiJWmpiW9ycR8yNwD5XSOVM4RQTpDN/yZyEF0JDqTDcN6ETrc5yH6NiKMabXHtn4OD7Uciu46c3VOAAT8x3KAWqldMh0XwPWmepu9B1ajtWzEuytFG/aTscWrBd2zf3EBStS7ctLpLZmqmOQJBAP1EA6a3q+Jl2uoQlZSpmqQlfAPtn2gYHLpnbY4mev6TdbHnq0id4dZ5zQIASQgzGDuaG3lyzNWg063+H+nCaqsCQQDvjfHKTirqxH7OViu2+ms9ufJ4zI5AQfTcywE1MP7Hg861gb1BEzqGDlOUkEwbo2YMrBfG6AP7IXRJGU9pG0O9AkBxhshgNhrNTDz6CN8UGYahJ9BUbnKzFYPjJrOcMbGWZgEu8xr7XRI7srNrvzb9fvHQ3b6NDSG2bPYWG0Cw5x4rAkEA0rFMjTuNAalLQl2F20yLD+JBAcAgCSI5pAwkhs0N+RrTrs5qTxcDbS6iklMLrW9cbR7bVsVv4uu8pCJPtskVHQJBAIg0NHL72U8fbRfh2Oo4hYMk2NP1KcH/LscehaNrrIOF71dX5aOYwalX2Q0rEBvawlJptv33xbDP+M8lTB+ecHM= ``` #### 3. @Check 简化版的@validated,一个注解实现99%项目所需的校验功能,基本思路和使用同@validated,只是更简单好用,主要功能如下: 1. 一个注解实现校验,自动根据数据类型判断而不需要和@validated一样由开发人为去根据类型使用不同注解。 2. 自动封装校验异常返回值,而不用手动一个个属性指定,默认属性名,可自定义属性名称。 3. 支持 最大以及最小(数值属性=值的大小,字符串属性=字符的长度,List属性=集合的长度)、正则表达式(字符串和数值属性可用)、分组(同组才会被校验)。 4. 支持在类上和属性注解@Check,如类上注解则相当于给该类所有属性都加上了同类上注解的@Check(如属性也加了@Check,则优先使用它自己的)。 5. 可选 是否可为空 ,表示如果为空则不校验,如果不为空则校验。 6. 可选 是否有效,表示有效才会校验。 7. 校验异常结果可自行捕捉JLCheckError异常类,获取message属性返回给前端即可。 举例: ``` public class OrderServiceimpl { @PostMapping("test") public void test(@RequestBody @Check(min = 1, max = 5) List user) { //指定user集合最小长度=1 最大长度=5 } @Data @Check//指定全局非空校验 public static class User { //指定数值最小=1 最大=10000 @Check(min = 1, max = 10000) private BigDecimal id; //指定字符最小长度=1 最大长度=20 @Check(min = 1, max = 20) private String name; //自定义属性名称,校验不通过会使用该名称抛出异常 @Check("订单集合") private List order; //指定集合最小长度=1 最大长度=10 并自定义属性名称 @Check(min = 1, max = 10, value = "测试名称") private List ints; private Boolean wq; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime time; } @Data @Check//指定全局非空校验 public static class Order { private BigDecimal id; private String no; private Money money; } @Data @Check//指定全局非空校验 public static class Money { private BigDecimal id; private String cal; } } ``` 以上为常规使用例子,如下将使用分组功能: ``` public class OrderServiceimpl { @PostMapping("test") public void test(@RequestBody @Check(groups = {TestGroup.class}) List user) { //指定校验只被TestGroup分组修饰的属性 } @Data @Check public static class User { @Check(groups = {TestGroup.class}) private BigDecimal id; //该属性就不参与test接口的校验 private String name; @Check(groups = {TestGroup.class}) private List order; } @Data @Check public static class Order { @Check(groups = {TestGroup.class}) private BigDecimal id; //该属性就不参与test接口的校验 private String no; @Check(groups = {TestGroup.class}) private Money money; } @Data @Check(groups = {TestGroup.class}) public static class Money { private BigDecimal id; private String cal; } /** * 分组接口 */ interface TestGroup { } } ``` #### 3. @JLFilter 注解版过滤器,一个注解生成过滤器,支持传入基本参数,你只需保证注解参数能强转为形参即可,可选择返回布尔值来实现请求是否继续,如返回false则中断请求并抛出JLFilterError异常。 举例: ``` @Service @AllArgsConstructor public class Test { private HttpServletRequest request; /** * 过滤器1 error为中断执行时抛出异常的值, order为执行顺序 */ @JLFilter(error = "test过滤器异常", order = 2) public boolean test() { System.out.println("请求url:" + request.getRequestURI()); return true; } /** * 过滤器2 excludes为指定 /login /register不过滤 */ @JLFilter(excludes = {"/login", "/register"}) public boolean demo() { System.out.println("请求url:" + request.getRequestURI()); return true; } /** * 过滤器3 values为传入了方法参数,基本数据类型会自动转换 */ @JLFilter(values = {"张三", "18"}) public boolean uat(String name, Integer age) { System.out.println("请求url:" + request.getRequestURI()); return true; } } ``` #### 4. @JLInterceptor 注解版拦截器,使用和设计思路同@JLFilter,只是多了之前和之后执行时可配置,自行捕捉异常换为JLInrerceptorError。 ### tools #### 1. JMap 继承自Map,有Map的所有功能,增强封装了常用方法,让代码更优雅,支持功能如下: 1. 常用数据类型的get方法。 2. lambda方法引用的key。 3. 链式调用。 4. 转换为list。 5. 转换为实体。 ``` public class Test { public static void main(String[] args) { JMap map = new JHashMap() .set(Demo::getZhangsan, "张三") .set("age", 18); //获取string String zhangsan = map.getString(Demo::getZhangsan); //获取int Integer age = map.getInt("age"); //key转换为list JList key = map.toList().key(); //value转换为list JList value = map.toList().value(); //转换为bean Demo demo = map.toBean(Demo.class); //更多参考源码注释... } @Data @Accessors(chain = true) public static class Demo { private String zhangsan; private Integer age; } } ``` ##### 1. JHashMap 继承自HashMap,JMap的实现类之一。 ##### 2. JConcurrentHashMap 继承自ConcurrentHashMap,JMap的实现类之一。 #### 2. JList 继承自List,有List的所有方法,增强封装了常用方法的lambda操作,让代码更优雅,支持功能如下: 1. 链式调用。 2. 转换为map。 3. 数据去重。 4. 数据排序。 5. 获取某个属性集合。 6. 集合查询。 7. 随机洗牌。 8. 根据值获取下标。 9. 按指定长度切割为N个集合 10. 求差集。 11. 求交集。 ``` public class Test { public static void main(String[] args) { JList jlist = new JArrayList() .set(new Demo().setAge(18).setName("张三")) .set(new Demo().setAge(20).setName("李四")); //age属性为key转换为map,重复分组 JMap> group = jlist.toMap(Demo::getAge).group(); //age属性为key转换为map,重复覆盖 JMap cover = jlist.toMap(Demo::getAge).cover(); //根据age属性去重 JList comparing = jlist.comparing(Demo::getAge); //获取age属性集合 JList property = jlist.getProperty(Demo::getAge); //查询age=18 && name=张三 的数据 返回集合 JList zhangsan = jlist.filter() .eq(Demo::getAge, 18) .eq(Demo::getName, "张三") .list(); //查询name=李四 的数据 返回对象 Demo lisi = jlist.filter() .eq(Demo::getName, "李四") .object(); //随机洗牌 JList shuffle = jlist.shuffle(); //按长度2分隔为多个小集合 JList> partition = jlist.partition(2); //根据值获取下标 int index = list.getIndex(new Demo().setAge(18).setName("张三")); JList list2 = new JArrayList() .set(new Demo().setAge(18).setName("张三")); //差集 JList diff = jlist.diff(list2); //交集 JList section = jlist.section(list2); //更多参考源码注释... } @Data @Accessors(chain = true) public static class Demo { private String name; private Integer age; } } ``` ##### 1. JArrayList 继承自ArrayList,JList的实现类。 #### 3. JLSet 集合操作工具类,封装了常用的集合操作,链式调用,让操作更集中,也是JMap以及JList的基础工具类,使用方法参考JList和JMap。 #### 4. @JLRunner 项目启动完成后执行方法注解,封装了CommandLineRunner,实现注解自启方法,支持传入基本参数,你只需保证注解参数能强转为形参即可,举例: ``` @Service public class Test { //name=jl age=18 @JLRunner(value = {"jl", "18"}) public void test(String name, Integer age) { System.out.println(name + "--" + age); } } ``` #### 5. JLIo io工具类,api列表如下: ``` inputStream(String) 字节输入流,支持本地文件和网络资源 inputStream(File) 字节输入流 get() 获取字节输入流 content() 获取文件内容 outputStream(String) 字节输出流 outputStream(String, boolean append) 字节输出流 outputStream(File) 字节输出流 outputStream(String, boolean append) 字节输出流 get() 获取字节输出流 save(String) 保存文件 save(InputStream) 保存文件 ``` #### 6. JLLambda lambda解析工具,可实现根据方法引用获取class、方法名、属性名,避免硬编码!举例: ``` public class Test { public static void main(String[] args) { //获取class com.jl.Test$Demo Class claszz = JLLambda.getClass(Demo::getId); //获取属性名 id String property = JLLambda.getProperty(Demo::getId); //获取方法名 getName String getMethod = JLLambda.getMethod(Demo::getName); } @Data public static class Demo { private Integer id; private String name; } } ``` #### 7. JLQRCode 二维码生成工具,支持方形和圆形两种,支持网络资源图片设置为背景图: 效果图: ![图片备注](https://gitee.com/laoshirenggo/jl/raw/master/1.jpg) #### 8. JLReflect 反射工具类,该项目所有反射都是基于这个类做的。 #### 9. JLTuple 元祖,顾名思义就是一组数据,当有多个值且数据类型不同时,使用Map并不能准确的表达值的数据类型,元祖就是为了解决这一问题,这里一共提供了五维元祖,足够满足大多数需求,举例: ``` public class Test { public static void main(String[] args) { //二维元祖,同时放入两种数据类型 JLTuple.Tuple2 tuple2 = new JLTuple.Tuple2<>(200, "操作成功"); Integer v21 = tuple2.getV1(); String v22 = tuple2.getV2(); //三维元祖,同时放入三种数据类型 JLTuple.Tuple3 tuple3 = new JLTuple.Tuple3<>(200, "操作成功", new Demo().setId(1).setName("张三")); Integer v31 = tuple3.getV1(); String v32 = tuple3.getV2(); Demo v33 = tuple3.getV3(); ... } @Data @Accessors(chain = true) public static class Demo { private Integer id; private String name; } } ``` #### 10. JLEmpty 判空的封装,让任意数据类型基本判断只需使用一个方法即可,写代码更无脑,举例: ``` public class Test { public static void main(String[] args) { Object obj = null; String str = ""; Double number = 0; //判空 boolean check = JLEmpty.check(obj); //指定字符串 最小长度=1 最大长度=10 boolean check2 = JLEmpty.check(str, 1, 10); //指定字符串 正则匹配 boolean check3 = JLEmpty.check(str, "[a-zA-Z0-9_]+@[a-zA-Z0-9_]+(\\.[a-zA-Z0-9]+)+"); //指定数值 最小=1 最大=10 boolean check4 = JLEmpty.check(number, 1, 10); } } ``` #### 11. JLSpringBean spring bean动态管理工具,实现项目运行中动态增删bean,举例: ``` public class Test { @Autowired private JLSpringBean jlSpringBean; public void test() { //对象注册bean DemoBean demoBean = new DemoBean() .setId(1) .setName("张三"); DemoBean demoBean1 = jlSpringBean.registerBean(demoBean); //map注册bean Map map = new HashMap<>(); map.put("id", 1); map.put("name", "张三"); DemoBean demoBean2 = jlSpringBean.registerBean("demoBean2", DemoBean.class, map); //修改bean 只需更改对应bean对象的值即可 demoBean1.setName("李四"); //删除bean jlSpringBean.removeBean("demoBean2"); } @Data @Accessors(chain = true) public static class DemoBean { private int id; private String name; } } ``` #### 12. JLPlainClassSpringBean 普通类获取spring bean工具类,可以根据class或bean名称获取bean,无需手动设置ApplicationContext,且是静态方法形式,使用较传统方式更方便,举例: ``` public void test() { //class获取 TestServiceImpl testServiceImpl1 = JLPlainClassSpringBean.getBean(TestServiceImpl.class); //bean名称获取 TestServiceImpl testServiceImpl2 = JLPlainClassSpringBean.getBean("testServiceImpl", TestServiceImpl.class); } ``` #### 13. JLBean 对象工具类,提供bean拷贝、生成树等方法。 ##### 1. copy()方法 对象拷贝 纯反射实现的bean拷贝,性能是BeanUtils的10-20倍之上,也解决BeanCopier无法读取有返回值的set方法而导致无法使用@Accessors(chain = true)编程,并且实现了常用数据类型的转换,所以无需要求源bean和目标bean的属性数据类型一致,只要能保证数据类型可以强转即可,举例: ``` public static void main(String[] args) { User user = new User() .setId("1") .setName("test") .setMoney(99.99) .setTime(LocalDateTime.now()); //对象拷贝 UserVo1 userVo1 = JLBean.copy(UserVo1.class, user); //对象拷贝 属性数据类型不一样,但能保证数据类型可以转换,同样可以拷贝 UserVo2 userVo2 = JLBean.copy(UserVo2.class, user); //集合拷贝 List userVo2s = JLBean.copy(UserVo2.class, Arrays.asList(user)); } @Data @Accessors(chain = true) public static class User { private String id; private String name; private Double money; private LocalDateTime time; } @Data @Accessors(chain = true) public static class UserVo1 extends User { } @Data @Accessors(chain = true) public static class UserVo2 { private Integer id; private String name; private BigDecimal money; private String time; } ``` ##### 2. tree()方法 生成树 递归生成树,必须要知道顶级对象,举例: ``` public static void main(String[] args) { //生成测试数据 List datas = new ArrayList<>(); datas.add(new Entity().setId(1).setName("顶级1")); datas.add(new Entity().setId(2).setName("顶级2")); datas.add(new Entity().setId(3).setName("子级1").setPid(1)); datas.add(new Entity().setId(4).setName("子级2").setPid(3)); datas.add(new Entity().setId(5).setName("子级3").setPid(1)); //获取顶级菜单(pid == null) List list = datas.stream().filter(entity -> entity.getPid() == null).collect(Collectors.toList()); for (Entity top : list) { //获取子级集合 参数依次为:最顶层父级对象, 全部数据集合, 父级关联子级属性名, 子级关联父级属性名, 存放子级集合属性名 List tree = JLBean.tree(top, datas, Entity::getId, Entity::getPid, Entity::getSonEntitys); //存入下级集合 top.setSonEntitys(tree); } //转json,方便观察结果 JSONArray array = JSONUtil.parseArray(list); //输出[{"name":"顶级1","id":1,"sonEntitys":[{"pid":1,"name":"子级1","id":3,"sonEntitys":[{"pid":3,"name":"子级2","id":4,"sonEntitys":[]}]},{"pid":1,"name":"子级3","id":5,"sonEntitys":[]}]},{"name":"顶级2","id":2,"sonEntitys":[]}] System.out.println(array.toString()); } @Data @Accessors(chain = true) public static class Entity { //id private Integer id; private String name; //上级id 为null则表示顶级,规则是你自己定义 private Integer pid; //下级集合 private List sonEntitys; } ``` #### 14. JLThread 线程池,只需配置yml即可,用法同spring常规用法,额外提供了多线程执行结果集合并方法,配置bootstrap.yml举例: ``` jl: thread: param: - core: 10 #核心线程数 max: 100 #最大线程数 queue: 1000 #队列大小 name: test #线程池名称 - core: 10 max: 100 queue: 1000 name: demo ``` java代码举例: ``` @RestController public class Test { @Autowired private JLThread jlThread; /** * 使用test线程池 */ @Async("test") @GetMapping("test") public void test() { System.out.println("test"); } /** * 使用demo线程池 执行结果集合并 */ @GetMapping("demo") public void demo() { List list = new ArrayList<>(); list.add(new Object[]{"张三"}); list.add(new Object[]{"李四"}); list.add(new Object[]{"王五"}); //根据参数list长度决定使用线程数,参数依次为: 线程池名称, springbean, 执行方法名, 返参泛型, 参数集合 List names = jlThread.exec("demo", this.getClass(), "getName", String.class, list); //输出[张三,李四,王五] 耗时5s System.out.println(names); } public String getName(String name) { try { //暂停线程5s,测试多线程效果 Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } return name; } } ``` #### 15. JLInvocationHandler 接口代理抽象父类,封装抽象了接口代理相关的操作,继承使用即可,简单明了实现spring接口代理,可实现功能如:mybatis-plus的BaseMapper以及我的[JLMongoMapper](https://gitee.com/laoshirenggo/jl#1-jlmongomapper)等等,举例: ``` /** * 测试bean,主要演示代理实现类获取spring bean */ @Service public class TestBean { /** * 输出泛型class名称 */ public void test(Class clazz) { System.out.println(clazz.getName()); } } ``` 下面开始编写代理类: ``` /** * 代理接口,用于给其他接口继承 * @param */ public interface DemoService { void test(); } ``` ``` /** * 代理接口实现 * @JLProxy注解表示声明该类为代理类 * 单个泛型继承JLInvocationHandler, 两个泛型继承JLInvocationHandler2, 三个泛型继承JLInvocationHandler3 * * @param */ @JLProxy public class DemoServiceImpl extends JLInvocationHandler implements DemoService { public DemoServiceImpl(Class interfaceType) { super(interfaceType); } /** * 因该代理类不受spring管理,所以使用JLInvocationHandler提供的getBean()手动获取用于演示的TestBean * @return */ public TestBean testBean() { TestBean testBean = getBean(TestBean.class); return testBean; } /** * 实现接口方法,调用测试bean,输出泛型class名称 */ @Override public void test() { //classa为JLInvocationHandler父类提供的泛型实例,JLInvocationHandler2对应classa、classb,JLInvocationHandler3对应classa、classb、classc testBean().test(classa); } } ``` 此时代理类已经完成,下面编写测试类: ``` /** * 声明一个接口,并继承代理类,指定泛型String */ public interface TestService extends DemoService { } ``` ``` /** * 测试控制层 */ @RestController public class TestServiceImpl { @Autowired private TestService testService; @GetMapping("test") public void test() { //输出java.lang.String testService.test(); } } ``` ### springcloud #### 1. cloud架构搭建 主要整合抽象一些注解和配置文件,可实现快速本地搭建和一键切换注册中心,最简单的配置最快的速度搭建微服务框架,yml具体配置可引入包后查看代码补全,主要功能包括: 1. 服务注册发现 2. 配置中心 3. feign客户端、使用feign继承特性后controller重复注册解决、feign请求头拦截(解决feign不支持GET、PUT、DELETE等方法传对象,以及feign请求头参数传递) 4. hystrix断路器 5. gateway网关 6. 提供增强注解,包括:微服务注解整合、网关注解整合、feign客户端注解增强 ``` 默认配置: 1. 注册中心:连接127.0.0.1 1. eureka端口8858 2. consul端口8500,文件名=config/服务名/data.yaml 3. nacos端口8848,空间名=public,文件名=服务名.yaml 2. feign:超时5s 3. hystrix:超时5s,核心线程数30,队列数200,阈值200 ------------------------------------------------------------------------ nacos yml配置参考类JLNacosConfig eureka yml配置参考类JLEurekaConfig consul yml配置参考类JLConsulConfig feign yml配置参考类JLFeignConfig hystrix yml配置参考类JLHystrixConfig 以上参数可根据yml代码补全提示自行配置,如不配置则会以上方默认的参数启动 ``` 这里给出一个最简单的框架搭建例子: 1. 继承父依赖 ``` com.jl spring-cloud 1.0-SNAPSHOT ``` 2. 引入注册中心依赖,如nacos ``` com.jl nacos 1.0-SNAPSHOT ``` 3. 配置bootstrap.yml ``` spring: profiles: #引入内置文件 include: cloud jl: cloud: service: #服务名 name: demo ``` 4. 启动类加注解@JLCloudApplication ``` @SpringBootApplication @JLCloudApplication public class DemoApp { public static void main(String[] args) { SpringApplication.run(DemoApp.class); } } ``` 至此一个最简单的微服务就搭建完成了。 #### 2. feign请求头传递 项目中必然不可少请求头传递,当使用feign调用时,想传递请求头就显得比较繁琐,故封装该工具。 1. 配置bootstrap.yml ``` jl: cloud: feign: head: #传递的头,意思为当前请求体里面本身就有的一个或多个请求头,会原样传递到feign的被调用方 carr-heads: - test #请求头key - demo #设置的头,意思为设置一个或多个固定的请求头到feign的被调用方 set-heads: #请求头key: value - login-name: admin login-pwd: 123456 ``` 2. 配置FeignClient拦截器 ``` /** * feign加上拦截器 */ @FeignClient("test", configuration = JLFeignInterceptor.class) public class Test extends TestService { } ``` #### 3. gateway网关 1. 继承父依赖 ``` com.jl spring-cloud 1.0-SNAPSHOT ``` 2. 引入gateway和注册中心依赖,如nacos ``` com.jl gateway 1.0-SNAPSHOT com.jl nacos 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web ``` 3. 配置bootstrap.yml ``` jl: cloud: service: name: gateway #gateway: #开启服务发现自匹配,访问前缀为服务名,默认true #auto: true #也可以手动配置路由,注:auto需要为false #route: #list: #- service-name: demo #服务名 #path: demo #访问路径 #type: get,post #请求方式 #fall-back-url: fallBack #针对具体服务的服务降级请求 fallBack 是一个可访问的controller请求 #全局服务降级请求 fallBack 是一个可访问的controller请求 #fall-back-url: fallBack #配置服务降级超时时间 gateway需要手动配置路由才生效 #hystrix: # timeout: 2000 ``` 4. 启动类加注解@JLGatewayApplication ``` @SpringBootApplication @JLGatewayApplication public class GatewayApp { public static void main(String[] args) { SpringApplication.run(GatewayApp.class); } } ```