`
QING____
  • 浏览: 2251318 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Springboot自动装配与bean注入

    博客分类:
  • JAVA
 
阅读更多

     自动装配是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自动装配原理以及starter技术.pdf

    总结,Spring Boot的自动装配和starter技术大大简化了Java应用的配置,通过`@Import`注解和自动配置机制,我们可以高效地管理Bean的生命周期。理解这些原理对于优化和调试Spring Boot应用至关重要。

    SpringBoot自动装配原理(简单易懂)

    自动装配是SpringBoot的一项功能,它通过`@Autowired`注解来自动注入依赖对象,无需显式地在配置文件中声明。SpringBoot会根据类型或名称自动找到合适的bean并注入到需要的对象中。 2. **基于注解的配置** ...

    Springboot自动装配实现过程代码实例

    在Spring Boot项目中,自动装配Bean是指在应用程序启动时自动将Bean实例化并注入到容器中的过程。在本例中,我们使用@Reference注解来标记需要自动装配的字段。 在配置类中,我们使用...

    关于spring boot中几种注入方法的一些个人看法

    在启动 Spring IoC 时,容器自动装载了一个 AutowiredAnnotationBeanPostProcessor 后置处理器,当容器扫描到 @Autowired、@Resource 或 @Inject 时,就会在 IoC 容器自动查找需要的 bean,并装配给该对象的属性。...

    手动实现SpringBoot自动装配类

    自动装配是通过`@EnableAutoConfiguration`注解来开启的,这个注解会触发一系列预定义的`AutoConfiguration`类,这些类根据应用中已存在的依赖(如类路径中的JAR文件或配置类)来决定哪些bean需要被创建和装配。...

    springBoot自动装配原理最全思维导图

    自动装配的基础,是 Spring 从 4.x 版本开始支持 JavaConfig,让开发者可以免去繁琐的 xml 配置形式,而是使用熟悉的 Java 代码加注解,通过 @Configuration、@Bean 等注解可以直接向 Spring 容器注入 Bean 信息。

    动态的装配bean,注入到Controller中

    在Spring框架中,动态装配bean并将其注入到Controller中是一种常见的需求,这允许我们根据运行时的条件或配置来创建和管理对象。本篇将详细解释两种实现方式:基于XML配置和使用`@PostConstruct`注解。 首先,让...

    Spring 自动装配及其注解

    1. **@Autowired**:这是最常用的注解,用于自动装配bean。Spring会尝试找到类型匹配的bean进行注入。如果存在多个候选bean,则可以通过`@Qualifier`进一步指定。 2. **@Qualifier**:与`@Autowired`配合使用,用于...

    面试官常问的spring依赖注入和bean的装配问题.pdf

    自动装配是Spring自动检测Bean之间的依赖关系,并自动注入这些依赖的过程。它可以极大减少配置量,但缺点是可能因为过于复杂而难以预测注入结果,特别是在复杂的项目中。Spring提供了五种自动装配模式:no(默认不...

    SpringBoot 打成jar包供第三方引用自动装配方案实现

    SpringBoot 打成jar包供第三方引用自动装配方案实现 每天多学一点点~ 话不多说,这就开始吧… ...虽然知道目前水平有限,先试试普通的jar包如何与spring自动装配吧~ 环境 jdk1.8 springboot 2.1.12.RELEASE 2.使用者手

    springboot源码解析(二):自动装配原理1

    在Spring Boot的应用程序启动流程中,自动装配是一个关键特性,它使得开发者无需手动配置bean,而是由Spring Boot自动识别和管理。本节我们将深入探讨自动装配的原理。 首先,Spring Boot的启动是从`run`方法开始的...

    SpringBoot 获取spring bean方式.rar

    `@Autowired`是Spring提供的一个依赖注入注解,它能够自动将匹配的Bean注入到需要的地方。例如: ```java @Autowired private MyService myService; ``` 这里,`MyService`类型的Bean会被自动注入到`myService...

    自动装配+条件注解+条件配置实现.zip

    Spring Boot通过`@Autowired`注解自动将匹配的bean注入到需要的地方。例如,如果一个类需要数据库连接,Spring Boot会根据配置自动注入一个合适的DataSource。此外,Spring Boot还提供了一些默认配置,使得许多常见...

    详解SpringBoot 解决拦截器注入Service为空问题

    然而,在实际开发过程中,可能会遇到在拦截器中注入Service为空的问题,这个问题通常是由于SpringBoot的自动装配机制没有正确地识别和管理拦截器中的依赖。本文将详细讲解如何解决这个常见问题。 首先,我们来看...

    自动装配模拟.zip

    自动装配的概念源于Spring框架的依赖注入(Dependency Injection, DI),但在Spring Boot中被进一步加强,使得开发者能够快速构建可运行的应用。本教程将深入探讨Spring Boot的自动装配机制,并通过实例进行演示。 ...

    SpringBoot之Bean的使用及处理

    在Spring Boot框架中,Bean是核心概念之一,它代表了应用程序中的对象,这些对象由Spring容器管理,包括它们的创建、初始化、装配以及销毁。理解并熟练使用Bean对于开发高效且可维护的Spring Boot应用至关重要。本文...

    springboot+netty+mybatis+sqlserver

    "bean的注入问题"可能是指在Spring Boot框架中遇到的依赖注入问题,这通常是通过@Autowired注解来解决的,用于在运行时自动装配所需的bean。"springboot的异步加载bean"则提到了Spring Boot的异步特性,这允许应用...

    SpringBoot简单示例.zip

    - `@Autowired`:自动装配,用于注入依赖,SpringBoot会自动查找类型匹配的Bean进行注入。 - `@RequestMapping`:用于处理HTTP请求,可以与`@GetMapping`、`@PostMapping`等结合使用,指定URL映射。 3. **...

    详解Spring基于Annotation的依赖注入实现

    5. **@Resource**:与@Autowired类似,但它是JSR-250规范的一部分,不仅可以按类型装配,还可以通过名称来装配Bean。 #### 实现机制 Spring基于注解的依赖注入主要通过以下步骤实现: - **注解处理器**:Spring的...

    详解Spring中bean的作用域

    对于 prototype 作用域的 bean,有一点非常重要,那就是 Spring 不能对一个 prototype bean 的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个 prototype 实例后,将它交给客户端,随后就对该 ...

Global site tag (gtag.js) - Google Analytics