0%

Spring注解

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 {

/**
* 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)
*/
@AliasFor(annotation = Component.class)
String value() default "";

}

2.1 AliasFor

别名注解,在注解创建时,有的时候为了加深语义以及向前兼容的原则,可以通过别名的方式进行创建新字段。比如以下例子,valuepath互为别名。在日常使用中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
/**
* @AliasFor 别名注解测试
*
* @Author: xiaocainiaoya
* @Date: 2021/08/30 22:24:08
**/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AliaForAnnotation {
@AliasFor(value = "path")
String value() default "";

//@AliasFor(value = "value")
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中除了在一个注解中相互标记互为注解之外,还可以对组合注解进行相互标记,同时别名具有传递性。

  1. 显示标记:标记一个注解中的两个注解属性互为别名
  2. 隐式标记: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 {

/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class)
String name() default "";

/**
* Alias for {@link RequestMapping#value}.
*/
@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等等。

-------------本文结束感谢您的阅读-------------