摘要
Spring-Web的好处是,快速上手、快速成型,且成熟稳定无Bug。
但对于个人而言,这套框架太重了。由此探寻更好的轻量Web框架Jooby!
正文
本文源码地址meethigher/jooby-example: 基于Netty的轻量级Web框架Jooby使用示例
一、搭建项目 创建原生maven空项目,引入依赖
1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId> io.jooby</groupId>
<artifactId> jooby-netty</artifactId>
<version> 2.16.1</version>
</dependency>
<dependency>
<groupId> ch.qos.logback</groupId>
<artifactId> logback-classic</artifactId>
<version> 1.2.12</version>
</dependency>
创建启动类
1
2
3
4
5
6
7
8
9
10
11
12
13
import io.jooby.Jooby ;
public class Application extends Jooby {
//构造函数快
{
get ( "/" , ctx -> "Hello World" );
}
public static void main ( String [] args ) {
runApp ( args , Application . class );
}
}
展开
二、配置文件 Jooby同样支持解析多种类型的配置文件,只记录常用的application.conf
Jooby读取配置文件优先级顺序
优先应用根目录下。即System.getProperty("user.dir") 其次类路径下,即resources下或者jar包内的。 resources添加application.conf
1
2
3
server.port = 8080
app.name = "内部"
database.url = "jdbc:mysql://localhost:3306/mydb"
示例接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import io.jooby.Jooby ;
import io.jooby.MediaType ;
import java.nio.charset.StandardCharsets ;
public class Application extends Jooby {
//构造函数快
{
// use Decorator.
decorator ( next -> ctx -> {
// get response content-type.
final MediaType responseType = ctx . getResponseType ();
// if content-type is text-specific
if ( responseType . isTextual ())
// override response type with current media type and always use UTF-8 charset!
ctx . setResponseType ( responseType , StandardCharsets . UTF_8 );
// pipe next response procedure.
return next . apply ( ctx );
});
get ( "/" , ctx -> {
String appName = getConfig (). getString ( "app.name" );
return "Welcome to " + appName ;
});
get ( "/database" , ctx -> {
String dbUrl = getConfig (). getString ( "database.url" );
return "Database URL: " + dbUrl ;
});
}
public static void main ( String [] args ) {
runApp ( args , Application . class );
}
}
展开
三、开发接口 3.1 Script API-仅支持简单路由 Script API 通常用于编写小型的、单一用途的路由处理器。
支持占位符路由规则,但是像拼参形式的就不支持。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import static io.jooby.Jooby.runApp ;
public class ScriptAPIApplication {
public static void main ( String [] args ) {
//consumer函数式接口简写
runApp ( args , app -> {
app . get ( "/param/{id}" , ctx -> {
String id = ctx . path ( "id" ). value ();
return id ;
});
app . post ( "/param/{id}" , ctx -> {
return ctx . path ( "id" ). value ();
});
});
}
}
3.2 MVC API-支持复杂路由/静态资源/全局请求拦截/全局异常拦截 添加依赖
1
2
3
4
5
6
7
8
9
10
11
12
<!-- MVC API 核心模块 -->
<dependency>
<groupId> io.jooby</groupId>
<artifactId> jooby-apt</artifactId>
<version> 2.16.1</version>
</dependency>
<!-- json模块 -->
<dependency>
<groupId> io.jooby</groupId>
<artifactId> jooby-gson</artifactId>
<version> 2.16.1</version>
</dependency>
创建controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package top.meethigher.mvc.controller ;
import io.jooby.MediaType ;
import io.jooby.Multipart ;
import io.jooby.annotations.* ;
import java.io.FileOutputStream ;
import java.io.IOException ;
import java.io.InputStream ;
/**
* @author chenchuancheng github.com/meethigher
* @since 2023/6/17 23:31
*/
@Path ( "/mvc" )
public class Controller {
@GET ( "/get" )
public String get () {
return "Hello World" ;
}
@GET ( "/pathParam/{id}" )
public String pathParam ( @PathParam String id ) {
return id ;
}
/**
* @see <a href="https://jooby.io/#context-parameters-path">正则表达式校验</a>
*/
@GET ( "/pathParam/{id:^[a-zA-Z0-9]{5}$}" )
public String pathParamRegex ( @PathParam String id ) {
return id ;
}
@GET ( "/queryParam" )
public String queryParam ( @QueryParam String id , @QueryParam String name ) {
return name + "--->" + id ;
}
/**
* form表单,支持两种形式
* application/x-www-form-urlencoded,不支持文件
* multipart/form-data,支持文件
*/
@POST ( "/formParam" )
public String formParam ( @FormParam String name , @FormParam String age , @FormParam Multipart file ) {
if ( file != null ) {
try ( InputStream is = file . file ( "file" ). stream (); FileOutputStream fos = new FileOutputStream ( "test.txt" )) {
int b ;
while (( b = is . read ()) != - 1 ) {
fos . write ( b );
}
fos . flush ();
} catch ( IOException e ) {
}
}
return name + "--->" + age ;
}
@POST ( "/bodyParam" )
@Consumes ( MediaType . JSON ) //可省略
@Produces ( MediaType . JSON ) //可省略
public Object bodyParam ( Object body ) {
return body ;
}
@POST ( "/pageQuery" )
public String pageQuery ( PageRequest req ) {
return req . toString ();
}
@GET ( "/failure" )
public String failure () {
throw new RuntimeException ( "测试失败" );
}
@GET ( "/error" )
public String error () {
int i = 1 / 0 ;
return null ;
}
}
class PageRequest {
private Integer pageIndex ;
private Integer pageSize ;
public Integer getPageIndex () {
return pageIndex ;
}
public void setPageIndex ( Integer pageIndex ) {
this . pageIndex = pageIndex ;
}
public Integer getPageSize () {
return pageSize ;
}
public void setPageSize ( Integer pageSize ) {
this . pageSize = pageSize ;
}
}
注册controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import io.jooby.* ;
import io.jooby.json.GsonModule ;
import top.meethigher.mvc.controller.Controller ;
import top.meethigher.mvc.utils.Resp ;
import javax.annotation.Nonnull ;
import java.nio.charset.StandardCharsets ;
import static io.jooby.Jooby.runApp ;
/**
* @author chenchuancheng github.com/meethigher
* @since 2023/6/17 23:30
*/
public class MVCApplication {
public static void main ( String [] args ) {
runApp ( args , app -> {
// 配置静态资源,优先应用路径下static,其次类路径下static
app . assets ( "/**" , "/static" );
// 设置全局装饰器
app . decorator ( new GlobalDecorator ());
// 设置全局异常处理
app . error ( new GlobalErrorHandler ());
// 设置 JSON 支持
app . install ( new GsonModule ());
// 注册控制器
app . mvc ( new Controller ());
});
}
}
/**
* 全局装饰器
*
* @author chenchuancheng github.com/meethigher
* @since 2023/6/18 17:38
*/
class GlobalDecorator implements Route . Decorator {
@Nonnull
@Override
public Route . Handler apply ( @Nonnull Route . Handler next ) {
return ctx -> {
final MediaType responseType = ctx . getResponseType ();
//设置编码为utf-8
if ( responseType . isTextual ()) {
ctx . setResponseType ( responseType , StandardCharsets . UTF_8 );
}
return next . apply ( ctx );
};
}
}
/**
* 全局异常处理器
*
* @author chenchuancheng github.com/meethigher
* @since 2023/6/18 17:36
*/
class GlobalErrorHandler extends DefaultErrorHandler {
@Override
public void apply ( @Nonnull Context ctx , @Nonnull Throwable cause , @Nonnull StatusCode code ) {
// 处理异常并返回适当的响应
// if (cause instanceof RuntimeException) {//会存在多态问题,如继承。而我需要的是严格相等
if ( cause . getClass (). equals ( RuntimeException . class )) {
// 处理自定义异常
ctx . setResponseType ( MediaType . json , StandardCharsets . UTF_8 );
ctx . send ( Resp . getFailureResp ( cause . getMessage ()). toString ());
} else {
// // 处理自定义异常
// ctx.setResponseType(MediaType.json, StandardCharsets.UTF_8);
// ctx.send(Resp.getErrorResp().toString());
super . apply ( ctx , cause , code );
}
}
}
展开
四、参考致谢 jooby:做更多! 更容易!