【问题标题】:Looking for the Spring profile in a component class always returns null在组件类中查找 Spring 配置文件总是返回 null
【发布时间】:2020-10-17 09:10:41
【问题描述】:

在某些情况下:

组件需要根据当前环境做逻辑。由于无法手动设置环境,很多 @Properties 注释将不起作用 - 设置属性文件不够灵活,无法满足我们的需求。但是,当我尝试将环境细节拉入我的组件类时,似乎没有任何效果!示例使它看起来很简单,但我的总是返回 null。我是否会在 Spring 的底层遗漏一些会导致这种情况的东西?这是我尝试过的:

使用资源,它的日志:

@Component
@Slf4j
public class compClass {
    @Resource
    private Environment env;

    public void map(DtoObj dto){
        log.info("RUN PROFILE IS: {}", env.getActiveProfiles());
    }
}

java.lang.NullPointerException: null at com.demo.Com.map(CompClass.java:22) ~[classes/:na]

使用 Autowire,它的日志:

@Component
@Slf4j
public class CompClass {
    @Autowired
    private Environment env;

    public void map(DtoObj dto){
        log.info("RUN PROFILE IS: {}", env.getActiveProfiles());
    }
}

java.lang.NullPointerException: null at com.demo.Com.map(CompClass.java:22) ~[classes/:na]

@Value 与环境及其日志:

@Component
@Slf4j
public class CompClass {
    @Value("#{environment.activeProfiles}")
    private String[] profiles;

    public void map(DtoObj dto){
        log.info("RUN PROFILE IS: {}", profiles);
    }
}

RUN PROFILE IS: null

带有属性路径的@Value 和它的日志

@Component
@Slf4j
public class CompClass {
    @Value("${spring.profiles.active}")
    private String[] profiles;

    public void map(DtoObj dto){
        log.info("RUN PROFILE IS: {}", profiles);
    }
}

RUN PROFILE IS: null

这是我可以在 java/main/resources 下共享的属性:

spring:
  application:
    name: namnam
  profiles:
    active: dev

我正在使用这个 VM 在 IntelliJ 上运行它:

-Dspring.profiles.active=dev

我的属性文件位于 java/main/resources 目录中,我们可以在代码中使用它的其他属性。我搞砸了怎么办?

【问题讨论】:

  • 你能显示properties 文件吗?而且我仍然没有得到您的问题,您的意思是 profiles 始终为 null 而 env.getActiveProfiles() 为 null ?你也可以添加日志
  • 是的,我已经根据您要求的编辑更新了我的 OP。这是更具描述性的吗?我所做的调用总是返回 null,因此尝试对这些 null 使用方法会抛出 NullPointerException(正如人们所期望的那样)。
  • 您的第二种方法Autowired Environment 对我来说看起来不错,而且我还看到Environment 为空,所以这意味着您在使用compClass 类时做错了一些地方,如果可能的话,你能显示代码你是如何调用compClass 类方法或将compClass 注入另一个bean
  • 不幸的是,我在不违反某些协议的情况下分享了我可以使用的代码的范围。这个 CompClass 是一个映射器。控制器调用读取、处理、写入的批处理作业。那个批处理作业就是bean。在流程步骤中,我们的处理器类接触到一个映射器 - 我们的映射器在这里 :) 我是否在 spring 上下文之外太远以至于环境没有任何值?
  • 哦,亲爱的 - 在我的配置中,我有用于批处理作业的 bean、我的项目阅读器和我的项目映射器(不同的映射器,而不是我们的映射器),但没有我的处理器。我想知道为我的批处理配置类添加处理器 bean 是否有帮助?

标签: java spring spring-boot environment-variables


【解决方案1】:

首先,让我澄清一点: 我不建议将@Configuration 放在您的业务对象(bean)上。用@Configuration 注释的类旨在成为定义其他bean 的工具。它们通常看起来像:

@Configuration
public class SampleConfiguration {
   @Bean
   public SomeBean someBean() {
      return new SomeBean();
   }

      @Bean
   public AnotherBean anotherBean() {
      return new AnotherBean();
   }
}

在此示例中,SomeBean 和 AnotherBean 是“业务”对象。这可以作为将@Service/@Component 放在这些类上的替代方法,在任何情况下,您都不应该将@Configuration 放在它们上。

现在关于问题和建议的解决方案:

简而言之,您可以像这样注入环境:

@Service
public class EnvChecker {


    private final Environment env;

    public EnvChecker(Environment env) {
        this.env = env;
    }

    @PostConstruct
    public void init() {
        System.out.println(env); // this is not null
    }
}

如果您想激活配置文件,请使用--spring.profiles.active=dev 作为 intelliJ 中的“程序参数”文本字段(-D 不起作用)

虽然这回答了这个问题,但由于它是真正的项目,让我表达一些可能会影响您最终编写的实际代码的想法。

IMO:让您的业务代码(我的意思是在编码级别)依赖于不同的环境不是一个好主意,如果明天您将添加另一个环境(测试,qa ,集成,生产 1,生产 2 - 你的名字)。这个逻辑将变得不可维护。

为了解决这个问题,spring 传统上使用配置文件。

根据注入的环境,您可以创建两个 bean 并使用不同的配置文件激活,而不是在代码中使用 if-condition:

例子:

你可能有这样的事情:

public class MyClass {
    private  Environment env; // lets assume it works right
    
    public void doSomething() {

      if(env.getActiveProfiles() contains "dev") { // its a pseudo code, not real java, but still...
         doFoo(); // some code
      }
      else {
        doBar(); // another piece of code
      }
    }    
}

所以使用配置文件的第一个建议是:


@Service
@Profile("dev")
public class DevBean {
   public void doSomething() {
     doFoo();
   }
}


@Service
@Profile("prod")
public class ProdBean {
   public void doSomething() {
     doBar();
   }
}

这种方法更好,因为业务代码会更容易阅读和维护,另一方面,仍然有代码依赖于配置文件状态。 但是,我更喜欢另一种解决方案,可以推荐:

这个想法是将个人资料“视为”一系列可以启用或禁用的业务功能。这是有道理的,因为配置文件本身通常表示环境,但项目始终由业务功能组成。

因此,假设您的代码实现了处理数据库连接的功能 X(为了示例)。您想在开发环境中禁用数据库并希望在“内存中”工作。

因此,就系统提供的功能而言,您可以定义以下内容:

feature.X.mode=database / in-memory

然后您可以使用“内存”属性配置dev 配置文件,默认值(其余配置文件)可以是“数据库”:

application-dev.yml:

feature:
  X: 
    mode: "in-memory"
application.yml:
feature:
  X: 
    mode: "database" # or you can even omit the definition, leaving the default to be the "database" 

现在可以以与以前的技术类似的方式定义 bean,但现在您将不再依赖于配置文件,而是依赖于业务功能的可用性:

@Service
@ConditionalOnProperty(name = "feature.X.mode", havingValue="in-memory")
public class DevBean {
   public void doSomething() {
     doFoo();
   }
}


@Service
@ConditionalOnProperty(name = "feature.X.mode", havingValue="database", matchIfMissing=true) // the last parameter guarantees that the bean will be loaded even if there is no configuration at all
public class ProdBean {
   public void doSomething() {
     doBar();
   }
}

现在你终于没有“环境依赖代码”了。

最后一个小技巧是如果你有几个这样的 bean(服务/组件)。 在这种情况下,您可以定义“stereotype annotation”(@FeatureXService)或使用 Java Configuration(使用 @Configuration 注释的类)并将 @ConditionalOnProperty 注释只放置一次。

【讨论】:

    【解决方案2】:

    这段代码对我来说很好,我希望它对你有用。

    @Configuration
    public class CompClass {
    
    private final Environment env;
    
    public CompClass (Environment env) {
        this.env = env;
    }
    
    public void map(DtoObj dto){
        log.info("RUN PROFILE IS: {}", env.getActiveProfiles());
    }
    
    public void doSomethingBasedOnSpeceficProfile() {
     if (env.acceptsProfiles(Constants.SPRING_PROFILE_PRODUCTION)) {
        // do something if the profile is what you want
      } else {
            // do something else
      }
    }}
    

    【讨论】:

    • 我正在做一个企业项目,我不能称它为 ClassName1。你能澄清一下这个配置的目的是什么,这样我就可以知道我是否有一个更适用的名字,或者可以把 bean 放在已经存在的类中?
    • 您可以随意重命名它-实际上我在提交代码之前自己重命名它-@配置只是用于配置应用程序的类的另一种接线方式-@组件的子类型- 我在类中添加的bean方法只是为了说明如何使用它
    • 所以尝试添加@配置并使用构造函数注入,它可能会工作
    • 机器服务在这里做什么,它告诉Spring我的项目什么? CerebralIntegration 是什么意思,它的用途是什么?我不明白 CMachineService 和 buildMachineService 在做什么?这一切都与我有关还是我误解了?这样的东西通常会和其他春豆一起吃吗?我不想无缘无故地为大型项目中的 bean 创建全新的配置类,那么这个组和其他组一样吗?
    • 我编辑了我的答案,删除了不相关的部分。你可以用@configuration注释你的类并且仍然得到@component的相同行为,因为@configuration只是@component的一个子类型,但无论如何,如果它不起作用,请先用@component尝试使用@configuration,如果两者兼而有之没有工作那么肯定有一些关于你如何配置你的个人资料的问题
    猜你喜欢
    • 1970-01-01
    • 2017-12-27
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-16
    • 1970-01-01
    • 2018-09-24
    相关资源
    最近更新 更多