Spring注解 一、简介 spring
为了减少过多xml
的配置,减轻开发者的配置负担,提供了通过注解的方式进行bean
对象的加载。并且在单一注解的基础上增加了一些模式上的约定。
比如@Component
、@Service
、@Controller
、@Repository
,实际上它们的作用都是将标记的类生成对应的bean
对象之后添加到IOC
容器中,通过精细注解的方式,为注解带来一些语义。使得可以更直观的看出一些分层行为。再比如springboot
中扩展了spring
的@Condition
注解,通过@OnXXXConditon
的方式标记某个类是否需要进行加载到IOC
容器。
二、注解 在注解上标记的注解称为元注解。比如在@Service
注解之上标记@Component
注解。使得@Service
拥有将标记类加载为Bean
对象后添加到IOC
容器中的能力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { @AliasFor(annotation = Component.class) String value () default "" ; }
2.1 AliasFor 别名注解,在注解创建时,有的时候为了加深语义以及向前兼容的原则,可以通过别名的方式进行创建新字段。比如以下例子,value
和path
互为别名。在日常使用中path
更能体现访问路径的语义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { String name () default "" ; @AliasFor("path") String[] value() default {}; @AliasFor("value") String[] path() default {}; }
2.1.1 使用 在使用上基本上是标记两个注解属性互为别名,但若使用不当,仅在一方上标记为另一方的别名,这时实际上效果和互相标记一致,只是这样就降低了可读性,所以为了提高可读性和隐式别名带来的值覆盖,还是应该遵守规范,进行相互标记。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface AliaForAnnotation { @AliasFor(value = "path") String value () default "" ; String path () default "" ; } public static void main (String[] args) { AliaForAnnotation aliaForAnnotation = AnnotationUtils.getAnnotation(SpringMvcStart.class, AliaForAnnotation.class); System.out.println("value:" + aliaForAnnotation.value()); System.out.println("path:" + aliaForAnnotation.path()); }
在spring
中除了在一个注解中相互标记互为注解之外,还可以对组合注解进行相互标记,同时别名具有传递性。
显示标记:标记一个注解中的两个注解属性互为别名
隐式标记:A
注解的a
属性标记为C
注解的c
的别名,B
注解的b
属性标记为C
注解的c
的别名,那么可以得出A
注解的a
属性也是B
注解的b
属性的别名。
在PostMapping
注解中,name
属性就是RequestMapping
注解中name
的别名,value
属性就是RequestMapping
注解中value
的别名,所以当在声明@PostMapping(value = "/xx/x")
的时候,不论是直接获取PostMapping
注解后获取value
值,还是通过元注解获取RequestMapping
后获取它的value
值,都为/xx/x
。在更为复杂的场景下,不会别名链有多长,若存在隐式标记都可能存在值的传递性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.POST) public @interface PostMapping { @AliasFor(annotation = RequestMapping.class) String name () default "" ; @AliasFor(annotation = RequestMapping.class) String[] value() default {}; }
2.2 Conditional 在spring
中提供了条件注解@Conditional
,用于判断在某种场景下是否需要加载某个类为Bean
对象。在springboot
中将其扩展之后,使用上更为方便。
2.2.1 使用 创建一个注解ConditionalOnDev
,且使用@Conditional
指定条件类为DevCondition
。当ConditionalOnEnviroment
配置类被加载,要创建enviroment
对象之前,会进入条件类的matches
方法,若该方法返回true
则表示允许将这个bean
对象添加到IOC
容器中,反之不处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(DevCondition.class) public @interface ConditionalOnDev { String value () default "" ; } public class DevCondition implements Condition { @Override public boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) { return true ; } } @Configuration public class ConditionalOnEnviroment { @Bean @ConditionalOnDev(value = "dev") public Environment enviroment () { return new Dev(); } }
通过以上示例,若对springboot
有所了解,应该知道springboot
中存在大量的@OnXxxCondition
,它就是利用spring
提供的条件控制注解的方式。比如在springboot
中有@OnBeanCondition
、@OnClassCondition
、@OnResourceCondition
等等。