j-spring
TypeScript icon, indicating that this package has built-in type declarations

2.0.42 • Public • Published

J-SPRING

Spring for TS

运行步骤:

  • 一.注解解析

  • 1.1 主力主要查看 SpringAnnotation.ts 里面 注解的实现 以及 annotation.test.ts 里面对注解的扩展。

  • 1.2 这里有一个难点,就是理解 SpringContext 中的 SwapBeanDefine 类,这个类是用来描述辅助描述 bean 的元(类,方法,参数,字段)信息的。

  • 1.3 还要系统的复习一遍 TS 的注解实现以及反射。

  • 1.4 注解的运行在容器启动之前,所以在检索注解的时候会搜集指定的 bean(后置处理器),供后面容器的运行。

  • 二.日志

  • 1.1 默认启动内置日志,使用 console 打印。配置第三方后,委托给第三方运行.

  • 1.2 调用 spring.loadLogger(op: (r: ResourceOperate) => Logger) 使用第三方日志功能。第三方日志无法使用容器功能,只能通过 ResourceOperate 读取 spring 的配置。

  • 三.spring 工厂

  • 3.1 首先打印所有检索到的配置项(通过@Value 注解触发搜集到的配置信息)

  • 3.2 实例化后置处理器(通过@Component 扫描的实现 BeanPostProcessor 接口的 bean),根据 sort 属性决定运行顺序。

  • 3.3 随后装配手动注入的类(通过 spring.bind|bindList|bindMoudle)

  • 四.容器操作

  • 4.1 通过 launch 运行容器中启动类(该类必须包含 main 方法),找到后直接调用 main 方法。

  • 4.2 通过 getBean 获取容器中的指定类。

  • 4.3 通过 replaceClass 调换容器中的指定类,需要在工厂实例化之前操作

  • 五.模块启动器 starter

  • 5.1 设计原因,解决模块间的启动顺序和依赖关系。 例如模块(web,mysql)需要先启动 mysql 然后启动 web。

  • 5.2 这里存在一个延迟装配的概念,例如 web 模块中可能导入了 mysql 的模块,但是工厂启动时无法像 web 中装配 mysql 的模块,因为 Mysql 的模块需要在 stater 执行后注入到 spring 容器中。所以工厂将这些装配信息(例如 web 缺少 mysql.db 模块)记录到 lazyAutowiredList 中。每当一个启动器执行时,就将未装配的 lazyAutowiredList 信息尝试装配一次!当所有 starter 启动后,仍然存在未装配的记录,则抛出异常‘依赖不满足’。

  • 5.3 spring 调用 invokeStarter 执行异步启动器,启动顺序由手动注入顺序决定。

spring
  .loadConfig(config) //加载配置
  .loadLogger() //设置日志
  .bindModule([springWebModule, springWebConfig, controllerClassList]) //绑定模块
  .invokeStarter();

install

npm install j-spring

Usage

example

  import { spring,Component,Autowired } from "j-spring";

  it('autowired by special interface',()=>{

    interface A {
      v():number;
    }

    @Component()
    class A1 implements A{
      v(): number {
        return 1;
      }
    }

    @Component()
    class A2 implements A{
      v(): number {
        return 2;
      }
    }

    @Component()
    class Application {

      @Autowired<A>({clazz:A1})
      a1:A;

      @Autowired<A>({clazz:A2})
      a2:A;

      main(c:number){
        return this.a1.v()+this.a2.v()+c;
      }
    }

    expect(spring.getBean(Application).main(3)).toBe(6);

  })

inject profile infomation

import { spring,Component,Autowired } from "j-spring";

describe('resource config load test',()=>{

  @Component()
  class Student {
      @Value({path:'student.name',type:String})
      name:String;
      @Value({path:'student.age',type:Number})
      age:20;
      @Value({path:'student.city',type:String})
      city:String

      getMsg(){
          return ''+this.name+this.age+this.city;
      }
  }


  @Component()
  class Application {

      @Value({path:'app.msg',type:String})
      appMsg:string;

      @Autowired({clazz:Student})
      student:Student;

      public main(){
          return this.appMsg+this.student.getMsg();
      }

  }

  it('A simple set value',()=>{

      spring.loadConfig({
        'app.msg':'hello',
        student:{
            name:'lina',
            age:20,
            city:'youda'
        }
      })

      expect(spring.launch(Application)).toEqual(`hello! my name is lina and 20 years old!`)

  })

})

aop

it('test sample Aop',()=>{

    import { spring,BeanPostProcessor,Component } from "j-spring";

    //create Annotation
    const SupperCaseParamter = spring.methodAnnotationGenerator('SupperCaseParamter',{})

    @Component()
    class SupperCaseParamterBeanProcessor implements BeanPostProcessor {
        getSort(): number {
            return 100;
        }
        postProcessBeforeInitialization(bean: any, _beanDefine: BeanDefine): Object {
            return bean;
        }
        postProcessAfterInitialization(bean: any, beanDefine: BeanDefine): Object {
            beanDefine.methodList.filter(m => m.hasAnnotation(SupperCaseParamter)).forEach(m => {

                const method = bean[m.name];

                bean[m.name] = function(...args:any[]){
                    return  method.apply(bean,args.map(a => {
                        return typeof a === 'string' ? (a as string).toUpperCase() : a;
                    }));
                }

            })
            return bean;
        }

    }

    @Component()
    class Application {

        @SupperCaseParamter
        main(name:string){
            return name;
        }

    }

    expect(spring.bind(SupperCaseParamterBeanProcessor).getBean(Application).main('hello')).toEqual('HELLO');

})

costom annotation

import { spring, SpringContainer } from '../src';

//diy annotation
const Controller = (path: string) =>
  spring.classAnnotationGenerator('Controller', { path }, Controller);

const ResfulApi = spring.classAnnotationGenerator('ResfulApi', {});

const Inject = (path: string) =>
  spring.fieldAnnotationGenerator('Inject', { path }, Inject);

const Get = (path: string) =>
  spring.methodAnnotationGenerator('Get', { path }, Get);

const Query = (fieldName: string) =>
  spring.paramterAnnotationGenerator('Query', fieldName, {}, Query);

describe('test custom annotation', () => {
  it('it should be work', () => {
    @Controller('/apiController')
    @ResfulApi
    class ApiController extends SpringContainer {
      @Inject('small pigBank')
      pigBank: String;

      @Get('/say')
      async say(@Query('user') user: string) {
        return user;
      }

      main() {
        let result: any[] = [];

        this.getBeanDefineMap().forEach((_v, k) => {
          const data = {
            class: k.clazz,
            'anno-length': k.annotationList.length,
            'anno-class': k.annotationList.map(a => a.clazz),
            'anno-param-list': k.annotationList.map(a => a.params),
            'field-list': k.fieldList.map(f => {
              return {
                name: f.name,
                'anno-list': f.annotationList.map(a => a.clazz),
                'anno-param-list': f.annotationList.map(a => a.params),
              };
            }),
            'method-list': k.methodList.map(m => {
              return {
                name: m.name,
                'anno-list': m.annotationList.map(m => m.clazz),
                'anno-params': m.annotationList.map(m => m.params),
                'field-list': m.paramterDefineList.map(pb => {
                  return {
                    name: pb.name,
                    index: pb.index,
                    'anno-list': pb.annotationList.map(a => a.clazz),
                  };
                }),
              };
            }),
          };
          result.push(data);
        });

        return result;
      }
    }

    expect(spring.launch(ApiController)).toEqual([
      {
        class: ApiController,
        'anno-length': 2,
        'anno-class': [ResfulApi, Controller],
        'anno-param-list': [{}, { path: '/apiController' }],
        'field-list': [
          {
            name: 'pigBank',
            'anno-list': [Inject],
            'anno-param-list': [{ path: 'small pigBank' }],
          },
        ],
        'method-list': [
          {
            name: 'say',
            'anno-list': [Get],
            'anno-params': [{ path: '/say' }],
            'field-list': [
              {
                name: 'user',
                index: 0,
                'anno-list': [Query],
              },
            ],
          },
        ],
      },
    ]);
  });
});

replace dependence

it('replace autowired class', () => {
  @Component()
  class A {
    value() {
      return 1;
    }
  }

  @Component()
  class A100 extends A {
    value(): number {
      return 100;
    }
  }

  @Component()
  class Application {
    @Autowired({ clazz: A })
    a: A;

    main() {
      return this.a.value();
    }
  }

  expect(spring.replaceClass(A, A100).launch(Application)).toBe(100);
});

Readme

Keywords

Package Sidebar

Install

npm i j-spring

Weekly Downloads

34

Version

2.0.42

License

MIT

Unpacked Size

421 kB

Total Files

27

Last publish

Collaborators

  • 892280082