
public <T> void setCacheObject(final String key, final T value) { redisTemplate.opsForValue().set(key, value); } public <T> T getCacheObject(final String key) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); return operation.get(key); } 这个逻辑在调用 getCacheObject 时 返回对象会随机变成 com.alibaba.fastjson.JSONObject 或者 com.alibaba.fastjson2.JSONObject 导致外部调用时报类型强制转换异常
就是 ruoyi 脚手架 里面的 RedisService TokenService 里面调用 getLoginUser 就会报 java.lang.ClassCastException: com.alibaba.fastjson2.JSONObject cannot be cast to com.ruoyi.system.api.model.LoginUser
而且之前都正常 一段时间没运行 再跑就出现这个问题 定义是在 RedisConfig
RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); 全部都是 object 获取到就变 JSONObject 了 实在是没搞懂哪里有问题
]]>小弟我在这里先谢谢各位了
]]>@Override public int deleteByColorId(Long colorId,String userCode) { LambdaUpdateWrapper<ColorLibraryFile> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.set(ColorLibraryFile::getDeletedFlag,DeletedFlagEnum.DELETE.getCode()); updateWrapper.set(ColorLibraryFile::getDeletedBy,userCode); updateWrapper.set(ColorLibraryFile::getDeletedTime, new Date()); updateWrapper.eq(ColorLibraryFile::getColorId,colorId); updateWrapper.eq(ColorLibraryFile::getDeletedFlag,DeletedFlagEnum.NORMAL.getCode()); return colorLibraryFileDao.update(null,updateWrapper); } ]]>我在想,为了 AI 阅读方便,我们是不是应该将所有 crud 都纳到框架层,或者是放到独立的包。业务层只有存逻辑。提升信息密度。这样才对 AI 友好,各位觉得呢,Java 是不是需要这样子的框架。
我个人最近好久没做 Java 了,不喜轻喷。
]]> } catch (Exception e) { log.error(e.getMessage(), e); return RestResult.fail("系统异常"); } 日志文件中显示
2025-11-07 09:43:23.128 [http-nio-20001-exec-934] ERROR StoreOrderServiceImpl:557 - null java.lang.NullPointerException: null 然后就结束了, 最近出了好多这种问题. 打印空指针,但是完全没有栈相关信息
]]>问了 AI ,可以用下面的方法,但感觉还是太麻烦了,不够简单优雅
public class PooledRestClient { private static final CloseableHttpClient httpClient; static { PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(100); cm.setDefaultMaxPerRoute(20); // 可选:设置空闲连接清理 httpClient = HttpClients.custom() .setConnectionManager(cm) .evictIdleConnections(60, TimeUnit.SECONDS) .build(); } // 核心方法:动态发起请求 public static ResponseEntity<String> exchange( String url, HttpMethod method, HttpEntity<?> requestEntity, Class<String> responseType, int connectTimeoutMs, int readTimeoutMs) { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory( httpClient); factory.setConnectTimeout(connectTimeoutMs); factory.setReadTimeout(readTimeoutMs); RestTemplate restTemplate = new RestTemplate(factory); return restTemplate.exchange(url, method, requestEntity, responseType); } } // 动态构造请求头 HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", "Bearer " + token); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<String> entity = new HttpEntity<>("{\"key\":\"value\"}", headers); // 发起带连接池的请求(每次 URL/Token/Body 都可不同) ResponseEntity<String> respOnse= PooledRestClient.exchange( "https://api.example.com/v1/data", HttpMethod.POST, entity, String.class, 3000, // connect timeout 10000 // read timeout ); ]]>我知道 java ee 是个规范(这个规范是书面文档而已,是个倡议?还是一些具体的 API 且内置的?) 然后 JavaEE 交给了 Eclipse 现在叫 JakartaEE 我看到有一些包名是 jakarta.* 所以应该是一些具体的 jdk 内置的包?或三方包?
spring 是个什么东西?是类似 php 的 laravel ,python 的 fastapi ,go 的 gin ,rust 的 tokio 吗?只是在某个领域的事实标准,只是大多数人用,但不强制?
netty 又是个啥?文档说同等功能的还有 undertow 、tomcat-embed ,也只是基于内置 NIO 实现的“实时标准”框架吗?
]]>然后现在有一个任务是用浏览器打开指定网址,返回网页源代码,我就打算把这个服务器做成代理服务器。
本来是计划每个 IP 做一个代理服务器,代理服务器根据入口 IP 用对应的 IP 连接目标服务器。
开启每个浏览器的时候设置代理地址,比如 1.2.3.4:8639, 1.2.3.5:8639 这样 。
然后发现多开浏览器非常吃性能,要充分利用所有的公网 IP 得开几十个浏览器,这时候已经卡到动不了了,肯定不行。
所以我就想开 4 个浏览器,每个浏览器设置一个代理,然后通过接口去切换代理后端的出口。
代理服务器是用 netty 写的,逻辑改成了绑定不同端口,然后通过接口指定端口号和出口 IP ,来切换不同端口对应代理的出口 IP 。
其实就是存了一个 Map<Integer, String>,调接口修改这个 map ,netty 代理服务器连接目标服务器的时候使用出口地址去连接。

现在问题在于,调用接口切换出口 IP 后,日志显示已经使用新的出口 IP 了,但是访问查询 IP 的网站,还是使用之前的 IP ,好像要等一段时间才生效,这是什么问题,求各位大佬指教
@Log4j2 public class ProxyFrontendHandler extends SimpleChannelInboundHandler<FullHttpRequest> { private final AddressFunction addressFunction; public ProxyFrontendHandler(AddressFunction addressFunction) { this.addressFunction = addressFunction; } @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) { if (HttpMethod.CONNECT.equals(req.method())) { handleConnectRequest(ctx, req); return; } handleHttpRequest(ctx, req); } private void handleConnectRequest(ChannelHandlerContext ctx, FullHttpRequest req) { List<String> split = StrUtil.split(req.uri(), ":"); String host = CollUtil.getFirst(split); int port = Convert.toInt(CollUtil.get(split, 1), 443); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(ctx.channel().eventLoop()) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast(new RelayHandler(ctx.channel())); } }); ChannelFuture connectFuture; InetSocketAddress remoteAddress = new InetSocketAddress(host, port); InetSocketAddress sourceAddress = addressFunction.apply(ctx); if (sourceAddress != null) { log.info("Using outbound: {} | host: {}", sourceAddress, remoteAddress.getHostString()); cOnnectFuture= bootstrap.connect(remoteAddress, sourceAddress); } else { cOnnectFuture= bootstrap.connect(remoteAddress); } connectFuture.addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { Channel outboundChannel = future.channel(); DefaultFullHttpResponse respOnse= new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK ); response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); ctx.writeAndFlush(response).addListener((ChannelFutureListener) f -> { try { ctx.pipeline().remove(HttpServerCodec.class); ctx.pipeline().remove(HttpObjectAggregator.class); ctx.pipeline().addLast(new RelayHandler(outboundChannel)); } catch (Exception ignored) { } }); } else { sendErrorResponse(ctx, "无法连接到目标服务器"); closeOnFlush(ctx.channel()); } }); } private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { String host = req.headers().get(HttpHeaderNames.HOST); if (host == null) { sendErrorResponse(ctx, "缺少 Host 头"); closeOnFlush(ctx.channel()); return; } String[] hostParts = host.split(":"); String targetHost = hostParts[0]; int targetPort = hostParts.length > 1 ? Integer.parseInt(hostParts[1]) : 80; // 修改请求 URI 为绝对路径 req.setUri(req.uri().replace("http://" + host, "")); req.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); // 复制请求以避免在异步操作期间被释放 FullHttpRequest copiedReq = req.copy(); // 创建到目标服务器的连接 Bootstrap bootstrap = new Bootstrap(); bootstrap.group(ctx.channel().eventLoop()) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast(new HttpClientCodec()); ch.pipeline().addLast(new HttpObjectAggregator(1024 * 1024)); // 增加到 1MB ch.pipeline().addLast(new RelayHandler(ctx.channel())); } }); ChannelFuture connectFuture; InetSocketAddress remoteAddress = new InetSocketAddress(targetHost, targetPort); InetSocketAddress sourceAddress = addressFunction.apply(ctx); if (sourceAddress != null) { log.info("Using outbound: {} | host: {}", sourceAddress, remoteAddress.getHostString()); cOnnectFuture= bootstrap.connect(remoteAddress, sourceAddress); } else { cOnnectFuture= bootstrap.connect(remoteAddress); } connectFuture.addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { future.channel().writeAndFlush(copiedReq); } else { closeOnFlush(ctx.channel()); } if (copiedReq.refCnt() != 0) { copiedReq.release(); } }); } private void sendErrorResponse(ChannelHandlerContext ctx, String message) { FullHttpResponse respOnse= new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_GATEWAY, Unpooled.wrappedBuffer(message.getBytes()) ); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); ctx.writeAndFlush(response); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { if (cause instanceof SocketException) { closeOnFlush(ctx.channel()); return; } log.error(cause.getMessage()); closeOnFlush(ctx.channel()); } private void closeOnFlush(Channel ch) { if (ch.isActive()) { ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); } } } @Log4j2 public class RelayHandler extends ChannelInboundHandlerAdapter { private final Channel relayChannel; public RelayHandler(Channel relayChannel) { this.relayChannel = relayChannel; } @Override public void channelActive(ChannelHandlerContext ctx) { ctx.read(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { if (relayChannel.isActive()) { relayChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { ctx.read(); // 继续读取数据 } else { future.channel().close(); } }); } else { closeOnFlush(ctx.channel()); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { log.error(cause); closeOnFlush(ctx.channel()); } private void closeOnFlush(Channel ch) { if (ch.isActive()) { ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); } } } ]]>后端开发日常少不了排查慢 SQL 。平时我都是用 mysqldumpslow 或 pt-query-digest 来分析日志,但命令行看着太不直观。 于是我写了一个可视化小工具,可以把慢日志结果直接展示成表格,并支持参数排序:
c:访问次数
l:锁定时间
r:返回记录数
t:查询时间
al:平均锁定时间
ar:平均返回记录数
at:平均查询时间
👉 在线体验地址: http://tool.linger.host/tools/mysql-analysis
欢迎大家体验下,有什么优化建议也欢迎留言交流!
]]>c:访问次数
l:锁定时间
r:返回记录数
t:查询时间
al:平均锁定时间
ar:平均返回记录数
at:平均查询时间
👉 在线体验地址: http://tool.linger.host/tools/mysql-analysis
欢迎大家体验下,有什么优化建议也欢迎留言交流!
]]>插件地址:https://plugins.jetbrains.com/plugin/28686-bean-copy-helper
源码地址:https://github.com/Aresxue/bean-copy-helper
安装方式:在 Settings -> Plugins -> Marketplace 中输入 bean-copy-helper 检索即可找到该插件

这个插件一开始主要是写给自己用的,在阅读代码和排查 bug 的时候发现有很多项目使用了 Bean Copy ,Bean Copy 有时候确实很好用, 但是它也会带来一些困扰比如源对象通过 Bean Copy 赋值给目标对象以后通过 IDEA 的引用是找不到字段读取和写入( Bean Copy )的地方的,这个插件可以解决这个问题



可以看到我们可以对于Getter/Setter 方法或者字段使用属性复制范围查找,会认为一次 Bean Copy 是对源对象和目标对象同名且同类型的字段读取和写入(对于源对象来说是读取对于目标对象来说是写入)
为了避免对原有方法引用的污染所以采用类似Find Usages的方式,这样在跟踪某个字段的读取/写入时再也不会因为 Bean Copy 而被阻断了!!!
除此之外 Bean Copy 还经常被误用,这里也会对其做一些风险识别




最典型的场景就是同名字段类型不一致,实际上这个字段是没法被 Copy 过去的!!!
除此之外还集成了其它一些功能
可以预览源类和目标类属性的对比并生成相应注释

预览生成属性复制对应的Getter/Setter 代码


可以预览源类和目标类复制成功的属性并生成相应注释


而且使用中我常常发现字体时大时小有时候几乎无法观看,所以开放了字体大小自定义大家可自行修改

本插件永久免费,有需要的小伙伴自取。
]]>业务场景:
假设一个业务表,数据量在几千万级。 需要为这个表提供一个列表展示页,要求按创建时间倒序分页。 主要是权限问题导致查询慢: 1 、用户可以查看自己创建的数据。 2 、用户可以查看自己所属群组的数据。 3 、群组的权限是可继承的、层级的:如果一个用户属于某个上级群组,那么他自动拥有查看其所有下级、下下级...群组内数据的权限。
问题: 如果权限简单,比如只看自己的数据,查询非常简单: WHERE user_id = ? ORDER BY create_time DESC LIMIT N 这种查询用索引就好解决。
但如果加入群组权限,查询的逻辑就变成了: SELECT * FROM a_large_table WHERE user_id = ? OR group_id IN (用户所属群组以及所有下级群组的 ID 列表) ORDER BY create_time DESC LIMIT N
这个查询就比较慢了 比如假定结构是这样:

查询就变成了
SELECT * FROM project JOIN `group` ON project.group_id = `group`.id WHERE `group`.id IN (SELECT 用户关联的群组及其子群组 id) OR user_id = 20 ORDER BY project.created_at DESC LIMIT 10; 这时候 (group_id, user_id, created_at) 也不好使;
问了 AI ,说了几个方案: 1 、应用层聚合/union user_id 和 group_id 的,建两个索引; 2 、冗余一张 用户能访问数据的表,直接查这个表; 3 、引入 es 之类的中间件;
想问一下实际大家是怎么处理的?
]]>项目地址: https://github.com/alchem-x/compact-sb
Compact Java App 是基于 Java 25 新特性( JEP 512 )的编程方式,让你可以像写 Python 一样写 Java 程序:
// 传统的 Java Hello World public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } // Compact Java App Hello World void main() { IO.println("Hello, World!"); } 这个仓库演示了 Compact Java App 的实际应用 - 一个轻量级的 Web 服务器实现。
compact-sb/ ├── Lu.java # 紧凑 Java App 主程序 ├── CompactSB.java # Web 服务器核心 ├── lib/ # 依赖库 └── README.md java -version # 需要 Java 25 或更高版本 java Lu.java && java -cp "lib/*" CompactSB.java Lu.java - 紧凑 Java App 的精髓:
void main() { // 简洁的 Web 服务器启动 IO.println("Starting Compact Web Server..."); // 自动导入所有 java.base 类 var server = new Server(); server.start(8080); IO.println("Server running at http://localhost:8080/"); } | 特性 | 传统 Java | Compact Java App |
|---|---|---|
| Hello World | 5 行,4 个概念 | 3 行,1 个概念 |
| 依赖管理 | 需要 Maven/Gradle | 直接运行源文件 |
| 学习曲线 | 陡峭 | 平缓 |
| 开发速度 | 慢 | 快速 |
| 运行时性能 | 优秀 | 同样优秀 |
IO.println()替代System.out.println()这个项目展示了 Compact Java App 在 Web 开发中的应用。未来可以:
Compact Java App 让 Java 重新变得简单优雅,同时保持其强大的生态系统。未来已来,让我们一起拥抱这个全新的 Java 时代! 🚀
本项目是 Compact Java App 理念的实践演示,展示了如何用简洁的代码构建实用的应用程序。
]]>@Component @RequiredArgsConstructor public class WebSocketServer implements ApplicationRunner, DisposableBean { @Value("${netty.port}") private int port; private final EventLoopGroup bossGroup = new NioEventLoopGroup(); private final EventLoopGroup workerGroup = new NioEventLoopGroup(); private final ServerBootstrap b = new ServerBootstrap(); private final Map<String, Channel> map = new HashMap<>(); @Override public void destroy() { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } @Override public void run(ApplicationArguments args) throws Exception { b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new WebSocketServerProtocolHandler("/ws", true, 3000L)); pipeline.addLast(new WebSocketFrameHandler(map)); } }); ChannelFuture future = b.bind(port).sync(); System.out.println("WebSocket server started on port " + port); future.channel().closeFuture().sync(); } } @Slf4j public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> { private final Map<String, Channel> map; public static final AttributeKey<String> URI_ATTRIBUTE_KEY = AttributeKey.valueOf("URI"); public WebSocketFrameHandler(Map<String, Channel> map) { this.map = map; } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { WebSocketServerProtocolHandler.HandshakeComplete handshakeComplete = (WebSocketServerProtocolHandler.HandshakeComplete) evt; Channel currentChannel = ctx.channel(); String uri = handshakeComplete.requestUri(); currentChannel.attr(URI_ATTRIBUTE_KEY).set(uri); Channel exist = map.get(uri); if (Objects.isNull(exist)) { map.put(uri, currentChannel); log.info(new Message(uri, currentChannel.id().toString(), 1).toString()); } else { //无效 exist.close().sync();//明确在 close 执行完成后在重新上线,但是好像没生效一样,直接就输出上线! 1= 上线,0= 下线 map.put(uri, currentChannel); log.info(new Message(uri, currentChannel.id().toString(), 1).toString()); //无效 //exist.close().addListener(future -> log.info(new Message(uri, currentChannel.id().toString(), 1).toString())); //无效 //exist.close().sync().addListener(future -> log.info(new Message(uri, currentChannel.id().toString(), 1).toString())); } } else { super.userEventTriggered(ctx, evt); } } @Override public void channelInactive(ChannelHandlerContext ctx) { Channel channel = ctx.channel(); String uri = channel.attr(URI_ATTRIBUTE_KEY).get(); log.info(new Message(uri, channel.id().toString(), 0).toString()); } } 日志输出:
2025-09-21 03:16:54.669 INFO 30008 --- [ntLoopGroup-5-1] org.example.WebSocketFrameHandler : Message{uri='/ws/abc_123', sessiOnId='86aeadad', status=上线} 2025-09-21 03:16:58.837 INFO 30008 --- [ntLoopGroup-5-2] org.example.WebSocketFrameHandler : Message{uri='/ws/abc_123', sessiOnId='fa3a5fbb', status=上线} 2025-09-21 03:16:58.838 INFO 30008 --- [ntLoopGroup-5-1] org.example.WebSocketFrameHandler : Message{uri='/ws/abc_123', sessiOnId='86aeadad', status=下线} 我用 netty 提供的 websocket 服务,想达到新设备踢出老设备的在线状态, 但是控制不好状态
预期的效果:
日志输出:
但是现在看结果是错误的顺序。
想在这个服务里保证状态的正确,我尝试使用自定义 eventGroup 并且设置线程数 1 来执行代码也不行,也尝试跟 ai 沟通了半宿也研究明白
netty-all 的版本是 4.2.6.Final
]]>zxing 库就可以生成二维码了,但是要注意一个重要参数,ErrorCorrectionLevel 纠错能力等级,等级越高,内容码点越密集,纠错能力当然也越强(即使被遮挡了一部分也能还原) <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.5.2</version> </dependency> public enum ErrorCorrectionLevel { L(1), M(0), Q(3), H(2); } 可以发现纠错等级越高,生成二维码的码点越密集。
左边 level=0 ,右边 level=3
|
|
|
如果为了生成更好一点的二维码,建议使用qrgen
<dependency> <groupId>com.github.aytchell</groupId> <artifactId>qrgen</artifactId> <version>3.0.0</version> </dependency> 创建二维码
private BufferedImage createRoundedQRCodeImage(String url, int width, int level) throws QrConfigurationException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, WriterException { QrCodeRenderer qrCodeRenderer = new QrCodeRenderer(PixelStyle.ROWS, MarkerStyle.ROUND_CORNERS); ColorConfig colorCOnfig= new ColorConfig(new RgbValue(0, 0, 0), new RgbValue(255, 255, 255)); Map<EncodeHintType, Object> hints = new HashMap<>(); hints.put(EncodeHintType.ERROR_CORRECTION, getErrorCorrectionLevel(level)); return qrCodeRenderer.encodeAndRender(url, colorConfig, width, width, hints); } 效果

--- 关于作者 ---

我现在有两个 springboot 项目,这两个项目本地开发连接 docker 的 mysql 都没有问题。
然后都部署到同一个 centos 虚拟机里,都使用的是本电脑的局域网 ip 连接的数据库,一个可以正常访问到数据库,一个就访问不到,确认了链接 url 除了库名都是一样的,想请教一下这能是什么原因呢?
]]>Extension Pack for Java 和 Spring Boot Extension Pack
插件配置 setting.json "java.configuration.runtimes": [ { "name": "JavaSE-1.8", "path": "D:\\xxxx\\xxxx\\jdk8-271", "default": true //项目默认运行 jdk 版本 }, { "name": "JavaSE-17", "path": "D:\\xxxx\\xxxx\\jdk-17.0.3.1" } ] // Extension Pack for Java 插件服务运行在高版本 jdk 上(jdk9+) "java.jdt.ls.java.home": "D:\\xxxx\\xxxx\\jdk-17.0.3.1", // Spring Boot Extension Pack 插件服务运行在高版本 jdk 上(jdk9+) "spring-boot.ls.java.home": "D:\\xxxx\\xxxx\\jdk-17.0.3.1", // 配置 maven 操作 "java.configuration.maven.userSettings": "D:\\xxxx\\settings.xml", //自定义仓库存储地址 "java.configuration.maven.globalSettings": "D:\\xxxx\\settings.xml",//自定义仓库存储地址(全局) "maven.executable.path": "D:\\xxxx\\apache-maven-3.8.2\\bin\\mvn", //自定义 mvn 命令地址 git graph //代码管理 IntelliJ IDEA Keybindings //idea 快捷键映射,ctrl+shift+p 打开"键盘快捷方式"映射自定义修改 mybatis-tools //mybatis 或 plus 的文件.xml 和.java 快速跳转 Copy Reference //快速复制类或方法的包路径 JDK 21 will receive updates under the NFTC, until September 2026, a year after the release of the next LTS. Subsequent JDK 21 updates will be licensed under the Java SE OTN License (OTN) and production use beyond the limited free grants of the OTN license will require a fee.
严格翻译的话,似乎收费的只有“Subsequent JDK 21 updates”,只要不更新就不收费,问了几个大模型也都这么说。 但是在网上搜索,似乎是按时间收费的说法居多,即三年免费期一过就会开始收费。
]]>之前也用过 IBM 的 OpenJ9
其他各类厂商的 JDK 也多多少少都试过,感觉并没有很明显的提升
甚至有的 JDK 跑出来,还有编码问题,日志都是乱码的
还有类似 SSL 的支持兼容问题
看各类文档,总说启动快快快,内存省省省
想知道是不是属实
总的来说,目前用的 bellsoft liberica jdk 基本没出过问题,我的 mac ,windows 还有 linux 服务器,几乎都是用的这个,甚至 docker 镜像也都是找的 bellsoft
]]>public static <T> List<T> parseList(String jsonString, Class<T> elementClazz) throws Exception { ObjectMapper mapper = new ObjectMapper(); TypeReference<List<T>> typeReference = new TypeReference<List<T>>() {}; return mapper.readValue(jsonString, typeReference); } public static <T> List<T> parseList(String jsonString, TypeReference<List<T>> typeReference) throws Exception { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(jsonString, typeReference); } public static void main(String[] args) throws Exception { String jsOnListStr= "[{\"username\":\"pure1\",\"phone\":\"18xxx\"},{\"username\":\"pure2\",\"phone\":\"19xxx\"}]"; List<User> userList1 = parseList(jsonListStr, User.class); List<User> userList2 = parseList(jsonListStr, new TypeReference<List<User>>(){}); } 两个转完的 list 里,第一个 list 里的对象在断点里看实际上是个LinkedHashMap ,是无法正常调用实体类的 get 方法的。第二个 list 里的对象就是真正的User。所以我分别去看了两个方法的 typeReference 对象,第一个方法的 typeReference 对象里的_type 值为“java.util.List<T>”,第二个方法里的 typeReference 对象里的_type 值为“java.util.List<xxx.xxx.entity.User>”。虽然第二个方法可以正常使用,但是封装肯定是为了简便,以TypeReference<List<T>> typeReference作为入参感觉很奇怪,我底层了解的不多,我的认知里在入参的时候 new 一个 TypeReference 和在方法里 new 一个 TypeReference 应该是一样的才对。希望有大牛帮我解惑,或者是不是我第一个方法的代码写的有问题。
在此先谢谢各位了!!!
]]>问题: 用户发现自己打卡位置在焦作,但系统显示位置在信阳;通过网页端高德 api ,发现高德的接口确实返回错误
高德 API: https://restapi.amap.com/v3/ip?key=xx&ip=xx ip 地址为:223.90.35.17 (实际为河南焦作,但高德返回为信阳)
lz 除了高德,分别试了腾讯、ip138 、ip2region(开源) 这三者都没问题
目前已经将该问题提给客服,客户说是会给产品团队;
** 这边给需要使用地图服务和已经使用高德 api 服务的各位提个醒 **
]]>简化一些通用性的操作吧 不用登录对应的系统就可以进行一些操作 企业多系统之间也可以有关联
]]>主要是用来储存用户的一些配置信息,还有程序的运行日志(日志可能会到 100k/day 级别),定期清理日志(90day/180day?)
目前有几个疑问:
目标是让程序尽可能的可靠,可以容忍日志丢失之类的情况,但程序至少需要正常运行。
]]>