1. 概述
Blade 是一个轻量级的 Java 8+ MVC 框架,从零开始设计,目标明确:自包含、高效、优雅、直观且极快。
它的设计灵感来自多个框架,包括 Node.js 的 Express、Python 的 Flask,以及 Go 语言的 Macaron 和 Martini。
Blade 是 Let’s Blade 项目的一部分,该项目还包含一系列小型库,涵盖了从验证码生成到 JSON 转换、模板引擎和数据库连接等功能。
不过,在本教程中,我们只聚焦于 Blade 的 MVC 功能。
2. 快速上手
首先,创建一个空的 Maven 项目,并在 pom.xml
中添加 最新的 Blade MVC 依赖:
<dependency>
<groupId>com.bladejava</groupId>
<artifactId>blade-mvc</artifactId>
<version>2.0.14.RELEASE</version>
</dependency>
2.1. 打包 Blade 应用
由于我们的应用是 JAR 包形式,不像 WAR 包那样有 /lib
目录,这就带来一个问题:如何将 blade-mvc
以及其它依赖打包进应用?
有多种方式可以实现,各有优劣,详情可参考 如何用 Maven 创建可执行 JAR。
为简单起见,✅我们使用 Maven Assembly Plugin 方式,将所有依赖的 JAR 解压后合并成一个 uber-JAR。
2.2. 运行 Blade 应用
**Blade 基于 Netty**,这是一个高性能的异步事件驱动网络框架。因此,运行 Blade 应用无需外部应用服务器或 Servlet 容器,仅需 JRE 即可:
java -jar target/sample-blade-app.jar
启动后,应用默认可通过 http://localhost:9000
访问。
3. 架构理解
Blade 的架构非常清晰:
其请求处理流程如下:
- Netty 接收请求
- 执行中间件(可选)
- 执行 WebHook(可选)
- 路由匹配
- 返回响应给客户端
- 清理资源
后续我们会详细讲解这些组件。
4. 路由机制
简单来说,路由是将 URL 映射到 Controller 的机制。
Blade 提供两种路由方式:基础路由和注解路由。
4.1. 基础路由
基础路由适用于微服务或小型 Web 应用:
Blade.of()
.get("/basic-routes-example", ctx -> ctx.text("GET called"))
.post("/basic-routes-example", ctx -> ctx.text("POST called"))
.put("/basic-routes-example", ctx -> ctx.text("PUT called"))
.delete("/basic-routes-example", ctx -> ctx.text("DELETE called"))
.start(App.class, args);
方法名即 HTTP 动词,简单粗暴。
虽然这里返回的是文本,但我们也可以渲染页面(后面会讲)。
4.2. 注解路由
更复杂的场景推荐使用注解路由,建议将路由逻辑放在独立的类中。
首先,使用 @Path
注解定义一个 Controller,Blade 会在启动时扫描它:
@Path
public class RouteExampleController {
@GetRoute("/routes-example")
public String get(){
return "get.html";
}
@PostRoute("/routes-example")
public String post(){
return "post.html";
}
@PutRoute("/routes-example")
public String put(){
return "put.html";
}
@DeleteRoute("/routes-example")
public String delete(){
return "delete.html";
}
}
也可以使用通用的 @Route
注解并指定方法:
@Route(value="/another-route-example", method=HttpMethod.GET)
public String anotherGet(){
return "get.html" ;
}
⚠️ 如果不指定方法,该路由会拦截所有 HTTP 请求(不论 GET、POST 等)。
4.3. 参数注入
Blade 支持多种参数注入方式,以下是一些常见用法:
- 表单参数:
@GetRoute("/home")
public void formParam(@Param String name){
System.out.println("name: " + name);
}
- RESTful 路径参数:
@GetRoute("/users/:uid")
public void restfulParam(@PathParam Integer uid){
System.out.println("uid: " + uid);
}
- 文件上传:
@PostRoute("/upload")
public void fileParam(@MultipartParam FileItem fileItem){
byte[] file = fileItem.getData();
}
- 请求头参数:
@GetRoute("/header")
public void headerParam(@HeaderParam String referer){
System.out.println("Referer: " + referer);
}
- Cookie 参数:
@GetRoute("/cookie")
public void cookieParam(@CookieParam String myCookie){
System.out.println("myCookie: " + myCookie);
}
- JSON 请求体:
@PostRoute("/bodyParam")
public void bodyParam(@BodyParam User user){
System.out.println("user: " + user.toString());
}
- VO 参数(通过表单字段绑定):
@PostRoute("/voParam")
public void voParam(@Param User user){
System.out.println("user: " + user.toString());
}
<form method="post">
<input type="text" name="age"/>
<input type="text" name="name"/>
</form>
5. 静态资源处理
Blade 可以自动处理静态资源,只需将文件放入 src/main/resources/static/
目录下。
例如:src/main/resources/static/app.css
可通过 http://localhost:9000/static/app.css
访问。
5.1. 自定义静态路径
可以通过代码添加自定义路径:
blade.addStatics("/custom-static");
或者通过配置文件设置:
mvc.statics=/custom-static
5.2. 启用资源列表
出于安全考虑,默认不显示静态目录内容。如需启用:
blade.showFileList(true);
或在配置文件中:
mvc.statics.show-list=true
访问 http://localhost:9000/custom-static/
即可查看目录内容。
5.3. 使用 WebJars
Blade 自动支持 WebJars,路径为 /webjars/
。
例如引入 Bootstrap:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>4.2.1</version>
</dependency>
访问路径为:http://localhost:9000/webjars/bootstrap/4.2.1/css/bootstrap.css
6. HTTP 请求处理
由于 Blade 不基于 Servlet 规范,其 Request
和 HttpRequest
对象与传统 Servlet 不同。
6.1. 表单参数
Blade 使用 Optional
处理表单参数查询结果:
Optional<String> query(String name)
Optional<Integer> queryInt(String name)
Optional<Long> queryLong(String name)
Optional<Double> queryDouble(String name)
也可带默认值:
String query(String name, String defaultValue)
int queryInt(String name, int defaultValue)
long queryLong(String name, long defaultValue)
double queryDouble(String name, double defaultValue)
可通过参数自动注入:
@PostRoute("/save")
public void formParams(@Param String username){
// ...
}
或从 Request
对象读取:
@PostRoute("/save")
public void formParams(Request request){
String username = request.query("username", "Baeldung");
}
6.2. JSON 数据
通过 @BodyParam
注解绑定 JSON 到 POJO:
curl -X POST http://localhost:9000/users -H 'Content-Type: application/json' \
-d '{"name":"Baeldung","site":"baeldung.com"}'
POJO 示例(使用 Lombok):
public class User {
@Getter @Setter private String name;
@Getter @Setter private String site;
}
自动绑定:
@PostRoute("/users")
public void bodyParams(@BodyParam User user){
// ...
}
手动读取:
@PostRoute("/users")
public void bodyParams(Request request) {
String bodyString = request.bodyToString();
}
6.3. RESTful 参数
路径参数支持:
@GetRoute("/user/:id")
public void user(@PathParam Integer id){
// ...
}
或通过 Request
读取:
@GetRoute("/user")
public void user(Request request){
Integer id = request.pathInt("id");
}
支持 Integer
、Long
、String
等类型。
6.4. 数据绑定
Blade 支持 JSON 和表单数据自动绑定:
@PostRoute("/users")
public void bodyParams(User user){}
6.5. 请求和会话属性
设置属性:
Session session = request.session();
request.attribute("request-val", "Some Request value");
session.attribute("session-val", 1337);
获取属性:
String requestVal = request.attribute("request-val");
String sessionVal = session.attribute("session-val"); //It's an Integer
支持泛型,无需手动转型。
6.6. 请求头
读取请求头:
String header1 = request.header("a-header");
String header2 = request.header("a-safe-header", "with a default value");
Map<String, String> allHeaders = request.headers();
6.7. 工具方法
提供如下便捷方法:
-
boolean isIE()
-
boolean isAjax()
-
String contentType()
-
String userAgent()
6.8. Cookie 处理
读取 Cookie:
Optional<Cookie> cookieRaw(String name);
String cookie(String name, String defaultValue);
Map<String, String> cookies = request.cookies();
7. HTTP 响应处理
通过方法参数获取 Response
对象:
@GetRoute("/")
public void home(Response response) {}
7.1. 简单输出
输出不同类型内容:
response.text("Hello World!");
response.html("<h1>Hello World!</h1>");
response.xml("<Msg>Hello World!</Msg>");
response.json("{\"The Answer\":42}");
response.json(user); // 自动序列化
7.2. 文件下载
response.download("the-file.txt", "/path/to/the/file.txt");
7.3. 模板渲染
response.render("admin/users.html");
默认模板路径:src/main/resources/templates/
7.4. 重定向
response.redirect("/target-route");
7.5. Cookie 写入
response.cookie("cookie-name", "Some value here");
response.removeCookie("cookie-name");
7.6. 其他操作
常见方法:
-
status(int status)
-
headers()
-
notFound()
-
cookies()
-
contentType(String contentType)
-
body(@NonNull byte[] data)
-
header(String name, String value)
8. WebHook
WebHook 是路由执行前后的拦截器。
实现 WebHook
接口:
@FunctionalInterface
public interface WebHook {
boolean before(RouteContext ctx);
default boolean after(RouteContext ctx) {
return true;
}
}
8.1. 全局拦截
使用 @Bean
注解全局生效:
@Bean
public class BaeldungHook implements WebHook {
@Override
public boolean before(RouteContext ctx) {
System.out.println("[BaeldungHook] called before Route method");
return true;
}
}
8.2. 特定路径拦截
Blade.of()
.before("/user/*", ctx -> System.out.println("Before: " + ctx.uri()));
.start(App.class, args);
8.3. 中间件
中间件优先于普通 WebHook 执行:
public class BaeldungMiddleware implements WebHook {
@Override
public boolean before(RouteContext context) {
System.out.println("[BaeldungMiddleware] called before Route method and other WebHooks");
return true;
}
}
注册方式:
Blade.of()
.use(new BaeldungMiddleware())
.start(App.class, args);
内置中间件:
-
BasicAuthMiddleware
-
CorsMiddleware
-
XssMiddleware
-
CsrfMiddleware
9. 配置管理
Blade 遵循约定优于配置,但可通过 application.properties
自定义。
9.1. 读取配置
启动时读取:
Blade.of()
.on(EventType.SERVER_STARTED, e -> {
Optional<String> version = WebContext.blade().env("app.version");
})
.start(App.class, args);
路由中读取:
@GetRoute("/some-route")
public void someRoute(){
String authors = WebContext.blade().env("app.authors","Unknown authors");
}
通过 BladeLoader
:
@Bean
public class LoadConfig implements BladeLoader {
@Override
public void load(Blade blade) {
Optional<String> version = WebContext.blade().env("app.version");
String authors = WebContext.blade().env("app.authors","Unknown authors");
}
}
9.2. 配置属性
配置项可按前缀分组读取:
Environment environment = blade.environment();
Map<String, Object> map = environment.getPrefix("app");
String version = map.get("version").toString();
String authors = map.get("authors","Unknown authors").toString();
9.3. 多环境支持
通过命令行指定环境:
java -jar target/sample-blade-app.jar --app.env=prod
框架会自动合并 application.properties
和 application-prod.properties
。
10. 模板引擎
Blade 支持多种模板引擎,如 FreeMarker、Jetbrick、Pebble、Velocity。
10.1. 默认模板
<h1>Hello, ${name}!</h1>
10.2. 使用外部引擎
以 Jetbrick 为例:
<dependency>
<groupId>com.bladejava</groupId>
<artifactId>blade-template-jetbrick</artifactId>
<version>0.1.3</version>
</dependency>
配置:
@Bean
public class TemplateConfig implements BladeLoader {
@Override
public void load(Blade blade) {
blade.templateEngine(new JetbrickTemplateEngine());
}
}
10.3. 自定义模板引擎
实现 TemplateEngine
接口:
void render (ModelAndView modelAndView, Writer writer) throws TemplateException;
11. 日志系统
Blade 使用 SLF4J,内置 blade-log
。
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
11.1. 自定义日志配置
通过系统属性设置:
com.blade.logger.rootLevel=info
com.blade.logger.dir=./logs
11.2. 排除内置日志
<exclusion>
<groupId>com.bladejava</groupId>
<artifactId>blade-log</artifactId>
</exclusion>
12. 自定义扩展
12.1. 异常处理
自定义异常处理器:
@Bean
public class GlobalExceptionHandler extends DefaultExceptionHandler {
@Override
public void handle(Exception e) {
if (e instanceof BaeldungException) {
BaeldungException baeldungException = (BaeldungException) e;
String msg = baeldungException.getMessage();
WebContext.response().json(RestResponse.fail(msg));
} else {
super.handle(e);
}
}
}
12.2. 错误页面
配置自定义错误页:
mvc.view.404=my-404.html
mvc.view.500=my-500.html
错误页中可使用变量:
<p>错误信息: "<strong>${message}</strong>"</p>
<pre> ${stackTrace} </pre>
13. 定时任务
使用 @Schedule
注解:
@Bean
public class ScheduleExample {
@Schedule(name = "baeldungTask", cron = "0 */1 * * * ?")
public void runScheduledTask() {
System.out.println("This is a scheduled Task running once per minute.");
}
}
管理任务:
List<Task> allScheduledTasks = TaskManager.getTasks();
Task myTask = TaskManager.getTask("baeldungTask");
boolean closed = TaskManager.stopTask("baeldungTask");
14. 事件监听
支持多种事件:
SERVER_STARTING, SERVER_STARTED, SERVER_STOPPING, SERVER_STOPPED,
SESSION_CREATED, SESSION_DESTROY, SOURCE_CHANGED, ENVIRONMENT_CHANGED
示例:
Blade.of()
.on(EventType.SESSION_CREATED, e -> {
Session session = (Session) e.attribute("session");
session.attribute("name", "Baeldung");
})
.start(App.class, args);
15. 会话实现
默认为内存会话,可自定义:
@Bean
public class SessionConfig implements BladeLoader {
@Override
public void load(Blade blade) {
blade.sessionType(new RedisSession());
}
}
16. 命令行参数
支持以下参数:
--server.address=192.168.1.100
--server.port=8080
--app.env=prod
17. IDE 运行支持
17.1. Eclipse
右键运行 App
类即可。安装 ANSI 插件可修复控制台乱码。
17.2. IntelliJ IDEA
直接运行 App.main()
,支持 ANSI 颜色。
17.3. Visual Studio Code
安装 Java 扩展包后,按 Ctrl+F5
即可运行。
18. 总结
Blade 是一个轻量高效的 Java MVC 框架,适合构建中小型 Web 应用。虽然文档主要为中文,但核心功能已在 GitHub 上有英文说明。
示例代码可在 GitHub 获取。