Spring组合注解详解(注解合并及别名属性覆盖)

 

Spring组合注解详解(注解合并及别名属性覆盖)

 

 

组合注解


  注解的作用就不用介绍了吧,主要就是用来简化配置,通过自定义注解或者其他框架提供的注解,只要往方法或者类上一加,就可以实现许多神奇的功能。

  spring 4.2之后就提供了组合注解的实现方式,啥是组合注解呢,其实就是将多个注解作用于一个注解,用一个注解就可以来实现那多个注解的功能,使作用的元素(即方法或类等)看上去更简洁美观,当然主要还是更强大的属性覆盖功能。

  举个最常见的组合注解吧,即spring的@RestController,它将@ResponseBody和@Controller两个注解组合为一个,我们在Controller类上只要加@RestController即可实现之前要加两个注解才能实现的功能。代码如:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller  //组合Controller使其实现Bean注册
@ResponseBody  //组合ResponseBody使其支持将结果转化为json
public @interface RestController {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     * @since 4.0.1
     */
    @AliasFor(annotation = Controller.class)
    String value() default "";

}

 


自定义注解(不使用Spring自带的注解)实现组合注解功能


  自定义注解(即自己要实现某些功能但不使用spring提供的一些注解,并非网上大部分那种将spring提供的多个注解作用于一个自己定义的注解然后将该注解作用于元素,让其实现Spring提供的组合功能)时,想使用spring那神奇般的组合注解该如何实现呢。接下来就介绍spring神奇的工具类AnnotatedElementUtils,使用它就可以让自己的注解实现组合注解般的功能。直接上代码:

/**
 * @author meilin.huang
 * @version 1.0
 * @date 2019-06-06 21:11
 */
public class SynthesizedAnnotationTest {

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @interface Test1 {
        String test1() default "test1";
    }

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @interface Test2 {
        String test2() default "test2";
    }

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @Test2
    @interface Test3 {
        String tset3() default "test3";
    }

    /**
     * 只有@Test3注解,但是Test3注解上组合了@Test2注解,故就可以通过Spring的工具类获取到Test2注解的内容,详见main方法
     * 当然也可以将组合注解作用于更高层次,如Test3组合Test2,Test2组合Test1,然后将Test3作用于元素,通过工具类获取Test1注解功能
     */
    @Test3
    static class Element {}

    public static void main(String[] args) {
        Test2 test2 = AnnotatedElementUtils.getMergedAnnotation(Element.class, Test2.class);
        System.out.println(test2);// 输出'@mayfly.sys.common.utils.SynthesizedAnnotationTest$Test2(test2=test2)'
    }
}

 

我们在合并注解的时候,比如注解A中有属性A(),注解B中有属性B(),它们的组合注解C这么写就可以了:

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@A
@B
public @Interface C{   // 合并了A, B注解
  
   String A();  // 自动对应注解A中的A属性
 
   String B();  // 自动对应注解B中的B属性
 
}


组合注解实现属性值覆盖(有点类似子类覆盖父类属性方法)


  如果spring只实现了以上那功能,其实作用也不太大,并且实现也就很简单了,自己便可轻易实现,通过直接查找元素上是否含有该直接注解,没有则遍历该元素其他注解,然后递归遍历查找注解的元注解是否含有该元素,直到找到返回即可。

  接下来就介绍下spring组合注解更强大的属性覆盖功能,即更低层次的注解属性方法覆盖高层次注解的属性方法,啥意思呢,具体见代码就比较清晰明了了,实现该功能还需要spring提供的另外一个注解即@AliasFor配合完成。

/**
 * @author meilin.huang
 * @version 1.0
 * @date 2019-06-06 21:11
 */
public class SynthesizedAnnotationTest {

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @interface Test1 {
        String test1() default "test1";
    }

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @interface Test2 {
        String test2() default "test2";
    }

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @Test2
    @interface Test3 {
        /**
         * AliasFor注解用来表示要覆盖Test2注解中的test2()属性方法,
         * annotation属性声明的注解类必须存在于该注解的元注解上
         * attribute属性声明的值必须存在于Test2注解属性方法中(即Test2注解的test2方法)
         */
        @AliasFor(annotation = Test2.class, attribute = "test2")
        String test3() default "test3";
    }

    /**
     * 只有@Test3注解,但是Test3注解上组合了@Test2注解,并将该注解的test3方法值用来覆盖Test2注解中的test2方法
     * 即更低层次声明的覆盖规则,会覆盖更高层次的属性方法值,即调用高层次的注解方法值实际显示的是低层所赋的值
     * 当然也可以将组合注解作用于更高层次,如Test3组合Test2,Test2组合Test1,然后将Test3作用于元素,通过工具类获取Test1注解覆盖的属性值
     */
    @Test3(test3 = "覆盖Test2属性中的test2方法")
    static class Element {}

    public static void main(String[] args) {
        Test2 test2 = AnnotatedElementUtils.getMergedAnnotation(Element.class, Test2.class);
        // 虽然调用了Test2注解的test2方法,但是实际显示的是Test3注解中的test3属性声明的值
        // 则说明Test2的test2属性被覆盖了 
        System.out.println(test2.test2());// out '覆盖Test2属性中的test2方法'
    }
}


  以上就是属性覆盖的最简单两层覆盖,当然原则上是可以支持无限层覆盖的,但是用法都是一致的。实现该功能的主要原理其实就是通过jdk的动态代理。具体实现方式有兴趣的可以参考AnnotatedElementUtils工具类的实现细节。

  那么组合注解有啥用呢,其实个人感觉用处是非常大的可以不改变原注解的代码,就可以定义新注解,并通过覆盖原则来覆盖原注解的一些属性值来实现更多的其他功能扩展,也不会影响原注解的使用。Spring的大量注解都使用这些原则,随便翻翻源码注解随处可见的都是这类组合注解。当然更多的用处还是需要根据自己的需求自己发挥啦,哈哈哈。

  我参考了spring该工具类的源码,大大简化了其实现方式(毕竟spring考虑的比较周到,代码比较复杂),但是在能实现其最基本功能的原则上个人的实现感觉也是够用了,且性能是spring的两倍。具体代码在本人的项目[ https://gitee.com/objs/mayfly ]的AnnotationUtils里,有兴趣的可以参考参考该实现方式蛤,多提宝贵意见~~~

 

 

Spring组合注解的神奇实用功能详解(功能组合以及别名属性覆盖)_mayfly_hml的博客-CSDN博客
https://blog.csdn.net/mayfly_hml/article/details/91070465

 

 

Spring源码---组合注解/合并注解的问题_小雨的光的博客-CSDN博客
https://blog.csdn.net/qq_28802119/article/details/83573950

  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring是一个Java开发框架,提供了许多注解来简化开发。下面是Spring中常用的注解详解: 1. @Autowired:自动装配,用于自动注入依赖项。 2. @Component:用于标记一个类为Spring容器管理的组件。 3. @Controller:用于标记一个类为Spring MVC控制器。 4. @Service:用于标记一个类为服务层组件。 5. @Repository:用于标记一个类为数据访问层组件。 6. @RequestMapping:用于映射请求到处理程序方法。 7. @PathVariable:用于获取请求URL中的变量值。 8. @RequestParam:用于获取请求参数的值。 9. @ResponseBody:用于将返回值直接写入响应体中。 10. @RestController:用于标记一个类为Spring MVC控制器,并且默认所有方法都返回JSON格式的数据。 11. @Configuration:用于标记一个类为配置类,相当于一个XML配置文件。 12. @Bean:用于在配置类中声明一个Bean。 13. @Value:用于获取配置文件中的属性值。 14. @Qualifier:用于指定自动装配时使用的Bean名称。 15. @Scope:用于指定Bean的作用域,例如singleton、prototype等。 16. @Transactional:用于标记一个方法为事务方法。 17. @Async:用于标记一个方法为异步方法。 18. @PostConstruct:用于标记一个方法为Bean初始化方法。 19. @PreDestroy:用于标记一个方法为Bean销毁方法。 以上是Spring中常见的注解详解,使用这些注解可以使开发更加简单和高效。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值