项目地址: 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?)
目前有几个疑问:
目标是让程序尽可能的可靠,可以容忍日志丢失之类的情况,但程序至少需要正常运行。
]]>我现在具体实现就是,用户点击运行,会先发送消息到一个分发的消息队列里,这个分发的消息队列用分发这个需要上传素材,然后发送消息到上传素材队列,这个上传素材队列的消费者是集群模式,上传完成一个会调用回掉队列,然后回掉队列监听到全部素材都完成,走轮询,然后轮询完成,发送消息搭建广告。
这个是一个流程图: image.jpg
困惑就是目前需要很多团队来使用功能,如何吧 mq 队列根据团队拆分,还有执行速度,以及服务器的划分,我这个方式是否合理,大家还有更好的方案吗
]]>看市面上的库基本都是模板写入 excel 的,各位彦祖有没有推荐能支持的从模板读取 excel 的?
或者有其他更好的解决方案?
不行只能硬编码了 : <
]]>伪代码 c#的
Db.From<UserTable>() .LeftJoin<DeptTable>((user,dept )=>user.id==dept.userId) .Select((user,dept)=>new UserDto(){ UserName=user.Name, DeptName=dept.Name }) .FilterWhere(input.rules)
input.rules = [{field:'UserName',condition:'eq',value:'张三'}]
类似这样效果的 java 应该怎么做 ,看了 mabatis-plus 好像没看到类似的 api
]]>时间过得真快,转眼我与 Java 相识也已有十一年。2014 年,那时候我刚刚接触 Java ,一开始只是觉得“Hello World”挺神奇,但没想到,这门语言会陪我走过这么多重要的时刻。
websocket 连接/smtp 端点,模拟 SMTP 协议发送邮件,发件地址: test@mine.com ,收件地址 test@nullht.com ,邮件内容 Hello World 。请注意:大小写敏感,websocket 连接仅保留 1 分钟左右,断了需要重连并重头发送 SMTP 命令
可以确定发送的地址是 wss://interview-test.nullht.com/smtp
我按照他的要求,写出了下面的代码,其他的不重要,我们直接看我发送请求的格式
private void initializeCommands() { commandQueue.clear(); commandQueue.add("EHLO mine.com\r\n"); commandQueue.add("MAIL FROM:<test@mine.com>\r\n"); commandQueue.add("RCPT TO:<test@nullht.com>\r\n"); commandQueue.add("DATA\r\n"); // 注意:邮件内容需要包含完整格式 commandQueue.add( "From: test@mine.com\r\n" + "To: test@nullht.com\r\n" + "Subject: Hello\r\n" + "\r\n" + // 空行分隔头部和正文 "Hello World\r\n" + ".\r\n" // 结束符 ); commandQueue.add("QUIT\r\n"); }
这个代码可以得到下面的响应:
Sending command: EHLO mine.com Server response: 220 邮件服务器准备就绪 Server response: 250 很高兴认识你 Sending command: MAIL FROM:<test@mine.com> Server response: 250 发件人已接受 Sending command: RCPT TO:<test@nullht.com> Server response: 250 收件人已接受 Sending command: DATA Server response: 500 命令不被识别
显然是 DATA 后的内容有问题,其他的都没毛病。但是我参照下面的教程
https://blog.csdn.net/weixin_39833509/article/details/88965720
感觉这个格式也没问题啊,我试了好久了都没搞出来到底哪里有问题,什么 AI 、谷歌全试过了,有没有大佬救一下,小弟我是真不行了,我先谢谢各位了
]]><groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-java</artifactId>
他在 app/jsapi/native 等支付中,每个交易类型一个单独的包,里面放对象 每个包一个 Transaction 类,支付回调时又拿不到是哪个类型的交易,应该用哪个包下的 Transaction 接回调的结果
伪代码
import com.wechat.pay.java.service.partnerpayments.nativepay.model.Transaction ; class{ Transaction transaction; try { RequestParam requestParam = WeChatUtil.handleNodifyRequestParam(request); transaction = notificationParser.parse(requestParam, Transaction.class); log.info("支付回调信息: {}", transaction); } catch (IOException e) { throw new RuntimeException(e); } }
如果交易类型不是 nativepay 又得换别的包下的 Transaction ,这不太对吧 有没有佬踩过这个坑
]]> <select id="getShareRoleUserId" resultType="com.agt.technology.component.role.pojo.po.RoleUserItem"> SELECT r.id, ur.user_id FROM agt_cim_role r JOIN agt_cim_user_role ur ON ur.role_id = r.id WHERE deleted = 0 AND r.type = 3 </select>
但是如果包含标签的话,就会想下面一样把所有行都齐平了
<select id="getShareRoleUserId" resultType="com.agt.technology.component.role.pojo.po.RoleUserItem"> SELECT r.id, ur.user_id FROM agt_cim_role r JOIN agt_cim_user_role ur ON ur.role_id = r.id WHERE deleted = 0 AND r.type = 3 <if test="roleIds != null"> AND r.id in <foreach collection="roleIds" item="roleId" open="(" separator="," close=")"> #{roleId} </foreach> </if> </select>
我试过同事的电脑也是这样,是不是我们配置出了什么问题,大佬们帮我看看你们的格式化会这样吗
]]>java: java.lang.ExceptionInInitializerError com.sun.tools.javac.code.TypeTag :: UNKNOWN
可以尝试检查 lombok 版本,使用 1.18.38 及以上版本
https://projectlombok.org/changelog
Lombok Changelog
v1.18.38 (March 31st, 2025)
https://github.com/projectlombok/lombok/issues/3814
https://youtrack.jetbrains.com/issue/IDEA-370086