Javalin - 发现 Java 的可爱之处 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
SuperMild
5.49D
V2EX    Java

Javalin - 发现 Java 的可爱之处

  •  
  •   SuperMild
    ahui2016 2022-08-31 17:58:07 +08:00 4871 次点击
    这是一个创建于 1168 天前的主题,其中的信息可能已经有所发展或是发生改变。

    多年以前我看到一篇神奇的文章 Java for Everything, 其中提出了一个大胆的想法:就连脚本小工具都用 Java 来写。

    我读了这篇文章,但并没有立即使用 Java, 而是写了一段时间 Go, 又写了一段时间 Python, 最近想起这篇文章,就想用 Java 写点东西。

    Java 的优点

    1. 易学 (而且程序员几乎都或多或少已经学过), 学习资料丰富
    2. 工具齐全 (比如有 IntelliJ IDEA, 有 maven)
    3. 生态强大,第三方库丰富
    4. 稳重感、安心感。Java 经过了多年工业级考验,能带给我很 "稳" 的心理暗示, 有一种脚踏实地的安全感。

    Java 的缺点

    本来 Java 是很嗦的,这是它最被诟病的地方了。

    但现在 Java 有了 record, lambda, Stream API, var(类型推断) 等等, 已经变得不是太嗦了,根据我狭隘的个人经验,甚至认为比 Go, Python 更简洁。

    • Python 我为了获得静态类型的好处,就试着老老实实用 type hints, 但用着用着发现,毕竟不是天生静态类型,有很多不方便的地方,而且这样做之后, 代码再也不简短了,经常要带着一大堆 type hints, 很麻烦。
    • Go 由于泛型还不完善,加上 if err != nil, 代码是很简明易懂, 但论代码的简短,也是经常比不上 Java 。
    • 但我说 Java 不嗦,前提是我只是轻度使用,较少遇到必须用 Java Bean, 或不能用 Stream API 的情况。只要能把 record, lambda, Stream API, var(类型推断) 等等尽量用上, Java 就不嗦了。

    Monostich (单行诗)

    以前我喜欢用 Go 来做一些自用的小软件,做一个本地网站通过访问网页来使用, 最近想做一个叫 monostich 的小工具,就尝试用 Java 来做。

    主要功能

    monostich 的功能非常简单。

    • 在首页点击 'New' 跳转到 '新增一条记录' 页面。
    • 输入标题和内容,点击 'Submit' 按钮,搞定。
    • 其中,标题是用来方便搜索的,内容则是任意一句话(例如一条命令、一个网址、一句备忘等等)
    • 首页有一个搜索框和最近新增项目列表。

    与 Go 语言代码对比

    这次我用 Javalin 来做本地网站,并且采用了以前我写 Go 时的代码组织方式, 因此两种语言可以做个对比。

    程序入口: main.go 与 Main.java 对比

    可见, Go 与 Java 的代码几乎一模一样:

    // github.com/ahui2016/dictplus/blob/main/main.go func main() { e := echo.New() e.Static("/public", "public") e.File("/", "public/index.html") api := e.Group("/api", sleep) api.POST("/get-word", getWordHandler) api.POST("/add-word", addWordHandler) api.POST("/update-word", updateWordHandler) api.POST("/delete-word", deleteWordHandler) api.POST("/search-words", searchHandler) e.Logger.Fatal(e.Start(*addr)) } 
    // github.com/ahui2016/monostich/.../App.java public class App { public static void main(String[] args) { Javalin app = Javalin.create(config -> config.addStaticFiles(staticFiles -> { staticFiles.hostedPath = "/"; staticFiles.directory = "/public"; staticFiles.location = Location.CLASSPATH; })).start(port); app.post("/api/insert-poem", Handle.insertPoem); app.post("/api/update-poem", Handle.updatePoem); app.post("/api/delete-poem", Handle.deletePoem); app.post("/api/get-poem", Handle.getPoem); app.post("/api/search-poems", Handle.searchPoems); } } 

    Handler

    可见, Go 比 Java 多了一些 if err != nil, 其余代码几乎一模一样:

    // github.com/ahui2016/dictplus/blob/main/handler.go func addWordHandler(c echo.Context) error { w := new(Word) if err := c.Bind(w); err != nil { return err } if err := db.InsertNewWord(w); err != nil { return err } return c.JSON(OK, Text{w.ID}) } 
    // github.com/ahui2016/monostich/.../Handle.java static Handler insertPoem = ctx -> { var form = ctx.bodyAsClass(PoemForm.class); var poem = db.insertPoem(form); ctx.json(poem); }; 

    数据库

    在上面 Handler 的代码中, db.InsertNewWord() 与 db.insertPoem() 的具体实现如下所示。

    可以看到, Go 的代码还是受到了 if err != nil 的困扰,显得很嗦, 而 Java 采用 Stream API 的好处是非常明显的,代码简洁了很多。

    // github.com/ahui2016/dictplus/.../database.go func (db *DB) InsertNewWord(w *Word) (err error) { tx := db.mustBegin() defer tx.Rollback() if w.ID, err = getNextID(tx, word_id_key); err != nil { return } w.CTime = util.TimeNow() if err = insertWord(tx, w); err != nil { return } err = tx.Commit() return } 
    // github.com/ahui2016/monostich/.../DB.java Poem insertPoem(PoemForm form) { return jdbi.withHandle(handle -> handle.inTransaction(h -> { var poem = new Poem(getNextId(h), form.title(), form.stich(), Util.now()); h.createUpdate(Stmt.INSERT_POEM) .bindMap(poem.toMap()) .execute(); return poem; })); } 

    相关链接

    本软件后端采用 Javalin, 前端采用 mj.js, 相关介绍请看以下链接。

    31 条回复    2022-09-01 21:06:49 +08:00
    qW7bo2FbzbC0
        1
    qW7bo2FbzbC0  
       2022-08-31 18:12:31 +08:00   4
    你说的这些优点是确实存在的,你用的这个 lin 框架比 spring 简洁, 同时我觉得 C#比 Java 走更多一步。甚至不需要额外的三方 web 框架

    https://docs.microsoft.com/en-us/aspnet/core/tutorials/min-web-api

    ```
    var builder = WebApplication.CreateBuilder(args);

    // Add services to the container.
    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();

    var app = builder.Build();

    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
    app.UseSwagger();
    app.UseSwaggerUI();
    }

    app.UseHttpsRedirection();

    var summaries = new[]
    {
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    app.MapGet("/weatherforecast", () =>
    {
    var forecast = Enumerable.Range(1, 5).Select(index =>
    new WeatherForecast
    (
    DateTime.Now.AddDays(index),
    Random.Shared.Next(-20, 55),
    summaries[Random.Shared.Next(summaries.Length)]
    ))
    .ToArray();
    return forecast;
    })
    .WithName("GetWeatherForecast");

    app.Run();

    internal record WeatherForecast(DateTime Date, int TemperatureC, string? Summary)
    {
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
    ```
    lisongeee
        2
    lisongeee  
       2022-08-31 18:24:16 +08:00   3
    呃呃,既然追求代码简洁,为什么不试试神奇的 kotlin 呢?

    ![image]( https://user-images.githubusercontent.com/38517192/187657094-4f610b00-e0fb-4e7c-a5e1-914a06ce019e.png)
    zbatman
        3
    zbatman  
       2022-08-31 18:32:00 +08:00   3
    有请双方辩手入场
    hccsoul
        4
    hccsoul  
       2022-08-31 18:38:10 +08:00
    我先来 ,java 是世界上最优秀的语言,杠我的去和楼主对线 /
    imzcg2
        5
    imzcg2  
       2022-08-31 18:39:33 +08:00
    不能自动写代码完成任务的都不是最优秀的图灵完备语言/div>
    justanetizen
        6
    justanetizen  
       2022-08-31 19:13:42 +08:00
    呃,java 8 连 UTF8 处理起来都麻烦,不知道你们为毛还要抱残守缺。
    WispZhan
        7
    WispZhan  
       2022-08-31 19:13:46 +08:00 via Android
    为什么不看看其他 JVM 语言呢?
    dqzcwxb
        8
    dqzcwxb  
       2022-08-31 19:21:40 +08:00
    @justanetizen #6 细说
    zed1018
        9
    zed1018  
       2022-08-31 19:28:23 +08:00
    如果一定要 everything 我选择 kotlin ,java 有的我都有,我有的 java 真不一定现在就有,而且 non-nullable type 这个对我来讲太刚需
    SuperMild
        10
    SuperMild  
    OP
       2022-08-31 19:31:27 +08:00
    @qW7bo2FbzbC0 一直听说 C# 的优秀,以后找个机会研究研究学习学习。

    @lisongeee kotlin 用了更多函数式编程方法,我脑子不太够用,总觉得 kotlin 用起来反而比 java 更累,java 还是比较直白的。
    lllllliiii
        11
    lllllliiii  
       2022-08-31 19:37:41 +08:00   1
    然后这个写 Java for everything 的人的 github 的置顶项目没有一个 Java 写的。太搞了哈哈。
    wuxqing
        12
    wuxqing  
       2022-08-31 19:55:32 +08:00
    如果不搞 spring 那个体系,https://solon.noear.org/ 这个看着也很简洁。
    lllllliiii
        13
    lllllliiii  
       2022-08-31 20:02:41 +08:00
    @wuxqing #12
    solon 就代码组织形式跟 spring 就什么区别吗?
    cyningxu
        14
    cyningxu  
       2022-08-31 20:10:10 +08:00
    kotlin ,你是我的神!
    Veneris
        15
    Veneris  
       2022-08-31 20:16:11 +08:00 via iPhone
    go 打成二进制包就可以了,java 需要 jre ,不过反过来说 java 因此也是跨平台的了,这一点是优点还是缺点见仁见智
    但是 java 打出来的包体积,启动后的内存占,普遍要大一些,有时候我只是想写个玩具项目而已,总给我一种很笨重的感觉
    另外,kotlin 可以 native 了,不过还没有具体尝试过
    Leviathann
        16
    Leviathann  
       2022-08-31 20:20:57 +08:00
    @Veneris kotlin 的 native ,目前还不如借助 graalvm 的 jvm native
    proxychains
        17
    proxychains  
       2022-08-31 20:21:53 +08:00
    我先来
    感觉不如 php...java
    SuperMild
        18
    SuperMild  
    OP
       2022-08-31 20:26:15 +08:00
    @Veneris Go 在这方面真是这大优点,如果是做东西放到 VPS 上去用,我现在也是倾向于用 Go
    TWorldIsNButThis
        19
    TWorldIsNButThis  
       2022-08-31 20:30:25 +08:00
    @SuperMild 恩 习惯 kotlin 的 lambda 算是一个坎,lambda 也是 kotlin 语法魔术的核心
    另外 kotlin 也比较推崇灵活变换隐含的 receiver 来实现更接近自然语言的表达
    Huelse
        20
    Huelse  
       2022-08-31 20:41:40 +08:00
    来体验下 scala fp 吧
    makelove
        21
    makelove  
       2022-08-31 21:03:31 +08:00
    java 还是不太行,很多年没写过 java 了,当年觉得 java 实在算不上好用,比如没有关键字参数,不知道现在有没有了
    wdhwg001
        22
    wdhwg001  
       2022-09-01 06:34:27 +08:00 via iPhone   1
    @qW7bo2FbzbC0 我私底下觉得这纯属微软管太宽了,啥都是第一方的,所以社区没有造轮子的热情。

    这个策略本身就不对,正确的是把基础的打好,然后 fund 一些好的第三方框架,出钱出力去帮忙补测试覆盖,提升性能和出钱 audit 代码。
    CodeCodeStudy
        23
    CodeCodeStudy  
       2022-09-01 09:10:55 +08:00
    java17 支持多行字符串了吗
    chrawsl
        24
    chrawsl  
       2022-09-01 09:25:49 +08:00
    @wdhwg001 但是第一方轮子确实好用(
    qinxi
        25
    qinxi  
       2022-09-01 09:58:40 +08:00
    keepfun
        26
    keepfun  
       2022-09-01 10:36:38 +08:00
    看了下 好牛逼 和 spring boot 完全两个样子
    和 go 挺像的
    fgwmlhdkkkw
        27
    fgwmlhdkkkw  
       2022-09-01 10:39:03 +08:00
    那你有什么理由拒绝 kotlin 呢?还有 async 用。
    CodeCodeStudy
        28
    CodeCodeStudy  
       2022-09-01 10:41:17 +08:00
    @qinxi #25 感谢! 20 年才搞这个,晚了点吧,早就该推出来了
    EthanZC
        29
    EthanZC  
       2022-09-01 14:32:35 +08:00
    scala YYDS
    wetalk
        30
    wetalk  
       2022-09-01 15:17:12 +08:00
    Kotlin ,Scala ,Groovy 任君挑选
    SuperMild
        31
    SuperMild  
    OP
       2022-09-01 21:06:49 +08:00
    @fenglangjuxu 是呀,我从 Go 转过来感觉很舒服。但如果正经项目还是用 spring boot 比较好,业余小项目用 Javalin 很方便。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4211 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 01:00 PVG 09:00 LAX 17:00 JFK 20:00
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86