【如何让代码变“高级”(一)】-Spring组合注解提升代码维度(这么有趣)

简介: 在定义某个类或接口时,使用了Spring自带的注解(@Controller、@Service,@Conditional),同时又要使用公司特定的注解标注公司的业务,接着就出现了以下处理方式的那一幕。

开发中这样的代码


对于每个开发人员都会遇到这样情况,代码如下:


@Api(tags = "自定义组合注解", description = "组合注解优化代码")
@StandardResult
@RequestMapping("/Ccww")
@Controller
@ResponseBody
public class CombinationController{
}
复制代码


在定义某个类或接口时,使用了Spring自带的注解(@Controller、@Service,@Conditional),同时又要使用公司特定的注解标注公司的业务,接着就出现了以下处理方式的那一幕。


“普通”开发人员


对于一般开发人员来说,只要功能、需求达到即可,代码也差不多就可以了,生活也就那样。只要不出现BUG,测试人员,产品经理不找,万事大吉了,不用加班准备开溜陪女朋友了真的有吗?😭)!!!


生活就像心电图一样,一帆风顺就证明挂了,

因此我们需要一颗折腾的心


高级”开发员(BUG工程师)


对于我们这类 “高级”开发员(BUG工程师) ,看到这样的代码,一个类上声明了五六个注解,长长的一大串注解,代码看起来就很普通,体现不出我们“高级”开发员(BUG工程师)折腾的心💗,BUG创造师的水平。


而且注解本意是为了提供我们便捷,代码优化,作标注用。但和XML一样,过度使用就编程了一种灾难。


于是我们持着一颗折腾的心💗,寻找一种可以让自己看来舒服的整洁的代码修正,现在找到了使用组合注解的方式把需要的注解组合在一个自定义的组合注解上。


"高级”开发人员(BUG工程师)-基于组合注解方案


首先我们持着一个折腾的心💗,想到平时我们常见的SpringBoot的启动注解@SpringBootApplication


为什么它一个注解可以实现那么多功能呢?

怎么实现的呢?

我们怎么才能在将这个实现转化出自己所需的自定义注解呢?


接下来我们看看它的源码:


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  @AliasFor(annotation = EnableAutoConfiguration.class)
  Class<?>[] exclude() default {};
  @AliasFor(annotation = EnableAutoConfiguration.class)
  String[] excludeName() default {};
  @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
  String[] scanBasePackages() default {};
  @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
  Class<?>[] scanBasePackageClasses() default {};
}
复制代码


@SpringBootApplication功能列表:


  • 声明@Inherited注解,声明了它的类的子类是可以继承它的
  • 声明@SpringBootConfiguration注解,可为类标注为配置类
  • 声明@EnableAutoConfiguration注解,声明了它的类会默认开启自动配置
  • 声明@ComponentScan注解,同时是@ComponentScan注解的容器。我们发现scanBasePackagesscanBasePackageClasses两个注解属性上面同样声明了@AliasFor注解,分别指向了@ComponentScan注解的basePackages注解属性和basePackageClasses属性。
  • 声明了exclude()为排除特定的自动配置类以及excludeName()排除特定的自动配置类名称


疑问??


  • 这时我们会想@SpringBootApplication为什么可以这样使用呢?
  • 为什么可以继承?
  • 我们定义的普通类和接口有什么区别?
  • @AliasFor 为什么可以这样使用?
  • ....


其实,基于@Retention的值可以分两时期对注解进行处理,:


  1. 编译时处理
  2. 运行时处理


@Retention的值为RetentionPolicy.SOURCE时注解只存在.java源文件中

@Retention的值为RetentionPolicy.CLASS时注解只存在于.class文件中,


都无法在运行时去获取注解的信息,只能在编译时处理


设定为RetentionPolicy.RUNTIME注解信息会存在.class文件中


通知单JVM加载.class文件时会把注解也加载到JVM中,所以就可在运行时获取注解的信息


运行时处理:运行时处理是通过反射机制获取注解


再看看最原始的注解(完全由元注解组成的):


public @Interface Annotation
{
}
复制代码


创建注解使用@Interface类型声明为注解,并且声明为@Interface类型的类在编译时会自动继承Annotation接口。


那什么类型的类可以继承接口呢?


自然是接口类型了,对编译器而言,注解其实就是一个接口。所以,我们在某个类上声明注解本质上等价于继承注解代表的接口。因为注解本质上是接口,就具有了相互继承的能力了。


还有(心想,这问题少年,那么多问题),在注解里声明的注解方法以及给注解属性赋值怎么处理的呢?


对了,就是JDK动态代理!编译器把我们的类翻译为实现了注解接口的类,然后用JDK动态代理的方式拦截所有该类方法的调用,如果是自定义方法就放行;如果是注解方法就拦截下来执行某些逻辑。至于我们给注解属性赋值,会以JDK动态代理类的常量的方式保存,需要时使用就可以了。我们可以看看代理注解的类是怎样的,具体的可以实现跟踪源码看看


最后,@AliasFor的作用是什么呢?


  • 用到注解 属性上,表示两个属性互相为别名,互相为别名的属性值必须相同,若设置成不同,则会报错
  • 注解是可以继承的,但是注解是不能继承父注解的属性的,也就是说,我在类扫描的时候,拿到的注解的属性值,依然是父注解的属性值,而不是你定义的注解的属性值,所以此时可以在子注解对应的属性上加上@AliasFor


手动起来,动手优化


现在该发挥我们程序员的核心本领之一“仿写”,有什么是我们不能仿写出来的呢?哈哈( 程序员的傲娇😏 ),话不多说,撸起袖子,开码,仿写@SpringBootApplication注解:


/**
*自定义组合注解
*@author Ccww
*
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
@RequestMapping
@Api
public @interface StandardResultRestControllerApi
{
  /**
    * 定义映射路径URL
  * @return
    */
   @AliasFor(annotation = RequestMapping.class, value = "path")
   String[] value() default {};
    /**
    *定义spring类名称
    *@return
    */
    @AliasFor(annotation = Controller.class, value = "value")
    String name() default "";
    /**
    *定义Api类tags属性
    *@return
    */
    @AliasFor(annotation = Api.class, attribute = "tags")
    String[] tags() default "";
    /**
    *定义Api类description属性
    *@return
    */
    @AliasFor(annotation = Api.class, attribute = "description")
    String description() default "";
}
复制代码


优化后的代码如下


@StandardResultRestControllerApi(path="/Combination",tags = "自定义组合注解", description = "组合注解优化代码")
public class CombinationController
{
}
复制代码


总结


经过优化后,整个类看起来轻简了很多。以后碰到类似情况,可以考虑使用组合注解来进行优化, 将多个注解作用于一个注解,用一个注解就可以来实现那多个注解的功能,使作用的元素(即方法或类等)看上去更简洁美观,当然主要还是更强大的属性覆盖功能。


但是也要视情况而定,不要盲目的使用组合注解,不然别人看起这代码就比较费劲了哈!!( “高级”程序员的微笑🙂 )


温馨提示:不要盲目的使用组合注解


优点:


  • 代码整齐


缺点:


  • 代码可读性比较差,想使用必须理解其原意


各位看官还可以吗?喜欢的话,动动手指点个💗,点个关注呗!!谢谢支持!



目录
相关文章
|
26天前
|
Java 开发者 Spring
【SpringBoot 异步魔法】@Async 注解:揭秘 SpringBoot 中异步方法的终极奥秘!
【8月更文挑战第25天】异步编程对于提升软件应用的性能至关重要,尤其是在高并发环境下。Spring Boot 通过 `@Async` 注解简化了异步方法的实现。本文详细介绍了 `@Async` 的基本用法及配置步骤,并提供了示例代码展示如何在 Spring Boot 项目中创建与管理异步任务,包括自定义线程池、使用 `CompletableFuture` 处理结果及异常情况,帮助开发者更好地理解和运用这一关键特性。
104 1
|
23天前
|
缓存 Java 数据库连接
Spring Boot奇迹时刻:@PostConstruct注解如何成为应用初始化的关键先生?
【8月更文挑战第29天】作为一名Java开发工程师,我一直对Spring Boot的便捷性和灵活性着迷。本文将深入探讨@PostConstruct注解在Spring Boot中的应用场景,展示其在资源加载、数据初始化及第三方库初始化等方面的作用。
44 0
|
7天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
1月前
|
Java 数据安全/隐私保护 Spring
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
|
20天前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
30 0
|
20天前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
25 0
|
22天前
|
监控 安全 Java
【开发者必备】Spring Boot中自定义注解与处理器的神奇魔力:一键解锁代码新高度!
【8月更文挑战第29天】本文介绍如何在Spring Boot中利用自定义注解与处理器增强应用功能。通过定义如`@CustomProcessor`注解并结合`BeanPostProcessor`实现特定逻辑处理,如业务逻辑封装、配置管理及元数据分析等,从而提升代码整洁度与可维护性。文章详细展示了从注解定义、处理器编写到实际应用的具体步骤,并提供了实战案例,帮助开发者更好地理解和运用这一强大特性,以实现代码的高效组织与优化。
34 0
|
23天前
|
Java 开发者 Spring
Spring Boot大法好:解耦、隔离、异步,让代码‘活’起来,性能飙升的秘密武器!
【8月更文挑战第29天】解耦、隔离与异步是Spring Boot中的关键设计原则,能大幅提升软件的可维护性、扩展性和性能。本文通过示例代码详细探讨了这些原则的应用:依赖注入和面向接口编程实现解耦;模块化设计与配置文件实现隔离;`@Async`注解和`CompletableFuture`实现异步处理。综合运用这些原则,可以显著提升软件质量和性能,使系统更加健壮、灵活和高效。
22 0
|
30天前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
2月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。