1. 概述

当前 Java Web 开发领域有不少主流框架,比如 SpringPlayGrails。选择哪个框架,往往取决于具体业务场景和技术诉求。

本文将带你快速上手 Ninja Framework —— 一个轻量但功能完整的全栈 Java Web 框架。我们会从零搭建一个简单应用,并覆盖其核心特性:路由、JSON 渲染、依赖注入、持久化、国际化等。适合有一定 Java Web 经验的开发者参考,踩坑预警提前拉满 ✅

2. Ninja 框架简介

Ninja 是一个基于成熟 Java 生态的全栈 Web 框架,主打 轻量 + 约定优于配置(convention-over-configuration)。它本身不重复造轮子,而是整合了一批久经考验的开源库:

  • JSON/XML 渲染:Jackson
  • 依赖注入:Guice
  • 持久化:Hibernate(支持 JPA 2.0)
  • 数据库迁移:Flyway
  • 模板引擎:Freemarker

开发体验上,Ninja 提供了 SuperDevMode 支持代码热重载,改完保存立马看效果,开发效率直接起飞 ⚡️

3. 环境搭建

启动一个 Ninja 项目需要以下基础工具:

  • Java 8+
  • Maven 3+
  • IDE(推荐 IntelliJ 或 Eclipse)

快速创建项目

使用 Maven Archetype 一键生成项目骨架:

mvn archetype:generate -DarchetypeGroupId=org.ninjaframework \
  -DarchetypeArtifactId=ninja-servlet-archetype-simple

按提示输入 groupIdartifactIdversion 即可。

手动添加依赖(适用于已有项目)

pom.xml 中引入核心依赖:

<dependency>
    <groupId>org.ninjaframework</groupId>
    <artifactId>ninja-core</artifactId>
    <version>6.5.0</version>
</dependency>

启动应用

首次构建并运行:

mvn clean install
mvn ninja:run

访问 http://localhost:8080,看到默认页面即表示启动成功:

home scaled 1

4. 项目结构

Ninja 遵循标准 Maven 目录结构,同时按约定组织代码:

eclipse projectStructure-1

关键目录说明:

  • src/main/java/controllers:控制器类,处理请求
  • src/main/java/models:实体类,对应数据库表
  • src/main/java/services:业务逻辑服务
  • src/main/java/conf:配置类(如路由、模块绑定)
  • src/main/java/views:Freemarker 模板文件(.ftl.html
  • src/main/java/assets:静态资源(JS、CSS、图片)
  • src/test/java:单元测试

5. 控制器(Controller)

控制器负责接收请求并返回响应。Ninja 的 Controller 需遵循以下约定:

  • ✅ 类放在 controllers 包下,类名以 Controller 结尾
  • ✅ 处理请求的方法返回 Result 类型

示例:创建 ApplicationController

@Singleton
public class ApplicationController {
    public Result index() {
        return Results.html();
    }
}
  • Results.html() 返回一个 HTML 响应
  • @Singleton 注解(来自 Guice)确保整个应用中该 Controller 只有一个实例,避免频繁创建

6. 视图(View)

Ninja 默认使用 Freemarker 作为模板引擎,视图文件需以 .ftl.html 结尾。

根据上一节的 index() 方法,Ninja 会自动查找: views/ApplicationController/index.ftl.html

创建该文件:

<html>  
<head>
    <title>Ninja: Index</title>
</head>
<body>
    <h1>${i18n("helloMsg")}</h1>
    <a href="/userJson">User Json</a>
</body>
</html>
  • ${i18n("helloMsg")}:调用内置 i18n 标签读取国际化消息,后续会详解
  • 链接指向 /userJson 接口,用于测试 JSON 渲染

7. 路由(Route)

路由定义了 URL 到控制器方法的映射。Ninja 使用 conf.Routes 类进行配置。

修改 Routes 类:

public class Routes implements ApplicationRoutes {
    @Override
    public void init(Router router) {          
        router.GET().route("/index").with(ApplicationController::index);
    }
}

现在访问 http://localhost:8080/index 即可看到页面:

index scaled 1

⚠️ 注意:路由注册顺序很重要,更具体的路由应放在前面,避免被通配路由拦截。

8. JSON 渲染

Ninja 内置 Jackson 支持,通过 Results.json() 即可返回 JSON 响应。

ApplicationController 中添加方法:

public Result userJson() {
    HashMap<String, String> userMap = new HashMap<>();
    userMap.put("name", "Norman Lewis");
    userMap.put("email", "norman.lewis@example.com");    
    return Results.json().render(userMap);
}

同时注册路由:

router.GET().route("/userJson").with(ApplicationController::userJson);

访问 http://localhost:8080/userJson,返回:

userJson scaled 1

9. 服务层(Service)

为解耦业务逻辑,推荐将核心逻辑放入 services 层,并通过依赖注入使用。

定义服务接口

public interface UserService {
    HashMap<String, String> getUserMap();
}

实现服务

public class UserServiceImpl implements UserService {
    @Override
    public HashMap<String, String> getUserMap() {
        HashMap<String, String> userMap = new HashMap<>(); 
        userMap.put("name", "Norman Lewis"); 
        userMap.put("email", "norman.lewis@example.com"); 
        return userMap;
    }
}

依赖注入配置

conf.Module 类中绑定接口与实现:

@Singleton
public class Module extends AbstractModule {
    protected void configure() {        
        bind(UserService.class).to(UserServiceImpl.class);
    }
}

在 Controller 中使用

public class ApplicationController {
    @Inject
    UserService userService;
    
    public Result userJson() {
        HashMap<String, String> userMap = userService.getUserMap();
        return Results.json().render(userMap);
    }
}

✅ Guice 的 @Inject 注解让依赖管理变得简单粗暴,无需 XML 配置。

10. Flash 作用域

Flash Scope 是 Ninja 提供的一种跨请求传递一次性消息的机制,常用于重定向后展示成功/错误提示。

在 Controller 中设置消息

public Result showFlashMsg(FlashScope flashScope) {
    flashScope.success("操作成功!");
    flashScope.error("操作失败,请重试");
    return Results.redirect("/home");
}
  • Results.redirect("/home") 重定向到 /home
  • 消息会暂存并在下一次请求中可用

在视图中显示消息

在对应的 .ftl.html 文件中添加:

<#if (flash.error)??>
    <div class="alert alert-danger">
        ${flash.error}
    </div>
</#if>
<#if (flash.success)??>
    <div class="alert alert-success">
        ${flash.success}
    </div>
</#if>

Freemarker 的 ??> 用于安全判断变量是否存在。

效果预览:

flash 1 scaled 2

11. 国际化(I18n)

Ninja 内建 I18n 支持,配置简单,切换语言只需改请求参数或 Cookie。

配置支持语言

conf/application.conf 中添加:

application.languages=fr,en

创建语言资源文件

  • 默认语言(英文):conf/messages.properties
header.home=Home!
helloMsg=Hello, welcome to Ninja Framework!
  • 法语:conf/messages_fr.properties
header.home=Accueil!
helloMsg=Bonjour, bienvenue dans Ninja Framework!

在代码中使用

通过 LangMessages 注入:

@Singleton
public class ApplicationController {
    @Inject
    Lang lang;

    @Inject
    Messages msg;
}

方式一:设置响应语言

Result result = Results.html();
lang.setLanguage("fr", result); // 强制设置为法语
return result;

方式二:获取指定语言消息

Optional<String> language = Optional.of("fr");        
String helloMsg = msg.get("helloMsg", language).get();

Ninja 会自动根据请求头(Accept-Language)、Cookie 或参数(如 ?lang=fr)判断当前语言。

12. 持久化(Persistence)

Ninja 基于 JPA 2.0,使用 Hibernate 作为实现,并内置 H2 支持便于开发。

12.1 实体类(Model)

models 包下创建实体:

@Entity
public class User {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    Long id;
    public String firstName;
    public String email;  
}

12.2 配置 Hibernate

src/main/resources/META-INF/persistence.xml 中配置:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
  version="2.0">
   
    <persistence-unit name="dev_unit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="hibernate.connection.driver_class" value="org.h2.Driver" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="hibernate.connection.autocommit" value="true" />
        </properties>
    </persistence-unit>
</persistence>

application.conf 中指定:

ninja.jpa.persistence_unit_name=dev_unit
db.connection.url=jdbc:h2:./devDb
db.connection.username=sa
db.connection.password=

12.3 使用 EntityManager

通过 Guice 的 Provider 注入 EntityManager

public class ApplicationController {
    @Inject 
    Provider<EntityManager> entityManagerProvider;
}

插入数据(带事务)

@Transactional
public Result insertUser(User user) {
    EntityManager entityManager = entityManagerProvider.get();
    entityManager.persist(user);
    entityManager.flush();
    return Results.redirect("/home");
}

查询数据(只读)

@UnitOfWork
public Result fetchUsers() {
    EntityManager entityManager = entityManagerProvider.get();
    Query q = entityManager.createQuery("SELECT x FROM User x");
    List<User> users = (List<User>) q.getResultList();
    return Results.json().render(users);
}

@UnitOfWork 是 Ninja 提供的便捷注解,自动管理 EntityManager 的开启与关闭,特别适合只读查询,避免手动处理事务。

13. 参数校验(Validation)

Ninja 支持 JSR-303 Bean Validation,开箱即用。

在实体上添加校验注解

public class User {
    // ...
    
    @NotNull
    public String firstName;
}

在 Controller 中启用校验

@Transactional
public Result insertUser(FlashScope flashScope, @JSR303Validation User user, Validation validation) {
    if (validation.getViolations().size() > 0) {
        flashScope.error("校验失败:用户创建失败");
    } else {
        EntityManager entityManager = entityManagerProvider.get();
        entityManager.persist(user);
        entityManager.flush();
        flashScope.success("用户 '" + user.firstName + "' 创建成功");
    }
    return Results.redirect("/home");
}
  • @JSR303Validation:开启对 User 对象的自动校验
  • Validation 对象:用于获取校验结果(getViolations()
  • 校验失败时,错误信息可通过 flashScope 返回给前端

⚠️ 注意:Ninja 当前基于 JSR-303(Bean Validation 1.0),如需使用 2.0(JSR-380)特性,需手动升级依赖。

14. 总结

Ninja 是一个设计简洁、集成度高的 Java Web 框架,适合快速构建中小型应用。其优势在于:

  • ✅ 轻量无侵入,依赖主流库(Guice、Jackson、Hibernate)
  • ✅ 约定优于配置,减少样板代码
  • ✅ 开发体验佳(热重载、内建 H2)
  • ✅ 功能完整:路由、I18n、验证、持久化一应俱全

虽然生态和社区相比 Spring 略显小众,但对于追求简洁和效率的项目,Ninja 依然是个值得考虑的选择。

文中所有代码示例均可在 GitHub 获取:https://github.com/eugenp/tutorials/tree/master/web-modules/ninja


原始标题:Introduction to Ninja Framework | Baeldung