1. 概述
当前 Java Web 开发领域有不少主流框架,比如 Spring、Play 和 Grails。选择哪个框架,往往取决于具体业务场景和技术诉求。
本文将带你快速上手 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
按提示输入 groupId
、artifactId
、version
即可。
手动添加依赖(适用于已有项目)
在 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
,看到默认页面即表示启动成功:
4. 项目结构
Ninja 遵循标准 Maven 目录结构,同时按约定组织代码:
关键目录说明:
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
即可看到页面:
⚠️ 注意:路由注册顺序很重要,更具体的路由应放在前面,避免被通配路由拦截。
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
,返回:
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 的 ??>
用于安全判断变量是否存在。
效果预览:
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!
在代码中使用
通过 Lang
或 Messages
注入:
@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