栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring(SpringBoot)--条件注入--方法/示例

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring(SpringBoot)--条件注入--方法/示例

原文网址:Spring(SpringBoot)--条件注入--方法/示例_IT利刃出鞘的博客-CSDN博客

简介

说明

        本文介绍Spring如何将某个类根据条件注入容器。比如:如果某个类不存在,则将类注入;如果某个配置没开启,则不注入。

源码中的使用

        在Spring Boot的源码中,比如涉及到Http编码的自动配置、数据源类型的自动配置等大量的使用到了@ConditionalOnProperty注解。

HttpEncodingAutoConfiguration类中部分源代码:

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
    // 省略内部代码
}

DataSourceConfiguration类中部分代码:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
        matchIfMissing = true)
static class Tomcat {
  // 省略内部代码
}
已有的条件注解 注解大全

注解

说明

示例

@ConditionalOnProperty

要求配置属性匹配条件

@ConditionalOnProperty(

  prefix = "spring.http.encoding",

  value = "enabled",

  matchIfMissing = true)

@ConditionalOnBean

容器中存在对应实例时,返回true。

当给定的类型、类名、注解、昵称在beanFactory中存在时返回true。

若不指定name,则为本bean的名字。

@ConditionalOnBean

@ConditionalOnBean(name="abc")

@ConditionalOnMissingBean

容器中不存在对应实例时,返回true。

当给定的类型、类名、注解、昵称在beanFactory中不存在时返回true。各类型间是or的关系。

若不指定name,则为本bean的名字。

@ConditionalOnClass

当给定的类在类路径上存在时返回true,各类型间是and的关系

@ConditionalOnClass(Abc.class)

@ConditionalOnMissingClass

当给定的类在类路径上不存在时返回true,各类型间是and的关系

@ConditionalOnSingleCandidate

当给定类型的bean存在并且指定为Primary的给定类型存在时,返回true

@ConditionalOnCloudPlatform

当所配置的CloudPlatform为激活时返回true

@ConditionalOnExpression

spel表达式执行为true

详见下方。

@ConditionalOnJava

运行时的java版本号是否包含给定的版本号.如果包含,返回匹配,否则,返回不匹配

@ConditionalOnJndi

给定的jndi的Location 必须存在一个.否则,返回不匹配

@ConditionalOnNotWebApplication

web环境不存在时

@ConditionalOnWebApplication

web环境存在时

@ConditionalOnResource

要求制定的资源存在

详解 @ConditionalOnProperty
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
    String[] value() default {};     //数组,获取对应property名称的值,与name不可同时使用
 
    String prefix() default "";      //property名称的前缀,可有可无
 
    String[] name() default {};      //数组,property完整名称或部分名称(可与prefix组合使用,组成完整的property名称),与value不可同时使用
 
    String havingValue() default "";  //可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置
 
    boolean matchIfMissing() default false;  //缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错
}

单个值 

@ConditionalOnProperty("config1.enable", havingValue="true")

多个值

@ConditionalOnProperty(name={"config1.enable","config.all"}, havingValue="true")

@ConditionalOnClass

用法1:通过class指定

import a.b.c.Xxx;

@ConditionalOnClass(Xxx.class)

也可以指定多个 

import a.b.c.Xxx;

@ConditionalOnClass({Xxx.class, Yyy.class})

用法2:通过类的全路径名指定 

@ConditionalOnClass("a.b.c.Xxx")

也可以指定多个

@ConditionalOnClass({"a.b.c.Xxx", "a.b.c.Yyy"})

源码示例

以openfeign的自动配置类为例:

package org.springframework.cloud.openfeign;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

import javax.annotation.PreDestroy;

import com.fasterxml.jackson.databind.Module;
import feign.Client;
import feign.Feign;
import feign.RequestInterceptor;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import okhttp3.ConnectionPool;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptor;
import org.springframework.cloud.openfeign.support.DefaultGzipDecoderConfiguration;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.cloud.openfeign.support.PageJacksonModule;
import org.springframework.cloud.openfeign.support.SortJacksonModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
    // 省略其他代码

    @Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(OkHttpClient.class)
	@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
	@ConditionalOnProperty("feign.okhttp.enabled")
	protected static class OkHttpFeignConfiguration {

		private okhttp3.OkHttpClient okHttpClient;

		@Bean
		@ConditionalOnMissingBean(ConnectionPool.class)
		public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
				OkHttpClientConnectionPoolFactory connectionPoolFactory) {
			Integer maxTotalConnections = httpClientProperties.getMaxConnections();
			Long timeToLive = httpClientProperties.getTimeToLive();
			TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
			return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
		}

		@Bean
		public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool,
				FeignHttpClientProperties httpClientProperties) {
			Boolean followRedirects = httpClientProperties.isFollowRedirects();
			Integer connectTimeout = httpClientProperties.getConnectionTimeout();
			Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
			this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
					.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects)
					.connectionPool(connectionPool).build();
			return this.okHttpClient;
		}

		@PreDestroy
		public void destroy() {
			if (this.okHttpClient != null) {
				this.okHttpClient.dispatcher().executorService().shutdown();
				this.okHttpClient.connectionPool().evictAll();
			}
		}

		@Bean
		@ConditionalOnMissingBean(Client.class)
		public Client feignClient(okhttp3.OkHttpClient client) {
			return new OkHttpClient(client);
		}

	}
}

注意点:

        如果项目中没有引入这个包的话,在Idea中可以看到,它是红色的。但项目在启动时并不会报错,可以正常启动。

        原因是:实际我们在引入这个依赖的时候,它的jar包是已经编译好了的。而我们现在这样查看是源码方式查看,不是查看的其jar包的class。

@ConditionalOnExpression

或/与

config1.enabled为true或者config2.enabled 为true

@ConditionalOnExpression("${config1.enabled:false} || ${config2.enabled:false}")

与时类似。换成&&即可。

计算

@ConditionalOnExpression("${mq.cumsumer.enabled}==1 && ${rabbitmq.comsumer.enabled:true}")

取反

config1.enabled为true而且config2.enabled 为false

@ConditionalOnExpression("${config1.enabled:false} && !${config2.enabled:false}")

equals

 @ConditionalOnExpression("'${mq.comsumer}'.equals('rabbitmq')")

分级

例2:若局部有配置,按局部配置。否则,按全局配置。若全局、局部都没配置,则不导入。

本处:config2.enabled为局部,config.all.enabled为全局

@ConditionalOnExpression(value = "${config2.enabled:${config.all.enabled:false}}")

实例

@Component、@Configuration+@Bean都可以与条件注入的注解结合。

@Component + 条件注解

Bean

package com.example.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

@Component
@ConditionalOnProperty(name = "custom.myComponent.enabled", havingValue = "true")
public class MyComponent {
    public MyComponent() {
        System.out.println("[MyComponent#MyComponent]");
    }
}

application.yml

custom:
  myComponent:
    enabled: true

运行结果:

[MyComponent#MyComponent]

若将application.yml的custom.myComponent.enabled去掉,或者设置为非true值,则不会输出上边的运行结果。

@Configuration + @Bean + 条件注解

Bean

package com.example.config;

public class MyComponent {
    public MyComponent() {
        System.out.println("[MyComponent#MyComponent]");
    }
}

配置类

package com.example.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {
    @Bean
    @ConditionalOnProperty(name = "custom.myComponent.enabled", havingValue = "true")
    public MyComponent getMyComponent() {
        return new MyComponent();
    }
}

application.yml

custom:
  myComponent:
    enabled: true

运行结果:

[MyComponent#MyComponent]

若将application.yml的custom.myComponent.enabled去掉,或者设置为非true值,则不会输出上边的运行结果。

@Configuration + 条件注解 + @Bean

Bean

package com.example.config;

public class MyComponent {
    public MyComponent() {
        System.out.println("[MyComponent#MyComponent]");
    }
}

配置类

package com.example.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(name = "custom.myComponent.enabled", havingValue = "true")
public class MyConfig {
    @Bean
    public MyComponent getMyComponent() {
        return new MyComponent();
    }
}

application.yml

custom:
  myComponent:
    enabled: true

运行结果:

[MyComponent#MyComponent]

若将application.yml的custom.myComponent.enabled去掉,或者设置为非true值,则不会输出上边的运行结果。

自定义Condition

自定义的condition的matches方法返回值为true时,才会创建bean。

条件类

//判断当前系统是否是Mac

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
 
public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, 
                           AnnotatedTypeMetadata annotatedTypeMetadata) {
        return conditionContext.getEnvironment().getProperty("os.name").contains("Mac");
    }
}
@Configuration
public class Config {
    @Conditional(MyCondition.class)
    @Bean
    public String condition() {
        System.err.println("This is mac");
        return "";
    }
}
其他网址

Spring Boot 2 实战:使用 @Condition 注解来根据条件注入 Bean

Spring注解系列之ConditionalOnProperty_瘦子没有夏天-CSDN博客_conditionalonproperty注解

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/846941.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号