全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-08-21_一行注解优化服务层百行@Autowire代码

您的位置:首页 >> 新闻 >> 行业资讯

一行注解优化服务层百行@Autowire代码 点击公众关注号,“技术干货”及时达! @Autowire可以说是我们日常开发中使用最频繁的一个注解了, 相信你在日常开发中一定看到过如下类似的代码: publicclassUserService{ @Autowire privatexxxMapperxxxMapper; @Autowire privatexxx1Mapperxxx1Mapper; @Autowire privatexxxServicexxxService; //...此处省略更多的@Autowire注解的使用 } 事实上,在实际的业务中有时需要注入的属性能达到十几个之多处,也就是说@Autowire在一个类中会频繁的重复出现。 那有没一种方式能让我们不用每次都重复写@Autowire的方式呢?肯定是有的,答案就是今天我们讨论的@RequiredArgsConstructor。 什么是@RequiredArgsConstructor@RequiredArgsConstructor 是 Lombok 提供的注解之一,其主要用于在类上,并为类中标有 final 字段的生成相应的构造方法。具体使用如下所示: @RequiredArgsConstructor publicclassExamplePo{ privatefinalStringname; privatefinalintage privateStringnameCode; publicstaticvoidmain(String[]args){ ExamplePoexamplePo=newExamplePo("name",12); } } 在上述代码中,在Main方法中使用@RequiredArgsConstructor生成了一个构造方法以供我们构建实体ExamplePo。 细心的读者可能已经注意到了在ExamplePo中定义了name、age、nameCode三个字段,但是当我们在Main方法中构建ExamplePo实体对象时,由于nameCode并未使用final修饰,所以仅能根据name和age两个属性来完成构建。 众所周知,final 修饰的字段在声明时必须进行初始化,且一旦被初始化后其值就不能再被修改。 因此为了保证字段的不变性,@RequiredArgsConstructor 会自动生成构造方法时,会为检查类内部被 final 字段修饰的全部变量,并在构造方法中进行初始化。 @RequiredArgsConstructor这样做不仅确保了对象在创建时所有 final 字段都得到正确的初始化,而且一旦初始化后,它们的值不可变。 剖析@RequiredArgsConstructor原理为了能透彻剖析Lombok中@RequiredArgsConstructor 的逻辑,这里我们借助注解处理器(Annotation Processor)来模拟实现对在编译阶段生成和修改字节码,以对Lombok中的@RequiredArgsConstructor的解析进行模拟。 为此,我们首先自定义一个MyRequiredArgsConstructor注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public@interfaceMyRequiredArgsConstructor{ booleanincludeAllFields()defaultfalse; } 然后,我们编写一个MyRequiredArgsConstructorProcessor来对我们自定义的MyRequiredArgsConstructor进行解析。其逻辑如下: //...省略包信息导入 @SupportedAnnotationTypes("com.example.annotation.MyRequiredArgsConstructor") publicclassMyRequiredArgsConstructorProcessorextendsAbstractProcessor{ @Override publicbooleanprocess(Set?extendsTypeElementannotations,RoundEnvironmentroundEnv){ 1遍历Class文件,判断其是否有MyRequiredArgsConstructor注解 for(Elementelement:roundEnv.getElementsAnnotatedWith(MyRequiredArgsConstructor.class)){ if(elementinstanceofTypeElement){ TypeElementtypeElement=(TypeElement)element; 2收集MyRequiredArgsConstructor注解所修饰类中字段信息 ListVariableElementfinalFields=newArrayList(); ListFieldSpecfields=newArrayList(); for(ElementenclosedElement:typeElement.getEnclosedElements()){ if(enclosedElement.getKind()==ElementKind.FIELD){ VariableElementfield=(VariableElement)enclosedElement; finalFields.add(field); FieldSpecfieldSpec=FieldSpec.builder(TypeName.get(field.asType()),field.getSimpleName().toString(),Modifier.PRIVATE,Modifier.FINAL) .build(); fields.add(fieldSpec); } } MethodSpec.BuilderconstructorBuilder=MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC); //3遍历属性内容,生成构造方法 for(VariableElementfield:finalFields){ constructorBuilder.addParameter(TypeName.get(field.asType()),field.getSimpleName().toString()); constructorBuilder.addStatement("this.$N=$N",field.getSimpleName(),field.getSimpleName()); } MethodSpecconstructor=constructorBuilder.build(); //4进行class文件的回写 TypeSpecclassWithConstructor=TypeSpec.classBuilder(String.valueOf(typeElement.getSimpleName())) .addModifiers(Modifier.PUBLIC) .addMethod(constructor) .addFields(fields) .build(); JavaFilejavaFile=JavaFile.builder("com.example.pojo",classWithConstructor) .build(); try{ JavaFileObjectsourceFile=processingEnv.getFiler().createSourceFile("com.example.pojo."+typeElement.getSimpleName()+"WithConstructor"); Writerwriter=sourceFile.openWriter(); javaFile.writeTo(writer); writer.close(); }catch(IOExceptione){ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,e.toString()); } } } returntrue; } } 上述代码的process大致逻辑如下,其首先会对加载类进行逐个遍历,从而找出带有MyRequiredArgsConstructor注解的Class文件。 然后,对目标类文件中的字段信息进行逐个遍历,收集其所有的field字段信息,并存储到finalFields列表中。 最后,依据扫描得到的字段信息创建一个公共的构造函数,构造函数的入参即为类中的所有字段,并在构造函数中将参数赋值给对应的字段。 更进一步,我们将MyRequiredArgsConstructor标注在MyClass上,然后手动调用我们MyRequiredArgsConstructorProcessor进行编译其结果如下图所示。 (注:不熟悉MyRequiredArgsConstructor如何调用的可参考笔者之前的:Java注解能力提升:教你解析保留策略为源码阶段的注解) @MyRequiredArgsConstructor @SuppressWarnings("unused") publicclassMyClass{ privateStringname; privateint privateStringaddress; privateStringoptionalField; } image.png可以看到,我们自定义的MyRequiredArgsConstructorProcessor成功的对标有MyRequiredArgsConstructor类进行了解析,并生为其成了相关的构造器。 至此,我们便通过Jdk提供给我们的AbstractProcessor接口成功地对Lombk中的@RequiredArgsConstructor的解析原理进行了模拟。 (注:Lombok内部在实现对注解解析时其实也是通过继承AbstractProcessor接口,来完成对相关LomBok注解的解析处理,感兴趣的读者可自行对Lombok中的LombokProcessor进行分析) 明白了@RequiredArgsConstructor的用途以及工作原理后,接下来我们便来用@RequiredArgsConstructor来改造我们的@Autowire。 使用@RequiredArgsConstructor减少@Autowire的书写正如我们前面介绍的那样,如果类上加上@RequiredArgsConstructor,那么需要注入的类所需的的关键字段需要通过final声明进行修饰。 为此我们对开头出现的UserService进行改造,具体如下所示: @Service @RequiredArgsConstructor(onConstructor_={@Lazy,@Autowired}) publicclassUserService{ privatefinalxxxMapperxxxMapper; privatefinalxxx1Mapperxxx1Mapper; privatefinalxxxServicexxxService; //...此处省略更多的@Autowire注解的使用 } 到在上述代码中,我们在用到了@RequiredArgsConstructor 注解的onConstructor_ 属性,该属性允许你为生成的构造函数添加额外的注解。 这个属性是一个注解数组,使你可以为Lombok 自动生成的构造函数添加多个注解。因此上述代码最终生成的代码造类似于: publicclassUserService{ privatefinalxxxMapperxxxMapper; privatefinalxxx1Mapperxxx1Mapper; privatefinalxxxServicexxxService; //...此处省略更多的@Autowire注解的使用 @Autowire @Lazy publicUserService(xxxMapperxxxMapper,xxx1Mapperxxx1Mapper, xxxServicexxxService){ //....省略属性注入 } } 笔者在使用@RequiredArgsConstructor的onConstructor_属性中除了使用@Autowire外,还使用了@Lazy注解,这主要目的是为了解决因循环依赖的发生。 因为当 Spring容器遇到 @Lazy 注解时,它不会立即创建该 bean的实例,而是创建一个代理对象。只有当该代理对象第一次被访问时,Spring容器才会创建真正的 bean实例并进行注入。 总结至此,我们就对Lombok中的@RequiredArgsConstructor的原理和使用场景进行了详细的介绍,结合@RequiredArgsConstructor我们完全可以对服务层频繁出现的@Autowire注解进行优化。 点击公众关注号,“技术干货”及时达! 阅读原文

上一篇:2019-11-18_知识图谱从哪里来:实体关系抽取的现状与未来 下一篇:2018-12-14_失去甜甜圈的Dunkin',换了个有故事的新LOGO

TAG标签:

15
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价