Springboot注解

346 阅读5分钟

@interface

  • @interface是用来自定义注解类型的,可定义多个参数和默认值,核心参数使用value名称
  • 必须设置@Target来指定Annotation可以应用的范围
  • 应当设置@Retention(RetentionPolicy.RUNTIME)便于运行期读取该Annotation

@Ducumented

将此注解包含在Javadoc中

@Inherited 

允许子类继承父类中的注解

@Target 

用来定义注解应用于什么地方,取值有:

  • CONSTRUCTOR:用于描述构造器
  • FIELD:用于描述域
  • LOCAL_VARIABLE:用于描述局部变量
  • METHOD:用于描述方法
  • PACKAGE:用于描述包
  • PARAMETER:用于描述参数
  • TYPE:用于描述类、接口(包括注解类型) 或enum声明

@Retention

定义该注解在哪一个级别可用,

  • RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
  • RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
  • RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用.

@Import注解的用法

SpringBoot 的 @Import 用于将指定的类实例注入之Spring IOC Container中。

  • 直接导入类
@Configuration
@Import(value={UserServiceImpl.class})
public class Config {
}
  • 直接导入配置类(@configuration)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({RocketAutoConfiguration.class})
public @interface EnableRocketMQ {
}


@Configuration
@EnableConfigurationProperties({RocketProperties.class})
@ConditionalOnBean(annotation = {EnableRocketMQ.class})
public class RocketAutoConfiguration {
    @Resource
    private RocketProperties rocketProperties;
    @Resource
    private Map<String, Object> consumerContainer;
    @Resource
    private RocketSerializer rocketSerializer;

    public RocketAutoConfiguration() {}

    @Bean
    @ConditionalOnMissingBean({RocketConsumerContainer.class})
    public RocketConsumerContainer rocketConsumerContainer() {
        return new RocketConsumerContainer(this.rocketProperties, this.rocketSerializer);
    }
}
  • 依据条件选择配置类(实现 ImportSelector 接口)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({AudioScoreServiceImportSelector.class})
public @interface EnableAudioVoiceScore {
    AudioScoreMode mode() default AudioScoreMode.ALL;
}

public class AudioScoreServiceImportSelector extends AudioScoreModeImportSelector<EnableAudioVoiceScore> {
    public AudioScoreServiceImportSelector() {
    }

    protected String[] selectImports(AudioScoreMode mode) {
        switch(mode) {
        default:
            return null;
        }
    }
}


public abstract class AudioScoreModeImportSelector<A extends Annotation> implements ImportSelector {
    private static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode";

    public AudioScoreModeImportSelector() {
    }

    @NonNull
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Class<?> annType = GenericTypeResolver.resolveTypeArgument(this.getClass(), AudioScoreModeImportSelector.class);
        Assert.state(annType != null, "Unresolvable type argument for AudioScoreModeImportSelector");
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(annType.getName(), false));
        if (attributes == null) {
            throw new IllegalArgumentException(String.format("@%s is not present on importing class '%s' as expected", annType.getSimpleName(), importingClassMetadata.getClassName()));
        } else {
            AudioScoreMode adviceMode = (AudioScoreMode)attributes.getEnum(this.getAdviceModeAttributeName());
            String[] imports = this.selectImports(adviceMode);
            if (imports == null) {
                throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
            } else {
                return imports;
            }
        }
    }

    @Nullable
    protected abstract String[] selectImports(AudioScoreMode mode);

    protected String getAdviceModeAttributeName() {
        return "mode";
    }
}
  • 动态注册Bean(实现 ImportBeanDefinitionRegistrar接口)
package com.example.demo.bean;

public class TestBean4 {
}


public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestBean4.class);
        registry.registerBeanDefinition("TestBean4", rootBeanDefinition);
    }
}


@Import({TestImportBeanDefinitionRegistrar.class})
@Configuration
public class AppConfig {
}

@NotNull

不能为null,但可以为empty

@NotEmpty

不能为null,而且长度必须大于0

@NotBlank

只能作用在String上,不能为null,而且调用trim()后,长度必须大于0

@ConfigurationProperties

  • 使用方式
    • 配合@component注解直接进行注入,只有当类所在的包被 Spring @ComponentScan 注解扫描到才会生效
    @Data
    @Component
    @ConfigurationProperties(prefix = AppLinkConfig.PREFIX)
    public class AppLinkConfig {
    	public static final String PREFIX = "student";
    	private String appLink;
    }
    
    • 把@ConfigurationProperties还可以直接定义在@bean的注解上
    @Bean
    @ConfigurationProperties(prefix = "connection")
    public ConnectionSettings connectionSettings(){
        return new ConnectionSettings();
    }
    
    • 配合@EnableConfigurationProperties,在该注解中其实是用了@Import(EnableConfigurationPropertiesImportSelector.class) 实现,通常配置在@configuration类上,也可用在其他@component派生类上
    @ConfigurationProperties(prefix = "acme")
    @Data
    public class AcmeProperties {
        private boolean enabled;
        private InetAddress remoteAddress;
    }
    
    @Configuration
    @EnableConfigurationProperties({AcmeProperties.class})
    public class AcmeConfigTest {
    }
    
    @RestController
    @RequestMapping("/properties-get")
    public class PropertiesTestController {
    	//此处注入
    	@Autowired
    	private AcmeProperties acmeProperties;
    
        @GetMapping("/address")
        public String getRemoteAddress(){
            return JSON.toJSONString(acmeProperties.getRemoteAddress());
        }
        @GetMapping("/enable")
        public String getEnabled(){
            return JSON.toJSONString(acmeProperties.isEnabled());
        }
    }   
    

@RefreshScope

  • 需要热加载的bean需要加上@RefreshScope注解,当配置发生变更的时候可以在不重启应用的前提下完成bean中相关属性的刷新
  • RefreshScope代理的bean强制为懒加载,只有在第一次使用的时候才会生成实例,当其需要刷新配置的时候直接调用destory()方法销毁当前bean,这样在刷新配置后在需要生成的bean已经是根据新的配置信息生成,完成bean的热加载

@Valid和@Validated

Validated和Valid区别

@RequestBody

  • 注解@RequestParam接收的参数是来自HTTP请求体或请求url的QueryString中
  • @RequestParam用来处理Content-Type为application/x-www-form-urlencoded编码的内容,Content-Type默认为该属性
  • @RequestParam也可用于其它类型的请求,例如:POST、DELETE等请求。

@RequestParam

  • 注解@RequestBody接收的参数是来自requestBody中,即请求体。一般用于处理非 Content-Type: application/x-www-form-urlencoded编码格式的数据,比如:application/json、application/xml等类型的数据
  • 常用于向表中批量插入数据

@EnableConfigurationProperties

  • 使使用@ConfigurationProperties 注解的类生效
  • 如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties配置文件转化的bean。说白了@EnableConfigurationProperties 相当于把使用@ConfigurationProperties 的类进行了一次注入
@ConfigurationProperties(prefix = "service.properties")
public class HelloServiceProperties {
    private static final String SERVICE_NAME = "test-service";

    private String msg = SERVICE_NAME;
       set/get
}


@Configuration
@EnableConfigurationProperties(HelloServiceProperties.class)
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(prefix = "hello", value = "enable", matchIfMissing = true)
public class HelloServiceAutoConfiguration {

}

@RestController
public class ConfigurationPropertiesController {

    @Autowired
    private HelloServiceProperties helloServiceProperties;

    @RequestMapping("/getObjectProperties")
    public Object getObjectProperties () {
        System.out.println(helloServiceProperties.getMsg());
        return myConfigTest.getProperties();
    }
}

@ConditionalOnBean

  • 要求bean存在时,才会创建这个bean;如我提供了一个bean名为RedisOperBean,用于封装redis相关的操作;但是我这个bean需要依赖restTemplate这个bean,只有当应用引入了redis的相关依赖,并存在RestTemplate这个bean的时候,我这个bean才会生效
@Component
@ConditionalOnBean(name="redisTemplate")
public class RedisOperBean {
  private final RedisTemplate redisTemplate;
  public RedisOperBean(RedisTemplate redisTemplate) {
      // ...
  }
}

@DependsOn

  • 该注解用于声明当前bean依赖于另外一个bean。所依赖的bean会被容器确保在当前bean实例化之前被实例化。举例来讲,如果容器通过@DependsOn注解方式定义了bean plant依赖于bean water,那么容器在会确保bean water的实例在实例化bean plant之前完成。

@ConditionalOnMissingBean

  • 结合使用注解@ConditionalOnMissingBean和@Bean,可以做到只有特定名称或者类型的Bean不存在于BeanFactory中时才创建某个Bean
  • 实例:
@Configuration
public class FeignClientsConfiguration {

	@Bean
	@ConditionalOnMissingBean
	public Decoder feignDecoder() {
		return new OptionalDecoder(
				new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
	}

	@Bean
	@ConditionalOnMissingBean
	public Retryer feignRetryer() {
		return Retryer.NEVER_RETRY;
	}

@AllArgsConstructor

  • @AllArgsConstructor是Lombok中的一个注解,其功能是生成一个为类中所有非static变量以及未初始化的final变量进行赋值的构造函数,被@AllArgsConstructor注解的类内可省略@Autowired

@ConditionalOnProperty

ConditionalOnProperty的使用

@ConditionalOnClass

只有在classpath下能找到类才会构建这个bean