源码: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的反射就是鸡肋,弃用)。
- 互有利弊,取长补短,不断进步,方得始终。