SpringBoot相关注解详细使用

Lou.Chen
大约 7 分钟

SpringBoot相关注解详细使用

1. @SpringBootApplication 启动类

SpringBootApplication包含以下注解

1.1 @ComponentScan

@Service,@Repository,@Component,@Controller用来定义一个bean.@ComponentScan注解就是用来自动扫描被这些注解标识的类,最终生成ioc容器里的bean.可以通过设置@ComponentScan(basePackages,includeFilters,excludeFilters)属性来动态确定自动扫描范围,类型以及不扫描的类型.

默认情况下:它扫描所有类型,并且扫描范围是@ComponentScan注解所在配置类包及子包的类

总结:

	使用@SpringBootApplication注解,就说明你使用了@ComponentScan的默认配置,这就建议你把使用@SpringBootApplication注解的类放置在root package(官方表述)下,其他类都置在root package的子包里面,这样bean就不会被漏扫描.
1.2 @SpringBootConfiguration

​ 这个注解的作用与@Configuration作用相同,都是用来声明当前类是一个配置类.可以通过@Bean注解生成IOC容器管理的bean.

例如,启动类:

@SpringBootApplication
public class QuickStartApplication {
    public static void  main(String[]args){
        SpringApplication.run(QuickStartApplication.class,args);
    }
    //直接生成ioc容器管理的bean
    @Bean
     public BeanTest beanTest(){
         return new BeanTest();
     }
}
1.3 @EnableAutoConfiguration

@EnableAutoConfiguration是springboot实现自动化配置的核心注解,通过这个注解把spring应用所需的bean注入容器中.@EnableAutoConfiguration源码通过@Import注入了一个ImportSelector的实现类 AutoConfigurationImportSelector,这个ImportSelector最终实现根据我们的配置,动态加载所需的bean.

AutoConfigurationImportSelector的完成动态加载实现方法源码如下:

@Override
   //annotationMetadata 是@import所用在的注解.这里指定是@EnableAutoConfiguration
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
     //加载XXConfiguration的元数据信息(包含了某些类被生成bean条件),继续跟进这个方法调用,就会发现加载的是:spring-boot-autoconfigure jar包里面META-INF的spring-autoconfigure-metadata.properties
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
            .loadMetadata(this.beanClassLoader);
      //获取注解里设置的属性,在@SpringBootApplication设置的exclude,excludeName属性值,其实就是设置@EnableAutoConfiguration的这两个属性值
       AnnotationAttributes attributes = getAttributes(annotationMetadata);
       //从spring-boot-autoconfigure jar包里面META-INF/spring.factories加载配置类的名称,打开这个文件发现里面包含了springboot框架提供的所有配置类
    List<String> configurations = getCandidateConfigurations(annotationMetadata,
            attributes);
     //去掉重复项
    configurations = removeDuplicates(configurations);
     //获取自己配置不需要生成bean的class
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    //校验被exclude的类是否都是springboot自动化配置里的类,如果存在抛出异常
    checkExcludedClasses(configurations, exclusions);
   //删除被exclude掉的类
    configurations.removeAll(exclusions);
   //过滤刷选,满足OnClassCondition的类
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
//返回需要注入的bean的类路径
    return StringUtils.toStringArray(configurations);
}

总结:

springboot是通过注解@EnableAutoConfiguration的方式,去查找,过滤,加载所需的configuration

@ComponentScan扫描我们自定义的bean@SpringBootConfiguration可以简单的理解为就是一个@Configuration注解,表示当前类是一个配置类,通过@Configuration@Bean结合,将Bean注册到Spring ioc 容器。因此@SpringBootApplication的作用等价于同时组合使用@EnableAutoConfiguration,@ComponentScan,@SpringBootConfiguration.

2. @RestControllerAdvice 统一异常处理

返回统一处理异常 json处理的字符串

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
    @AliasFor("basePackages")  //设置要扫描的包  一般为控制器所在的包
    String[] value() default {};
    @AliasFor("value")
    String[] basePackages() default {};
    Class<?>[] basePackageClasses() default {};
    Class<?>[] assignableTypes() default {};
    Class<? extends Annotation>[] annotations() default {};
}
2.1 @ControllerAdvice

https://blog.csdn.net/zxfryp909012366/article/details/82955259open in new window

返回非json处理的字符串

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
    @AliasFor("basePackages")
    String[] value() default {};
    @AliasFor("value")
    String[] basePackages() default {};
    Class<?>[] basePackageClasses() default {};
    Class<?>[] assignableTypes() default {};
    Class<? extends Annotation>[] annotations() default {};
}

3. 自动注入属性

baidu:
  api:
    APP_ID: 17757379
    API_KEY: QkqPC4AHa7ZCqVMGrWCvQ5Ns
    SECRET_KEY: rUIGyeIXpgWomyVASObwgNOgqc5fD9j9

3.1 @ConfigurationProperties

需要注入到IOC容器

@Getter
@Setter
//将组装的属性注入到ioc中  无需开启自动注入
@Configuration
@ConfigurationProperties(prefix = "baidu.api")  
public class BaiduProperties {
    private String APP_ID;
    private String API_KEY;
    private String SECRET_KEY;
}
3.2 @EnableConfigurationProperties
@Getter
@Setter
//此时未注入ioc
@ConfigurationProperties(prefix = "baidu.api")  
public class BaiduProperties {
    private String APP_ID;
    private String API_KEY;
    private String SECRET_KEY;
}
@SpringBootApplication
//开启属性配置
@EnableConfigurationProperties(BaiduProperties.class) 
public class WechartSpringBoot {
    public static void main(String[] args) {
        SpringApplication.run(WechartSpringBoot.class);
    }
}
3.3 @Value("${name:默认值}")

注意:在@Value注入属性的时候,该类必须注册到IOC中才能获取到该值

属性上加@value可不用定义该属性的get,set方法,否则要加get,set方法

#  阿里云短信配置
aliyun:
  sms:
    expireTime: 1
    limitTimes: 10
@RestController
public class MatchUserController {
	//短信过期时间 默认1分钟
    @Value("${aliyun.sms.expireTime:1}")
    private long expireTime;
    //短信获取上限 默认一天10次
    @Value("${aliyun.sms.limitTimes:10}")
    private Integer limitTimes;
}

4. @Enable*

功能:开启某方面的支持

@EnableScheduling 开启计划任务的支持

@EnableAsync 开启异步方法的支持

@EnableAspectJAutoProxy 开启对 AspectJ 代理的支持

@EnableTransactionManagement 开启对事务的支持

@EnableCaching 开启对注解式缓存的支持

所有@Enable 注解都是有@Import的组合注解,@Enable 自动开启的实现其实就是导入了一些自动配置的Bean**

4.1 示例@EnableScheduling原理
4.1.2 直接导入配置类(@Configuration 类)
  @Target(ElementType.TYPE)
  @Retention(RetentionPolicy.RUNTIME)
  @Import(SchedulingConfiguration.class)
  @Documented
  public @interface EnableScheduling {
  }
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
 @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
     return new ScheduledAnnotationBeanPostProcessor();
 }
}
4.1.3 依据条件选择配置类(实现 ImportSelector 接口)

作用:配置类不确定,需根据@Import注解所标识的类或另一个注解(通常是注解)里的定义信息选择配置类

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

 private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
         "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

 @Override
 @Nullable
 public String[] selectImports(AdviceMode adviceMode) {
     switch (adviceMode) {
         case PROXY:
             return new String[] {ProxyAsyncConfiguration.class.getName()};
         case ASPECTJ:
             return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
         default:
             return null;
     }
 }
}
4.1.4 动态注册Bean(实现 ImportBeanDefinitionRegistrar 接口)

​ 如果注入的bean不是确定类或者不是spring专用的(一般确切知道注入某个类的话,可以用@Component,@Service,@Repository,@Bean等),并不想用spring的注解进行侵入式标识,那么就可以通过@Import注解,实现ImportBeanDefinitionRegistrar接口来动态注册Bean

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

 boolean proxyTargetClass() default false;
 
 boolean exposeProxy() default false;

}

​ 关键点在于 AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口。ImportBeanDefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类。例子:Mybatis 中大名鼎鼎的@MapperScan

5.导入文件

5.1 @PropertySource 导入*.properties文件
@Configuration
@PropertySource("classpath:person.properties")
@ConfigurationProperties(prefix = "person")
@Getter
@Setter
public class Person {
    private String name;
    private Integer age;
    private String gender;
}

person.properties:

person.name=张三
person.age=10
person.gender=
5.2 @ImportResource("classpath:cd-config.xml") 导入xml
public class HelloController {
    public String sayHello() {
        return "say hello";
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean class="org.lc.xml.controller.HelloController" id="sayHello"></bean>
</beans>
@Configuration
//加载bean.xml配置文件,注入到spring容器中
@ImportResource(locations = "classpath:beans.xml")
public class WebMvcConfig {
}

注入使用

@RunWith(SpringRunner.class)
@SpringBootTest
public class XmlTest {
    @Autowired
    private HelloController helloController;
    @Test
    public void Test1() {
        String s = helloController.sayHello();
        System.out.println(s);
    }
}

6.Jackson注解总结

https://blog.csdn.net/blwinner/article/details/98532847open in new window

6.1 @RequestBody部分属性为null的问题

https://blog.csdn.net/qq_41170102/article/details/105905761open in new window

  • 使用@JsonProperty

7.@Configuration中的proxyBeanMethods属性详解

首先引出两个概念:Full 全模式Lite 轻量级模式

  • Full(proxyBeanMethods = true) :proxyBeanMethods参数设置为true时即为:Full 全模式。 该模式下注入容器中的同一个组件无论被取出多少次都是同一个bean实例,即单实例对象,在该模式下SpringBoot每次启动都会判断检查容器中是否存在该组件
  • Lite(proxyBeanMethods = false) :proxyBeanMethods参数设置为false时即为:Lite 轻量级模式。该模式下注入容器中的同一个组件无论被取出多少次都是不同的bean实例,即多实例对象,在该模式下SpringBoot每次启动会跳过检查容器中是否存在该组件
  • 什么时候用Full全模式,什么时候用Lite轻量级模式? 当在你的同一个Configuration配置类中,注入到容器中的bean实例之间有依赖关系时,建议使用Full全模式 当在你的同一个Configuration配置类中,注入到容器中的bean实例之间没有依赖关系时,建议使用Lite轻量级模式,以提高springboot的启动速度和性能

总结

  • Full 全模式,proxyBeanMethods默认是true:使用代理,也就是说该配置类会被代理,直接从IOC容器之中取得bean对象,不会创建新的对象。SpringBoot总会检查这个组件是否在容器中是否存在,保持组件的单实例
  • Lite 轻量级模式,proxyBeanMethods设置为false:每次调用@Bean标注的方法获取到的对象是一个新的bean对象,和之前从IOC容器中获取的不一样,SpringBoot会跳过检查这个组件是否在容器中

实例:

@Data
public class Car {
    private String name;
    private String model;

    public Car() {
    }

    public Car(String name, String model) {
        this.name = name;
        this.model = model;
    }
}
@Data
public class Driver {
    private String name;
    private Integer age;
    private Car car;

    public Driver() {
    }

    public Driver(String name, Integer age, Car car) {
        this.name = name;
        this.age = age;
        this.car = car;
    }
}
@Configuration(proxyBeanMethods = true)
public class MyConfiguration {
    @Bean
    public Car car() {
        return new Car("奥迪","A6L");
    }

    @Bean
    public Driver driver() {
        return new Driver("张三",20,car());
    }
}

启动类:

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
        String[] beanDefinitionNames = run.getBeanDefinitionNames();
        //打印所有容器中的bean
        System.out.println(Arrays.toString(beanDefinitionNames));
        MyConfiguration bean = run.getBean(MyConfiguration.class);
        Car car1 = bean.car();
        Car car2= bean.car();
        System.out.println("是否相等" + (car1 == car2));

        Car car3 = bean.car();
        Car car4 = bean.driver().getCar();
        System.out.println("是否相等" + (car3 == car4));
    }
}
  • proxyBeanMethods = true 时,结果都相等。
  • proxyBeanMethods = true 时,结果都不等。