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
润饰办法时须要留神:
- 办法带返回值,且返回类型为想要被治理的 bean 的类型;
- 办法名即为默认的 bean name,可应用
@Bean
的name
属性自定义; - 办法可带参数,则此时传入的参数也必须是被 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 对象的属性值。
假如有一个对象申明如下:
@Component
public class ServerProperty {@Value("${server.url}")
private String url;
// getter and setter
// ...
}
@Component
public 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
润饰类)。其应用与 @Conditional
和 Condition
接口的组合形式有点类似,如 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-type
为 application/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-type
是application/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 {// ...}
这里有一个 @Import
和 ImportSelector
接口实现类的组合,查看 AutoConfigurationImportSelector.class
的 selectImports
办法:
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 则是对应的一系列主动拆卸类的全限定名。
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
...
# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.data.redis.RedisUrlSyntaxFailureAnalyzer,\
...
# Template availability providers
org.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. 参考资料
- 一文理解 @Conditional 注解阐明和应用
- @Import、ImportSelector 注解应用及源码剖析
- @ResponseBody 与 @RestController 的作用与区别
- springboot(四)——@EnableConfigurationProperties 是如何起作用的你晓得吗