前言
使用kafka时遇到一个问题:
项目中有两个@kafkalistener分别监听不同kafka集群, 这两个kafka集群处于不同的环境, 需要针对不同的环境, 启动不同的@kafkalistener.
一套代码在不同的条件实例化不同的实例
解决方案
Spring4提供了 @Conditional 等一系列注解, 用于在Spring容器注入bean时的条件判断.
本次使用 @ConditionalOnProperty 解决.
@ConditionalOnProperty注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
// 数组,获取对应property名称的值,与name不可同时使用
String[] value() default {};
// 配置属性名称的前缀,比如spring.http.encoding
String prefix() default "";
// 数组,配置属性完整名称或部分名称
// 可与prefix组合使用,组成完整的配置属性名称,与value不可同时使用
String[] name() default {};
// 可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置
String havingValue() default "";
// 缺少该配置属性时是否可以加载。如果为true,没有该配置属性时也会正常加载;反之则不会生效
boolean matchIfMissing() default false;
}
(一) 创建maven项目
项目结构
(二) 编辑pom文件, 引入maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
(三) 编写application配置文件
其中一个为application.yml配置文件, 另两个为dev和pro的spring profile
内容为空即可
(四) 编写InitPropertyConfig配置文件
package cn.zack.config;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
/**
* 实现ApplicationContextInitializer接口, 重写其initialize方法
* 此处在SpringBoot启动时根据选择的profile, 选择注入Service1或者Service2
*
* @Order(Integer.MIN_VALUE) 值越小, 在容器中bean的执行顺序越靠前
*/
@Order(Integer.MIN_VALUE)
public class InitPropertyConfig implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
// 当前启动选择的spring profile
String[] activeProfiles = configurableApplicationContext.getEnvironment().getActiveProfiles();
String thisActiveProfile = activeProfiles[0];
System.out.println("当前profile为: " + thisActiveProfile);
HashMap<String, Object> map = new HashMap<>(8);
// 当profile为dev时, 只注入service1, 当profile为pro时, 只注入service2
if (thisActiveProfile.equals("dev")) {
map.put("flag", "service1");
} else if (thisActiveProfile.equals("pro")) {
map.put("flag", "service2");
} else {
map.put("flag", "");
}
MapPropertySource propertySource = new MapPropertySource("myFlag", map);
configurableApplicationContext.getEnvironment().getPropertySources().addLast(propertySource);
}
}
(五) 编写启动类
package cn.zack;
import cn.zack.config.InitPropertyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class InitPropertyApplication {
public static void main(String[] args) {
// 创建一个springApplication, 在启动时加载自定义配置, 控制注入条件
SpringApplication application = new SpringApplication(InitPropertyApplication.class);
application.addInitializers(new InitPropertyConfig());
application.run(args);
}
}
(六) 编写两个service类
package cn.zack.service;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
/**
* 当ApplicationContext中存在 flag的值为1时 配置生效, 注入这个bean
*/
@Component
@ConditionalOnProperty(name = "flag",havingValue = "service1")
public class Service1 {
{
System.out.println("注入了service1");
}
// 省略实际业务逻辑
}
package cn.zack.service;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
/**
* 当ApplicationContext中存在 flag的值为2时 配置生效, 注入这个bean
*/
@Component
@ConditionalOnProperty(name = "flag",havingValue = "service2")
public class Service2 {
{
System.out.println("注入了service2");
}
}
(七) 使用不同的spring profile启动
使用dev启动时, 控制台可以看到两行日志:
当前profile为: dev
注入了service1
使用pro启动时, 控制台可以看到:
当前profile为: pro
注入了service2
注: 当不指定profile时, 两个bean都不会被实例化.
(本次InitPropertyConfig类中未做active profile判空, 不指定profile启动时会抛异常)