1.1.2 • Published 1 year ago

j-spring-web v1.1.2

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

源码: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)
  • 代码层面有美感
  • 业务抽象描述能力强