1. Spring 罕用注解

1.1 主动拆卸

1.1.1 @Configuration

Spring 3.0之后开始反对了一种更优的配置形式:基于 Java 类的配置形式。通过减少 @Configuration 注解表明这是一个配置类,其底层为 @Component即该类的bean对象也会被Spring容器接管)。

proxybeanMethods 属性作用:指定被@Bean注解润饰的办法是否被代理,true示意该办法被代理(底层cglib,即该办法不能为private、final润饰),每次对该办法进行调用将会间接取得容器中的bean对象,而非真正执行该办法;false示意该办法不被代理,每次调用时都将取得一个新的对象(对应 full 和 lite 模式)。

1.1.2 @Bean

配合 @Configuration 能够实现 XML 配置实现注入的性能,成果与

<beans>    <bean>...</bean>    <bean>...</bean></beans>

相似,在应用 @Bean 注解时,示意将该办法返回的对象加载进 Spring 容器,在应用 @Bean 润饰办法时须要留神:

  1. 办法带返回值,且返回类型为想要被治理的 bean 的类型;
  2. 办法名即为默认的 bean name ,可应用 @Beanname 属性自定义;
  3. 办法可带参数,则此时传入的参数也必须是被 Spring 容器治理的 bean 。

1.1.3 @Value

该注解的作用是从配置文件中读取属性,如 application.yaml 中有个配置

server:        port: 8000

咱们应用该注解润饰办法参数 public void test(@Value("${server.port}") address),则该属性 8000 会主动被注入到 address 变量中。

@Value 定位属性有两种形式:

  • ${property: default_value}:读取配置文件(如 *.properties, *.yaml 中的值
  • #{obj.property?: default_value}:读取容器中治理的 bean 对象的属性值。

假如有一个对象申明如下:

@Componentpublic class ServerProperty {@Value("${server.url}")private String url;// getter and setter// ...}@Componentpublic class Server {    @Value("#{serverProperty.url}")    private String url;}

第一个 @Value 指代从配置文件中读取属性注入到名为 serverProperty 的 bean 对象中,而第二个 @Value 则是从名为 serverProperty 的 bean 对象中读取数据并注入到 server 中。

注:@Value 想设置 null 默认值给 String 类型的变量时需如此 @Value("${server.url: #{null}}")

1.1.4 @ConfigurationProperties

其作用是将 Java 类属性的值与配置文件进行映射,即通过@ConfigurationProperties注解,咱们能够应用 properties 或者 yaml 等配置文件对所有属性值进行治理,再映射加载到 Java 对象中。该注解的 prefix 属性则指定了配置文件中的惟一标识名。

1.1.5 @EnableConfigurationProperties

@ConfigurationProperties注解的作用仅仅是将配置文件的值映射载入到 Java 对象中,但该对象与一般 Java 对象无区别,并没有被 Spring 容器进行治理,@EnableConfigurationProperties的作用则是,使得这些被@ConfigurationProperties润饰的类转为被 Spring 容器治理的bean对象,在@ConfigurationProperties润饰的类上加@Component也能够使得其被 Spring 容器治理。

1.1,6 @Conditional

Spring4.0开始提供了 @Conditional 注解,提供了 if...then...else 的语义来治理 Bean 对象的注册。

应用 @Conditional 注解,须要与一个名为 Condition 的接口进行联合才可能施展成果。Condition 接口只有一个办法matches

public interface Condition {    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);}

这个matches办法里即是对if...then...else语义逻辑的具体代码实现。

@Conditional注解有一个属性value,是一个类数组Class<? extends Condition>[],其逻辑为,当value里所有元素类的macthes办法执行后果均为true时,批准注册Bean对象。

@Conditional可用于润饰类(@Conponent和@Configuration等)或办法(@Bean),并有许多拓展,如

  • @ConditionalOnBean:容器中存在指定 Bean 对象;
  • @ConditionalOnMissingBean:容器中不存在指定 Bean 对象;
  • ...

1.1.7 @Import

该注解用来整合所有在 @Configuration 注解中定义的 bean 配置,作用相似咱们将多个 XML 配置文件导入到单个 XML 文件中。

@Import 联合 ImportSelector 接口,能够实现动静抉择导入的配置类( @Configuration 润饰类)。其应用与 @ConditionalCondition 接口的组合形式有点类似,如 SpringBoot 中正是应用 @Import(AutoConfigurationImportSelector.class) 加载了所有主动配置类。ImportSelector 接口提供如下办法:

public interface ImportSelector {    // ...        String[] selectImports(AnnotationMetadata importingClassMetadata);}

该办法 selectImports 返回的字符串数组,即是 @Import 注解筹备导入的类。

1.1.8 @EnableAutoConfiguration

该注解被@Import(AutoConfigurationImportSelector.class)润饰,指定将要被主动装载到Spring容器中的一系列组件。

1.2 Web

1.2.1 @RequestMapping

将 HTTP 申请映射到该注解润饰的解决办法上,衍生出的注解有:

  • @GetMapping:实质是 @RequestMapping 指定了 GET 办法,接管 HTTP 的 GET 申请;
  • @PostMapping:实质是 @RequestMapping 指定了 POST 办法,接管 HTTP 的 POST 申请;
  • @PutMapping:实质是 @RequestMapping 指定了 PUT 办法,接管 HTTP 的 PUT 申请;
  • @DeleteMapping:实质是 @RequestMapping 指定了 DELETE 办法,接管 HTTP 的 DELETE 申请;
  • ...

1.2.2 @RequestBody

该注解用于读取 Http 申请的申请体数据(当 content-typeapplication/json 时),而后将这部分数据绑定到该注解润饰的办法参数上,在同一个办法里最多只有一个参数可能被 @RequestBody 润饰。

注:无奈配合 GET 申请应用。

1.2.3 @ResponseBody

该注解可润饰办法和其余注解,示意将办法返回的对象转换成肯定格局,并写入响应报文的响应体中,个别用于返回 Json 数据。

1.2.4 RestController

该注解个别用于润饰类,实质是 @Controller + @ResponseBody ,因而聚合了 @Controller 的路由性能和 @ResponseBody 的性能—类上的所有办法都会返回 Json 格局数据。

1.2.5 @RequestParam

该注解用于接管 Http 申请的查问参数,起源可分为两局部:

  • GET 办法的 URL
  • POST 办法(当 content-typeapplication/x-www-form-urlencoded 时)的 form data

1.2.6 @PathVariable

该注解用于解析 URL 模板,如果咱们设计 REST 格调的 URL 如下:

@RequestMapping("xxx.com/items/{id}")public String getItemById(@PathVariable("id") String id) {}

当拜访 "xxx.com/items/100" 时,办法 getItemById 的办法参数能够间接解析失去 "100"。

2. Spring Boot 主动拆卸原理

Spring Boot 在启动时会主动拆卸一系列组件( bean 对象),因为其主动加载这些组件,开发者无需做什么配置即可应用 Spring Boot 提供的性能和服务,且能够在 application.properties 中笼罩默认配置。其实现的原理次要是通过 JavaConfig 和几个上述提到的罕用注解。

2.1 如何进行主动拆卸

Spring Boot 的启动类上有一个组合注解 @SpringBootApplication

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {    // ...}

疏忽Java的元注解之后,次要剩下这三个注解:

  • @SpringBootConfiguration:标注这是一个配置类,实质是一个@Configuration
  • @EnableAutoConfiguration将符合条件的 bean 主动注入到 Spring 容器中
  • @ComponentScan:指定须要扫描包的门路。

主动拆卸的外围次要是通过@EnableAutoConfiguration来实现,该注解的源码如下:

@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {    // ...}

这里有一个 @ImportImportSelector 接口实现类的组合,查看 AutoConfigurationImportSelector.classselectImports 办法:

public String[] selectImports(AnnotationMetadata annotationMetadata) {    // 查看主动拆卸是否被禁用,即"spring.boot.enableautoconfiguration"的值是否为true    if (!isEnabled(annotationMetadata)) {        return NO_IMPORTS;    }    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}

getAutoConfigurationEntry办法中,调用了getCandidateConfigurations办法,其中应用了SpringFactoriesLoader(基于Java SPI 机制拓展)读取 spring-boot-autoconfigure-xxx.jar 包中的 META-INF/spring.factories 文件内容,失去主动拆卸类的全限定名并进行加载。

spring.factories文件的内容如下,SpringFactoriesLoader将其解析为一个Map<String, List<String>>构造,其中 key 依据组件作用分类,而 value 则是对应的一系列主动拆卸类的全限定名。

# Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.autoconfigure.BackgroundPreinitializer# Auto Configuration Import Listenersorg.springframework.boot.autoconfigure.AutoConfigurationImportListener=\org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener# Auto Configuration Import Filtersorg.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\org.springframework.boot.autoconfigure.condition.OnBeanCondition,\org.springframework.boot.autoconfigure.condition.OnClassCondition,\org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\...# Failure analyzersorg.springframework.boot.diagnostics.FailureAnalyzer=\org.springframework.boot.autoconfigure.data.redis.RedisUrlSyntaxFailureAnalyzer,\...# Template availability providersorg.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\...

因为@SpringBootApplication@EnableAutoConfiguration注解润饰,因而这里会加载EnableAutoConfiguration对应的所有主动拆卸类(XXXXAutoConfiguration.class)注入到 Spring 容器中。

2.2 主动拆卸类的属性如何注入

开发者在 application.properties 中笼罩默认配置的性能其实就是通过将配置文件的内容映射到主动拆卸类上的成员来实现的,主动拆卸类(XXXXAutoConfiguration.class)注入属性的过程利用了上述提到的几个注解:@ConfigurationProperties@EnableConfigurationProperties。以 KafkaAutoConfiguration.class 为例:

@Configuration(proxyBeanMethods = false)@ConditionalOnClass(KafkaTemplate.class)@EnableConfigurationProperties(KafkaProperties.class)@Import({ KafkaAnnotationDrivenConfiguration.class, KafkaStreamsAnnotationDrivenConfiguration.class })public class KafkaAutoConfiguration {    private final KafkaProperties properties;    // ...}

该类本身是一个配置类,外部有许多被 @Bean 注解润饰的办法用于生成被Spring容器治理的组件,其被 @EnableConfigurationProperties 润饰,注入了 KafkaProperties.class 的 bean 对象,而 KafkaProperties.class 又被 @ConfigurationProperties 润饰,即所有属性最终都由配置文件提供,这也解释了开发者为什么可能在 application.properties 中笼罩默认配置。

@ConfigurationProperties(prefix = "spring.kafka")public class KafkaProperties {    // ...}

3. 参考资料

  1. 一文理解 @Conditional 注解阐明和应用
  2. @Import、ImportSelector注解应用及源码剖析
  3. @ResponseBody与@RestController的作用与区别
  4. springboot(四)——@EnableConfigurationProperties是如何起作用的你晓得吗