自动装配是springboot中一大特性,即springboot在程序初始化时可以根据classpath类、property属性、context中实例、以及运行容器特征等各种动态条件,来按需初始化相应的bean并交付给终端使用。
1、@ConditionalOnBean:如果此Bean实例已存在,则执行(如果修饰在class上,则此类加载;如果修饰在方法上 ,则此方法执行)。通常用于限定“如果必备的bean已经被初始化时,才会自动装备当前实例”。
2、@ConditionalOnMissingBean:如果此Bean实例尚未被初始时,则执行。
3、@ConditionalOnClass:如果classpath中存在此类(且可被加载)时,则执行。
4、@ConditionalOnMissingClass:如果class中不存在此类时,则执行。
5、@ConditionalOnProperty:如果Envrionment中存在此property、或者相应的property参数值匹配时,则执行。比如springboot中,各种内置的组件是否enabled,就是根据此条件判断。
6、@ConditionalOnWebApplication:如果当前容器为WEB时,则执行。比如初始化springMVC、restTemplate、各种endpoint初始化等。
此外springboot还提供了其他更多的condition判断,大家按需使用即可。在springbean容器初始化之前、Envrionment准备就绪之后,将根据spring.factories文件中指定的AutoConfiguration类列表逐个解析上述Condition,并在稍后有序加载、初始化相关bean。
源码,请参见:ConfigurationClassParser,ConfigurationClassBeanDefinitionReader,SpringBootCondition。
装配时机:
1、@AutoConfigureAfter:在指定的其他自动装配类初始化之后,才装配当前类。
2、@AutoConfigureBefore:在指定的其他装配类初始化之后,才装配当前类。
3、@AutoConfigureOrder:指定当前类装配的优先级,类似于Ordered接口。
每个可以自动装配的springboot类,通常具备如下几个特征:
1)使用@Configuration修饰,且此类不应该被@ComponentScan包含。
2)类上可以被@Conditional*修饰。
3)构造函数不应该为private;构造函数中的参数类,可以被spring容器注入。
4)此类必须在META-INF/spring.factories中声明(注册),才能被springboot感知。
5)springboot中自动装配的类,需要设计好加载或者引用其他bean的顺序,否则可以引入“循环依赖”问题,导致容器初始化失败。
6)自动装配类,通常配合@ConfigurationProperties完成一些属性的配置和bean加载,当然@ConfigurationProperties并不是必须的。
本文展示一下,如何使用springboot自动装配,实现RestTemplate类的自定义设置。
一、HttpClientAutoConfiguration
import okhttp3.OkHttpClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.client.HttpClient; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.Map; /** * @author liuguanqing * created 2018/6/28 上午11:33 * http客户端 **/ @Configuration @ConditionalOnWebApplication @ConditionalOnBean(Context.class) @AutoConfigureAfter({ContextAutoConfiguration.class}) @EnableConfigurationProperties({HttpProperties.class}) public class HttpClientAutoConfiguration { private final HttpProperties httpProperties; private final Context context; public HttpClientAutoConfiguration(HttpProperties httpProperties, ObjectProvider<Context> contextObjectProvider, ObjectProvider<RestTemplateBuilder> restTemplateBuilderObjectProvider) { this.httpProperties = httpProperties; this.context = contextObjectProvider.getIfUnique(); } @Bean @ConditionalOnMissingBean public HttpClientConfiguration httpClientConfiguration() { HttpClientConfiguration configuration = HttpClientConfiguration.common(); String charset = httpProperties.getCharset(); if (charset != null) { configuration.setCharset(charset); } ..... return configuration; } @Bean public MeteorRestTemplateCustomizer meteorRestTemplateCustomizer(HttpClientConfiguration httpClientConfiguration) { return new MeteorRestTemplateCustomizer(httpClientConfiguration); } private static ClientHttpRequestFactory clientHttpRequestFactory(HttpClientConfiguration configuration) { String clientType = configuration.getClientType(); if (HttpClientConfiguration.HTTP_CLIENT_TYPE_OK_HTTP.equalsIgnoreCase(clientType)) { OkHttpClient httpClient = OkHttpClientBuilder.build(configuration); return new OkHttp3ClientHttpRequestFactory(httpClient); } HttpClient httpClient = HttpComponentsClientBuilder.build(configuration); return new HttpComponentsClientHttpRequestFactory(httpClient); } public static class MeteorRestTemplateCustomizer implements RestTemplateCustomizer { private final HttpClientConfiguration configuration; public MeteorRestTemplateCustomizer(HttpClientConfiguration configuration) { this.configuration = configuration; } @Override public void customize(RestTemplate restTemplate) { restTemplate.setRequestFactory(clientHttpRequestFactory(configuration)); //restTemplate.getInterceptors().add(new MeteorHttpClientRequestHeaderInterceptor(configuration)); } } }
1)HttpClientConfiguration为自定义的构建配置,原则不影响对本文的阅读,感兴趣可以参见如下两个博客地址:“HttpComponents客户端”、“okHttp客户端”。
2)HttpProperties为本文自定义的、用于配置http客户端参数的配置类,请参见下文。
3)Context为本例的一个额外类,仅供演示自动装配时如何注入外部类。不影响阅读。
@EnableConfigurationProperties注释中指定的Properties类(@ConfigurationProperties修饰的类,可以多个),可以被AutoConfiguration类构造函数中直接注入(最好它们已经被初始化结束);此外对于其他spring bean,则不能直接在构造函数中引入,我们需要使用ObjectProvider来“告知容器”指定和获取,比如本文中的Context实例类。
在springboot环境下,我们初始化RestTemplate,尽量使用RestTemplateBuilder来构建,参见下文。
二、HttpProperties
import org.springframework.boot.context.properties.ConfigurationProperties; import java.time.Duration; import java.util.Map; /** * @author liuguanqing * created 2018/7/29 下午8:40 * 用于构建Context实例,单例 **/ @ConfigurationProperties("http") public final class HttpProperties { private String charset = ContextConstants.DEFAULT_CHARSET; //连接创建,HTTP/TCP等RPC private Duration connectionTimeout = Duration.ofMillis(500); //SO_TIMEOUT private Duration socketTimeout = Duration.ofMillis(500); //连接池中的连接被保活的时长 private Duration keepAliveTime = Duration.ofMillis(6000); //请求异常(exception),重试的次数,默认为0,不重试 private Integer retryTimes = 0; //是否重试 private Boolean retryOnFailure = false; //业务全局header,默认会添加到http请求中,每项为K-V private Map<String, String> globalHeaders; private String clientType = HttpClientConfiguration.HTTP_CLIENT_TYPE_HTTP_COMPONENTS; public String getCharset() { return charset; } public void setCharset(String charset) { this.charset = charset; } public Duration getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(Duration connectionTimeout) { this.connectionTimeout = connectionTimeout; } public Duration getSocketTimeout() { return socketTimeout; } public void setSocketTimeout(Duration socketTimeout) { this.socketTimeout = socketTimeout; } public Duration getKeepAliveTime() { return keepAliveTime; } public void setKeepAliveTime(Duration keepAliveTime) { this.keepAliveTime = keepAliveTime; } public Integer getRetryTimes() { return retryTimes; } public void setRetryTimes(Integer retryTimes) { this.retryTimes = retryTimes; } public Boolean getRetryOnFailure() { return retryOnFailure; } public void setRetryOnFailure(Boolean retryOnFailure) { this.retryOnFailure = retryOnFailure; } public Map<String, String> getGlobalHeaders() { return globalHeaders; } public void setGlobalHeaders(Map<String, String> globalHeaders) { this.globalHeaders = globalHeaders; } public String getClientType() { return clientType; } public void setClientType(String clientType) { this.clientType = clientType; } }
三、声明自动装配
我们需要在META-INF/spring.factories文件中,声明所有的自动装配类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.test.commons.springboot.context.ContextAutoConfiguration,\ com.test.commons.springboot.httpclient.HttpClientAutoConfiguration,\ com.test.commons.springboot.web.WebApplicationAutoConfiguration,\ com.test.commons.springboot.metrics.HttpMetricsExportAutoConfiguration
每行声明一个类,多个类时以“,”分割,“\”作为换行符。
四、初始化RestTemplate
@SpringBootApplication public class Application { public static void main(String[] args) { Application.run(DemoApplication.class, args); } @Bean public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { return restTemplateBuilder.build(); } }
我们需要在使用@Configuration修饰的bean中,初始化RestTemplate,请注意不要尝试在AutoConfiguration类中为应用初始化RestTemplate,因为这是违反设计的。
此外,我们通常使用RestTemplateBuilder来初始化restTemplate,此时我们在AutoConfiguration类中指定的各种Customizer则会生效。
相关推荐
深入源码底层分析springBoot自动装配原理并结合案例进行实战。
springboot自动装配的过程解析...
Springboot 自动装配原理图文,里面描述了整个Springboot的装配流程和所有相关的组件。
1.基于SpringBoot自动装配,引用jar包即可,坐标如下: <groupId>com.glzt</groupId> <artifactId>feignextend <version>1.0.0-SNAPSHOT 2.基于logback、log4j的MDC机制 3.日志配置中添加traceId引用,如下: ...
SpringBoot自动装配原理学习
SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提。我们在熟练使用springboot的时候,能否也能熟悉它完整的自动装配的过程?该资源包含了:springboot自动装配主要节点流程图、springboot自动装配完整流程...
SpringBoot自动装配原理
使用mindmaster打开
SpringBoot自动装配原理(简单易懂)
手动实现SpringBoot自动装配类 ,清测可用,代码详见com.auto包
SpringBoot自动装配原理分析(初探)思维导图
swagger-spring-boot-starter 是一款建立在swagger基础之上的工具包,利用SpringBoot自动装配的特性,简化了传统swagger的繁琐配置
主要介绍了Springboot自动装配实现过程代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
pringboot自动装配原理(Av75233634,P6)
springboot的自定义自动默认配置(如果你不配置属性,就按照默认的来,如果配置了就按照你的来)
#学习springboot自动装配 ##一,手动装配 ### 1,模式注解装配 @Component注解,或者@Component注解的扩展,@ Controller,@ Service,存储库,@ Configruation等, ### 2. @ Configuration启动容器+ @ Bean注册...
springBoot,RabbitMQ,Redis
使用mindmaster打开
(2)byName:根据属性 名 自动装配,设值注入 <bean id="xxx" class="xxx" ></bean> (3)byType:根据属性 类型 自动装配,相同类型多个会抛出异常,设值注入 <bean class="xxx" ></bean> (4)constructor:与 ...