npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

j-spring

v2.0.42

Published

> Spring for TS

Downloads

6

Readme

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);
});