j-spring-web
v1.1.2
Published
源码:[j-spring](https://github.com/892280082/j-spring) 轻量级的 IOC 库. 源码:[j-spring-web](https://github.com/892280082/j-spring-web) 基于 j-spring 和 express 的 WEB 框架。
Downloads
6
Readme
源码:j-spring 轻量级的 IOC 库. 源码:j-spring-web 基于 j-spring 和 express 的 WEB 框架。
前言
j-spring-web 就是换了壳的 express,这个项目并没有重复创建轮子,只是对喜欢 express 的人提供了更多的选择。对于 java 程序员,肯定能闻到熟悉的配方和味道。
源码阅读思路
1. 该框架是基于 j-spring 开发,所以最先要看的对象是启动类和 bean 的后置处理器!
1.1 启动类 SpringWebStarter
该类实例化了 Express 对象 app,即启动了 web 服务器!随后将 ExpressConfiguration 类对 app 进行配置, 最后将实体类 app:ExpressApp (该类信息导入自 j-spring0type-wrap) 注入到了 spring 的 bean 容器,方便后续的二次注入!
1.2 配置类后置处理器 ExpressAppEnhanceBeanProcessor
ExpressConfiguration:Express 配置类,用于对 express 进行配置。例如设置 app.get,app.use,设置 session,设置 bodyParse。
- EjsViewConfigruation 设置 ejs 页面配置
- ExpressMemorySessionConfiguration 设置内存 session,用于开发
- BodyParseConfiguration body-解析
- MorganLogConfigruation express 的日志配置转接到 spring 日志
- StaticResourceConfigruation 静态资源配置
ExpressAppEnhanceBeanProcessor:该后置处理器主要用于搜集 ExpressConfiguration,用于 SpringWebStarter 使用
1.3 Controller 后置处理器
ControllerBeanConfiguration:controller 配置类,解析成路由映射信息。 ControllerBeanProcessor: 如果一个 bean 存在@Controller 注解,则被实例化成 ControllerBeanConfiguration (等同于 ExpressConfiguration)。
1.4 SpringParamterBeanPostProcessor 参数后置处理器
ParamEnhanceInterceptor: 一旦发生请求,遍历 action 中的所有参数和注解,然后通过该拦截器返回整理后的参数。
- QueryParamEnhance: 解析 url 传参
- PathVariableEnhance:解析 resful 传参
- ReflectParamEnhance: 解析类型传参 例如 (@Param() req:Request)
- SessionAttributeEnhance: 解析 session 取值 (@SessionAttribute('user') user:user)
SpringParamterBeanPostProcessor: 用于收集 ParamEnhanceInterceptor
1.5 SpringResultOperatePostProcessor 后置结果处理器
RouterEnhanceInterceptor: 路由提升拦截器,一旦匹配正确,可以更改原先的处理流程。
- RenderEnhance: 页面渲染
- ShuttleEnhance:模拟 rpc 远程调用
SpringResultOperatePostProcessor:用于收集 RouterEnhanceInterceptor 路由处理器!
启用流程
代码演示
import { spring, Component } from 'j-spring';
import { SpringMvcModule, Controller, Get, ResponseBody } from 'j-spring-mvc';
import { errorInfo } from 'j-spring-mvc/dist/springMvcExtends';
//控制器
@Controller('/api')
class ApiController {
@Get()
@ResponseBody()
async hello() {
throw 'requst error';
return { msg: 'hello' };
}
}
//控制器集合
const controllerList = [ApiController];
spring.bindModule([SpringMvcModule, controllerList]).invokeStarter();
设计思路
1.底层框架选用
对于 Node 上面的 WEB 框架,我最喜欢的还是 Express。
优点:
- 简单高效,并且具有函数式的美感。
- 生态丰富,拥有大量高质量的插件!
- 框架稳定,几乎没有坑。
缺点:
- 逼格有点低,划重点。
- 不支持 IOC 和 AOP
- 并且模块化不强,写起来散乱。
- 代码层面有美感,但是对于业务抽象描述能力不够。
2.优化方案
j-spring 提供 IOC 和 AOP 的能力,把 express 进行模块化的封装。
- 配置方面:定义 ExpressConfiguration 接口,提升模块化配置能力。
- 路由方面:定义@controller 然后利用后置处理器进行解析,装配进 express 中
代码实现
代码会在过后的几个章节进行描述,其实也不多,毕竟只是加了一层壳。
代码展现
1.启动配置
//1.SpringWeb 配置
const SpringWebModule = [
SpringWebStarter, //web启动器
ExpressAppEnhanceBeanProcessor, // express配置后置处理器
ControllerBeanProcessor, // 控制器后置处理器
SpringParamterBeanPostProcessor,
]; // 参数反射后置处理器 用于处理@RequestPram之类的
//2.express 配置
const springWebConfig = [
EjsViewConfigruation, // ejs视图配置
ExpressMemorySessionConfiguration, // 内存session配置
];
//3.控制器
const controllerClassList = [
StudentController, //学生路由控制器
XiaoAiController, //测试
];
spring
.bindModule([SpringWebModule, springWebConfig, controllerClassList])
.loadConfig({ indexMsg: 'j-spring', root: __dirname }) //加载配置
.invokeStarter(); //调用启动器
这里看到配置很多,主要是为了展示整个运行过程。其实 1 和 2 都可以放到 j-spring-web 里面作为默认配置一把到导出的。 例如
const SpringWebBaseModule = [...SpringWebModule,...springWebConfig]
spring.bindModule([SpringWebBaseModule,controllerClassList]).loadConfig({...}).invokeStarter();
如果需要更换其中一个配置,就只需要使用 j-spring 的 repalceClass 方法即可。例如将 session 交由 mysql 存储,更换指定配置即可。
spring
.bindModule([SpringWebBaseModule, controllerClassList])
.replaceClass(
ExpressMemorySessionConfiguration,
ExpressMysqlSeesionConfiguration
); //更换依赖即可
2.如何定义 express 配置
只要继承 ExpressConfiguration 接口即可。这样该配置就可以使用 j-spring 容器的能力,包括自动注入和装配。你可以写无限多个配置类,然后统一在 yaml 里面编写配置参数即可。
/**
* ejs页面配置
*/
@Component()
export class EjsViewConfigruation implements ExpressConfiguration {
@Value({ path: 'root', type: String })
root: string;
@Value({ path: 'express.viewPath', type: String, force: false })
viewPath: string = 'view';
load(app: any): void {
app.set('views', path.join(this.root, this.viewPath));
app.set('view engine', 'ejs');
}
isExpressConfiguration(): boolean {
return true;
}
}
//spring.bind(EjsViewConfigruation) 即可
3.设置路由
是不是熟悉的味道,嘿嘿。最大程度的还原了 springWeb 的编码风格。
- 页面渲染就是返回一个数组 [页面路径,渲染数据]
- @ResponseBody 就单纯返回 json 信息。
- @PathVariable @RequestParam 跟 java 一致
- @Param(key:string) 拿到 express 控制器原始的 req,res 对象
- 这里的参数反射是支持异步的,并且可以在请求结束后,执行销毁操作。主要为了后期的事务操作。
//定义控制器
@Controller('/student')
export class StudentController {
@Autowired({ clazz: StudentServiceImpl })
service: StudentService;
//页面渲染
@Get()
async index() {
return ['index.ejs', { msg: 'hello world' }];
}
//接口返回
@Get('/getStudentInfo/:id')
@ResponseBody()
async getStudentInfo(
@PathVariable('id') id: string,
@RequestParam('name') name: string
) {
return { id, name };
}
@Get()
@ResponseBody()
async addSessionName(@Param('session') session: any) {
session['name'] = 'xiaoAi';
return { msg: 'add success!' };
}
}
4.如何使用中间件
//定义中间件1
@Component()
class XiaoAiMustBeExist implements ExpressMiddleWare {
isExpressMidldleWare(): boolean {
return true;
}
invoke(req: any, res: any, next: Function): void {
if(! req.session?.name){
throw `xiaoai must be exist!`
}
next();
}
}
//定义中间件2
@Component()
class OtherMiddleWare implements ExpressMiddleWare {...}
@Controller('xiaoai')
@ApiMiddleWare([XiaoAiMustBeExist])
export class XiaoAiController {
@Get()
@ResponseBody()
@MiddleWare([OtherMiddleWare])
async getXiaoAiName(@SessionAttribute('name') name:string){
return {name}
}
}
- 使用 ExpressMiddleWare 接口创建中间件.
- 使用@ApiMiddleWare 添加中间件到控制器的类上,可以作用于该控制器所有的方法。(常用如拦截器)
- 使用@MiddleWare 也可以将中间件单独添加到方法上。
- @ApiMiddleWare + @MiddleWare 可以混合使用,执行顺序以定义顺序为准。
总结
到这里 j-spring-web 就完成了,因为底层还是 express,所以运行的还是相当稳定的。
j-spring-web 包含了的优点以及优化了不足。
- 简单高效,并且具有函数式的美感。(express)
- 生态丰富,拥有大量高质量的插件! (express)
- 框架稳定,几乎没有坑。(express)
- 支持 IOC 和 AOP (j-spring)
- 支持模块化 (j-spring)
- 代码层面有美感
- 业务抽象描述能力强