V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
SuperMild
V2EX  ›  Java

Javalin - 发现 Java 的可爱之处

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

    多年以前我看到一篇神奇的文章 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
    不能自动写代码完成任务的都不是最优秀的图灵完备语言
    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 还是比较直白的。
    fzdwx
        11
    fzdwx  
       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/ 这个看着也很简洁。
    fzdwx
        13
    fzdwx  
       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
    fenglangjuxu
        26
    fenglangjuxu  
       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 年才搞这个,晚了点吧,早就该推出来了
    promisenev
        29
    promisenev  
       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   ·   实用小工具   ·   1300 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 17:54 · PVG 01:54 · LAX 09:54 · JFK 12:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.