Java编写自带MVC框架的HTTP服务器,支持Sun、Netty、Jetty、Tomcat方式启动,支持HTTP和HTTPS协议及Websocket,并实现了类Spring MVC的部分注解,提供IOC注解(Component、Autowired、Qualifier),提供AOP注解(Before、After、Throwing),提供条件装配注解(Conditional),提供异步注解(Async),提供事务注解(Transactional),提供事件注解(EventListener),用来学习HTTP及Web框架相关知识,未强制使用第三方依赖,未实现标准的Servlet规范,自定义Request、Response、Cookie、Session及ServerContext,基于JDK1.8开发。
@NutBootTest
注解);@Controller
、@RestController
、@RequestParam
、@ResuestBody
、@ResponseBody
、@RequestMapping
等注解);@Component
、@Service
、@Repository
、@Autowired
、@Qualifier
、@Lazy
、@Primary
等注解);@Conditional
、@ConditionalOnClass
、@ConditionalOnBean
、@ConditionalOnApplicationType
等注解);@Aspect
、@Pointcut
、@Before
、@After
、@Throwing
、@Around
、@Compose
);@ConfigurationProperties
、@Value
);@Async
);@Transactional
);@EventListener
、@TransactionalEventListener
);@Websocket
、@PathParam
、@OnOpen
、@OnClose
、@OnMessage
、@OnError
);BeanPostProcessor
、BeanDefinitionPostProcessor
等);@ApiGroup
、@Api
等注解);可直接使用java -jar nut-1.x.x-SNAPSHOT.jar
进行启动,也可以配置启动参数,目前如下参数可以配置:
参数名 | 默认值 | 说明 |
---|---|---|
env | 无 | 环境变量 |
port | 8080 | 端口号 |
secure | false | 是否以HTTPS启动 |
secureKeyType | 无 | 安全证书类型:jks、pem |
jksPath | 无 | jks证书库路径 |
jkPassword | 无 | jks证书库密码 |
pemPublicKeyPath | 无 | pem证书公钥路径 |
pemPrivateKeyPath | 无 | pem证书私钥路径 |
staticDir | 无 | 静态资源的磁盘目录 |
staticPath | 无 | 静态资源的访问路径 |
staticDirPreview | false | 是否开启静态资源目录预览 |
enableScript | false | 是否开启脚本 |
scriptPath | 无 | 脚本资源的磁盘路径 |
scriptEncoding | UTF-8 | 脚本的字符编码集 |
enableScriptWatch | false | 是否开启脚本文件监听 |
logHandlers | ..NutLoggingConsoleHandler,..FileHandler | 日志处理器 |
logFormatter | ..NutLoggingFormatter | 日志格式化器 |
logLevel | INFO | 日志级别 |
logDir | ./logs | 日志存储目录 |
目前支持如下参数配置方式:
Nut#run(NutConfiguration, String[])
方式启动Nut,该方式只支持部分参数;java -Dserver.port=80 -jar nut-1.0.1-SNAPSHOT.jar
,该方式支持全部参数;java -jar nut-1.0.1-SNAPSHOT.jar server.port=80
,该方式支持全部参数;配置优先级(从低到高):
Nut#run(NutConfiguration, String[])
-> 操作系统的环境变量形式 -> JVM参数形式 -> 程序参数形式。
如开启 enableScriptWatch 后,将会自动监听脚本文件,脚本文件更新后无需重启服务即可重新加载脚本文件。如修改访问路径,自动加载配置后,旧访问路径仅仅保留之前的接口信息,但仍然可以访问,新接口将保持修改后的最新信息。
jks服务启动参数示例:
java -Dserver.env=dev -Dserver.port=443 -Dserver.secure=true -Dserver.secureKeyType=jks -Dserver.jksPath=/root/cert/keystore.jks -Dserver.jksPassword=a51DGpI6 -Dserver.enableScript=true -Dserver.scriptPath=/root/request.txt -Dserver.enableScriptWatch=true -jar nut-1.0.1-SNAPSHOT.jar
pem服务启动参数示例:
java -Dserver.env=dev -Dserver.port=443 -Dserver.secure=true -Dserver.secureKeyType=pem -Dserver.pemPublicKeyPath=/root/cert/fullchain.pem -Dserver.pemPrivateKeyPath=/root/cert/privkey.pem -Dserver.enableScript=true -Dserver.scriptPath=/root/request.txt -Dserver.enableScriptWatch=true -jar nut-1.0.1-SNAPSHOT.jar
Nut 已上传到Maven中仓仓库,可通过以下方式进行使用:
<dependency>
<groupId>com.yeskery.nut</groupId>
<artifactId>nut</artifactId>
<version>1.0.14</version>
</dependency>
<dependency>
<groupId>com.yeskery.nut</groupId>
<artifactId>nut-netty</artifactId>
<version>1.0.14</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.yeskery.nut</groupId>
<artifactId>nut-jetty</artifactId>
<version>1.0.14</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.yeskery.nut</groupId>
<artifactId>nut-tomcat</artifactId>
<version>1.0.14</version>
<type>pom</type>
</dependency>
作为静态文件web服务使用时,可通过命令行参数进行配置:
java -Dserver.staticDir={静态资源的磁盘目录} -Dserver.staticPath={静态资源的访问路径} -jar nut-1.0.1-SNAPSHOT.jar
例如:
java -Dserver.staticDir=/root/static -Dserver.staticPath=/static -Dserver.staticDirPreview=true -jar nut-1.0.1-SNAPSHOT.jar
启动成功后可通过http://localhost:8080/static/
进行访问(默认提供一个简易的无密码保护的目录浏览页面),也可通过直接访问http://localhost:8080/static/*
对文件进行下载(*为staticDir下的静态文件)
通过该功能可以在本地以编写配置文件的方式对外提供HTTP服务,而无需进行代码开发,用于测试或临时接口提供时使用。可通过命令行参数进行配置:
java -Dserver.enableScript=true -Dserver.scriptPath={脚本资源的磁盘路径(多个脚本可以使用逗号(,)进行分割)} -jar nut-1.0.1-SNAPSHOT.jar
例如:
java -Dserver.enableScript=true -Dserver.scriptPath=/root/script.txt -jar nut-1.0.1-SNAPSHOT.jar
脚本以[
开始,以]
结束,在中括号包裹的范围内需要配置path
、method
、response_header
、response_cookie
、body
五个参数,其中path
、method
、body
是必填项目,response_header
、response_cookie
是非必填项目。
参数名 | 参数说明 | 备注 |
---|---|---|
path | 访问路径 | 如:/path 多个路径可以用逗号(,)拼接,如:/path1,/path2
|
method | 访问方法 | 可选值为HTTP规范中支持的方法如:get 、post 等,如需要配置所有方法可设置为*
|
response_header(可选) | 响应头 | 向请求方的响应中加入指定的响应头 |
response_cookie(可选) | 响应cookie | 向请求方的响应中加入指定的cookie |
body | 响应体 | 向请求方响应指定内容 |
接口可配置成单行,如:[path=/path1;method=get;response_header=[name=value];response_cookie=[name=value];body=hello world;]
也可配置多行,在多行中分号(;)可选,如:
[
path=/path2
method=get
response_header=[name=value]
response_cookie=[name=value]
body=hello world
]
在body
参数中,如果响应的内容过多可以采用多行文本方式进行配置,以```开始和结束,和markdown语法一致。
[
path=/path3
method=get
response_header=[name=value]
response_cookie=[name=value]
body=%text(```hello
world
```)
]
脚本只处理
[表达式]
之间的内容,其他格式的内容均不会影响脚本的解析,所以非[表达式]
的内容均会被忽略,这些内容都可以作为注释存在在脚本文件中,例如:
# 单行脚本注释
[path=/path1;method=get;response_header=[name=value];response_cookie=[name=value];body=hello world;]
// 多行脚本注释
[
path=/path2
method=get
response_header=[name=value]
response_cookie=[name=value]
body=hello world
]
在body
中,支持视图表达式以响应不同的媒体类型,视图表达式以%
开始,后接视图名称,后以()
包裹的内容为视图表达式参数,如:%text(hello wolrd)
,目前支持的视图表达式如下:
视图表达式名称 | 表达式说明 |
---|---|
text | 纯文本视图 |
html | html视图 |
xml | xml视图 |
json | json视图 |
file | 文件视图 |
image | 图片视图 |
如:
[path=/path4;method=get;body=%html(<h1>hello,world</h1>);]
,则可以提供html视图给请求方。
[path=/path5;method=get;body=%file(/root/record.txt);]
,则可以将本地/root/record.txt
文件里的内容提供请求方。
[path=/path6;method=get;body=%image(/root/sun.jpg);]
,则可以将本地/root/sun.jpg
图片提供给请求方。
其中视图表达式可以进行嵌套处理, 例如:
[path=/path7;method=get;body=%html(%file(/root/hello.html));]
,则可以将本地的/root/hello.html
以HTML媒体类型的方式响应给请求方。
在response_header
、response_cookie
和 body
中支持使用函数表达式,函数表达式以$
开始,后接函数名称,后以()
包裹的内容为函数参数,如:$upper(abc)
,目前支持的函数表达式如下:
函数表达式 | 函数表达式名称 | 参数名 | 函数表达式说明 |
---|---|---|---|
cookie | cookie函数 | (name) | 获取请求中的cookie值,如$cookie(name)获取请求中的名称为name的cookie值 |
header | 请求头函数 | (name) | 获取请求中的header值,如$header(name)获取请求中的名称为header的请求头值 |
path | 请求路径函数 | 无参数 | 获取请求的路径 |
method | 请求方法函数 | 无参数 | 获取请求的方法 |
param | 请求参数函数 | (name) | 获取请求参数,在url中的参数或以表单方式提交的参数,name:参数名 |
pathVariable | 路径参数函数 | (name) | 获取路径请求参数,在url中的路径参数,如:/user/{id} ,name:参数名 |
requestBody | 请求体函数 | 无参数 | 以字符串形式获取请求体内容 |
responseStatus | 响应状态码函数 | (code, body) | 返回指定的Http响应状态码,code:状态码,body:响应体内容 |
函数表达式 | 函数表达式名称 | 参数名 | 函数表达式说明 |
---|---|---|---|
md5 | md5函数 | (str) | 将指定的字符串进行md5散列运算并获取计算后的值 |
sha | sha函数 | (str) | 将指定的字符串进行sha散列运算并获取计算后的值 |
函数表达式 | 函数表达式名称 | 参数名 | 函数表达式说明 |
---|---|---|---|
fupper | 首字母大写函数 | (str) | 将给定的字符串转换为以首字母大写的形式返回,str:字符串 |
upper | 字母大写函数 | (str) | 将给定的字符串转换为大写的形式返回,str:字符串 |
lower | 字母小写函数 | (str) | 将给定的字符串转换为小写的形式返回,str:字符串 |
random | 随机函数 | [a, b] 或 (number, number(可选)) | 获取随机数,直接传入数组,将在数组中随机一个元素返回。 只传入一个 number 则返回0-number 之间的一个随机数。传入两个 number 则返回两个number 之间的随机数支持整数和小数 |
subString | 字符串截取函数 | (str, separator(可选), int(可选), int) | 字符串截取,str:要截取的字符串, (str, int)截取从指定的索引开始(包括)的字符串; (str, int, int)截取从指定索引开始(包括)从指定索引结束(不包括)的字符串; (str, separator, int, int) 在给定separator标志后指定索引开始(包括)从指定索引结束(不包括)的字符串; |
date | 日期函数 | (pattern(可选), startTime(可选), endTime(可选)) | 无参数时候,获取当前时间戳; (pattern),格式化当前时间; (startTime, endTime)从指定范围随机返回一个时间戳; (pattern, startTime, endTime)格式化指定范围内的时间; |
jsonObject | Json对象函数 | (json, field) | 获取json中的字段值,json:json字符串,field:字段名,可多级嵌套,如:user.address.code
|
函数表达式 | 函数表达式名称 | 参数名 | 函数表达式说明 |
---|---|---|---|
nameGenerator | 姓名生成函数 | (surname[可选], boy[可选]) | 中文姓名生成,surname=1为复姓,surname=0或不传为单姓名,boy=1为男性,boy=0或不传为女性 |
ipGenerator | ip生成函数 | 无 | ipv4地址随机生成 |
phoneGenerator | 手机号生成函数 | (op[可选]) | op运营商:0 移动 1 联通 2 电信 |
uuidGenerator | uuid生成函数 | (separator[可选]) | UUID生成,true:包含分隔符,false:不包含分隔符 |
http | Http请求函数 | (url, method[可选], headers[可选], body[可选]) | 以http请求获取指定url的响应结果 method,请求方法,支持以下方法:GET、POST、PUT、DELETE headers,请求头,以 = 拼接,多个请求头以& 分割,例如:name1=value1&name2=value2 |
js | JS请求函数 | (js) | 执行JS函数,使用JDK的JS引擎运行,语法参考JDK使用 |
valid | 验证函数 | (expression, success[可选], failure[可选]) | 验证指定的表达式是否为真,真时返回success表达式,默认success ,假时返回failure表达式,默认failure 。表达式支持 && 和 || 逻辑运算符,支持== 、!= 、>= 、> 、<= 、< 关系运算符 |
函数表达式 | 函数表达式名称 | 参数名 | 函数表达式说明 |
---|---|---|---|
mset | 保存键值函数 | (key, value, success[可选]) | 保存指定键值对,key:键,value:值,success:成功时返回的字符串,默认success
|
mget | 获取值函数 | (key, failure[可选]) | 获取指定键对应的值,key:键,failure:指定键对应的值不存在时返回的字符串,默认failure
|
mdelete | 删除键函数 | (key, success[可选]) | 删除指定键对应的值,key:键,success:删除成功时返回的字符串,默认success
|
函数表达式之间可以嵌套使用,因为函数表达式中间使用
,
作为参数分隔符,如果参数中也需要使用字符,
,可以使用符号`将表达式进行包裹。 例如$valid
函数,如果返回参数是json格式,其中也包含,
就可以这样使用:$valid(true, `{"code": 200, "msg": "success"}`, `{"code": 400, "msg": "failure"}`)
函数表达式示例:
# 保存用户信息示例
[
path=/user/{id}
method=post
body=%json($mset(user:$pathVariable(id), `$requestBody()`,`{"code": 200, "msg": "成功"}`))
]
# 获取用户信息示例
[
path=/user/{id}
method=get
body=%json($mget(user:$pathVariable(id), `{"code": 404, "msg": "用户信息不存在"}`))
]
# 以Json请求体形式进行用户登录示例
[
path=/user/login
method=post
body=%json($valid($jsonObject(`$requestBody()`, username) == user && $jsonObject(`$requestBody()`, password) == 123456, `{"code": 200, "msg": "登录成功", "token":"$uuidGenerator(false)"}`, `{"code": 400, "msg": "用户名或密码错误"}`))
]
# 以表单形式进行用户登录示例
[
path=/user/form_login
method=post
body=%json($valid($param(username) == user && $param(password) == $md5(123456), `{"code": 200, "msg": "登录成功", "token":"$uuidGenerator(false)"}`, `{"code": 400, "msg": "用户名或密码错误"}`))
]
自定义函数可通过
NutApplication#getWebConfigure#registerCustomFunction
方法进行注册。也可以将自定义函数对象注册到ApplicationContext
应用上下文中,将会自动进行注册。
Nut 已完整实现自定义的Request
、Response
、Cookie
、Session
及ServerContext
,可依赖Nut开发web服务。
Nut 使用Controller
来处理请求,可以自定义Controller
来处理请求,Controller
提供了doGet
、doPost
、doPut
、doDelete
、doOptions
、doTrace
来处理Http请求,如果没有对应请求的方法,可以重写doRequest
方法,该方法是请求处理的主方法。
Controller
的方法均包含三个参数:request
、response
及execution
,request
是用来封装请对象信息,response
是用来处理客户端的响应,execution
种包含了系统中的扩展对象,例如:请求转发器(Forward)、绑定上下文(BindContext)、视图处理器(ViewHandler)等等。
使用Controller
接口方法中的execution
参数获取转发器(Forward
),使用forward
方法来从一个Controller
跳转到另一个Controller
。
使用Controller
接口方法中的Response
接口参数的sendRedirect
方法,可以向客户端发送302重定向请求。
Nut 支持两种方式的Controller注册:
Controller
接口,调用ControllerManager#registerController(Controller, String...)
来注册;RequestHandler
为支持注册的函数式接口,调用ControllerManager#registerController(RequestHandler, Method, String...)
来注册;也可以使用@Controller
注解进行注册,使用方式和Spring MVC一致,在调用Nut#run
方法前通过Nut#getWebConfigure#addBasePackage
方法来添加要扫描的包路径。
因未使用ASM方式来解析参数名,如果在编译阶段也未将方法的参数名编译进去,在使用
@RequestParam
注解时需要手动指定参数名,如不指定参数名获取到的参数为类似arg0
,将无法正确注入参数。自jdk1.8开始就提供了保留参数编译的功能-parameters
。
@Controller
;@RestController
;@ControllerAdvice
;@RestControllerAdvice
;@ExceptionHandler
;@RequestMapping
;@GetMapping
;@PostMapping
;@PutMapping
;@DeleteMapping
;@CookieValue
;@RequestAttribute
;@SessionAttribute
;@ServerContextAttribute
;@PathVariable
;@RequestBody
;@RequestParam
;@ResponseBody
;@Configuration
;@Bean
;@Component
;@Service
;@Repository
;@Autowired
;@Qualifier
;@Value
;@ConfigurationProperties
@Before
;@After
;@Throwing
;@Around
;@Compose
;@Aspect
;@Pointcut
;@Transactional
;@Async
;@Api
;@ApiGroup
;@ApiIgnore
;@ApiModel
;@ApiModelProperty
;@ApiParam
;使用
@RestController
、@RestControllerAdvice
、@ResponseBody
注解时,需要调用Nut#getWebConfigure#getPluginManager#registerPlugin
提前向Nut注册ResponsivePlugin
插件,或引用支持的序列化框架依赖,通过自动探测器向Nut注册ResponsivePlugin
插件。
Nut 提供了ServerEventPlugin
、ControllerPlugin
、InterceptorPlugin
、ExceptionHandlePlugin
四种类型的插件。
ServerEventPlugin
用来处理服务启动与关闭时的事件监听;ControllerPlugin
用来处理查找controller时的扩展操作;InterceptorPlugin
用来在controller处理业务前后执行扩展操作,该插件有一个简短名称的接口Interceptor
;ExceptionHandlePlugin
用来controller处理业务异常时进行异常扩展处理。Nut还提供了两种注入NutApplication
和ApplicationContext
的插件基类:
NutApplicationSupportBasePlugin
:该插件已注入NutApplication
对象,可通过getNutApplication()
方法调用获取;ApplicationContextSupportBasePlugin
:该插件已注入ApplicationContext
对象,可通过getApplicationContext()
方法调用获取。ActuatorPlugin
:用来获取服务运行信息及服务远程关闭支持;AuthPlugin
:用来控制服务权限的插件;LoginPlugin
:用来限制用户登录后才能访问资源的插件;I18nPlugin
:用来支持国际化的插件;MemoryPlugin
:用来支持基于内存存储数据的插件,支持数据持久化(需要保证服务正常关闭);JdbcPlugin
:用来支持JDBC访问数据库的插件;ResponsivePlugin
:用来支持响应式输出的插件;DruidPlugin
:Druid连接池插件;XmlMyBatisPlugin
:基于XML配置的MyBatis插件;AnnotationMyBatisPlugin
:基于注解配置的MyBatis插件;MyBatisPlusPlugin
:基于MyBatis-Plus实现的数据库操作插件;MyBatisFlexPlugin
:基于MyBatisFlex实现的数据库操作插件;JedisPlugin
:Jedis实现的Redis插件;LettucePlugin
:Lettuce实现的Redis插件;AsyptPlugin
:用来加解密配置文件(例如:application.properties、application-dev.properties)中的敏感信息;OpenApi3Plugin
:根据项目中注册的基于注解实现的controller信息,生成对应的OpenApi3格式的json文件。Nut已支持以下模板引擎:
在引入指定依赖后,nut启动时会自动探测指定的类型是否存在以自动配置插件或模板引擎。 目前已支持以下自动探测类:
ResponsivePluginAutoDetector
:响应式输出插件自动探测类,将以如下优先级进行探测:
JackSon
;FastJson2
;FastJson
;ViewResolverAutoDetector
:视图解析器自动探测类,将以如下优先级进行探测:
Thymeleaf
;FreeMarker
;Beetl
;如需要关闭指定的自动探测类,可调用
Nut#getWebConfigure#addIgnoreAutoDetectClass
方法指定要忽略的自动探测类。也可以通过@NutApplication#ignoreAutoDetectors
方法进行忽略。
开发完毕后,通过NutApplication#getWebConfigure#addBasePackage
方法或注解@NutApplication#baseScanPackage
来添加要扫描的包路径,调用Nut#run
方法启动web服务。
Nut支持以下服务启动类型(定义在ServerType
枚举中):
AUTO
(默认启动类型):判断classpath中是否存在指定的服务依赖,并按照NETTY
、JETTY
、TOMCAT
、SUN
的顺序自动配置;SUN
:以JDK提供的HTTP服务启动;NETTY
:以Netty方式启动服务;JETTY
:以Jetty方式启动服务,因nut以JDK1.8开发,所以目前最高支持的Jetty版本为9.x;TOMCAT
:以Tomcat方式启动服务,因javax.servlet-api
变更包名为:jakarta.servlet-api
,默认使用javax.servlet-api
,所以已经使用jakarta.servlet-api
的Tomcat版本可能无法运行,开发环境使用9.x版本进行;ServerType
参数的Nut#run
方法进行启动;NutConfiguration
参数的Nut#run
方法进行启动,并使用NutConfiguration#setServerType
方法指定服务启动类型;Class<?> mainClass
参数的Nut#run
方法进行启动,在指定的启动主类上加@NutApplication
注解,在注解中使用@NutApplication#serverType
方法指定服务启动类型;Netty:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.77.Final</version>
</dependency>
Jetty:
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.48.v20220622</version>
</dependency>
Tomcat:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.54</version>
</dependency>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。