j-spring 注解的实现及类信息的收集(2)

107 阅读3分钟

源码:j-spring

1.注解和装饰器的区别?

注解这个东西在TS里面叫做装饰器,不过做java叫习惯注解了也懒得改了。

其实java的注解和TS的装饰器还是有本质的区别的,前者是附着于基础信息上面并且可以设置在运行时拿到,而TS的装饰器实际上就是语法糖,返回一个函数用来对附着的属性进行增强,运行期间是拿不到的。

2.如何用装饰器模拟注解

装饰器是用来修饰对象信息的,在程序运行的时候就会主动运行。实际上就类似于做了一次扫描。只要掌握扫描的规律,再加上一些辅助信息就可以完整的描述对象的元信息。

2.1装饰器的执行顺序

@Component
class A {
    @Autowired
    field:any
    @Transit
    method(@RequestBody param:string){
    }
}

上面代码是一个带有装饰器修饰的类,经过观察编译后的代码和官网求证,得出以下顺序。

1.参数 2.方法 3.字段 4.类

只要我们在装饰器的工厂函数上面下功夫,就可以对A类进行完整的描述。方便后期进行装配工作。

2.2装饰器工厂的实现和作用

1.快速创建自定义注解。

2.将注解信息和描述关于类的信息放入spring容器中。

export function classAnnotationGenerator(annoName:string,param?:AnnoParam,ref?:Function):Function{
    return function temp<T extends Clazz>(constructor:T) {

        triggerClassAnnotation(constructor,new Anntation(annoName,ref?ref:temp,param));
    
        return constructor;
    
    } 
}

export function fieldAnnotationGenerator(annoName:string,param:AnnoParam,ref?:Function):Function{
    return function temp(_target:any,key:string){
        triggerFieldAnnotation(key,new Anntation(annoName,ref?ref:temp,param))
    }
}


export function methodAnnotationGenerator(annoName:string,param:AnnoParam,ref?:Function):Function{
    return function temp(_target: any, name: string, _descriptor: PropertyDescriptor){
        triggerMethodAnnotation(name,new Anntation(annoName,ref?ref:temp,param));
    }
}

export function paramterAnnotationGenerator(annoName:string,paramterName:string,param:AnnoParam,ref?:Function):Function{
    return function temp(_target: Object, _methodName: string, index: number){
        triggerParamterAnnotation(paramterName,index,new Anntation(annoName,ref?ref:temp,param));
    }
}

这里编写了4个装饰器的工厂函数,用于对注解信息的描述。 下列4个函数用于将获得注解信息和类描述信息放入spring容器。标注的顺序代表解析一个对象的顺序。当触发triggerClassAnnotation函数表示已经完整的扫描完一个类,就可以创建一个BeanDefine对象了。

  • 1.triggerParamterAnnotation
  • 2.triggerMethodAnnotation
  • 3.triggerFieldAnnotation
  • 4.triggerClassAnnotation

这里我们用一个临时对象来方便进行操作,源码详见SwapBeanDefine类。这个对象会一直收集注解和类的元数据信息,直到触发triggerClassAnnotation函数,就会把完整的信息赋值给一个BeanDefine,并且存入spring容器的BeanDefine集合中。

export function triggerClassAnnotation(clazz:Clazz,annotation:Anntation){
    printIndex('class-annotation',clazz,annotation)
    const bd = swapBeanDefine.copy(new BeanDefine(clazz,annotation));
    //添加到bd定义集合
    beanDefineList.add(bd);
}

2.3单元测试验证

源码:/test/annotation.test.ts

  • 这里的SpringContainer抽象类可以获取spring的容器信息
  • spring.launch(ApiController) 代表装配ApiController这个类,成功后调用main方法。
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),
                                'param-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'}],
                        'param-list':[
                            {
                                name:'user',
                                index:0,
                                'anno-list':[Query]
                            }
                        ]
                    },
                    
                ]
            }
        ])

    })

});

总结

  • j-spring利用装饰器工厂可以快速的创建自定义注解,再加上利用扫描的顺序完成了BeanDefine信息的收集。
  • 相对于java来说就是启动速度极快,不需要扫描源码等阻塞操作。缺点就是信息获取的不完整,例如无法获取字段的类型,方法的返回类型,参数的名称和类型(PS:es7的反射就是鸡肋,弃用)。
  • 互有利弊,取长补短,不断进步,方得始终。