Spring boot SpringBoot-属性占位符配置无法与@Service annotation一起使用

Spring boot SpringBoot-属性占位符配置无法与@Service annotation一起使用,spring-boot,property-placeholder,Spring Boot,Property Placeholder,我试图使用属性占位符值定义服务bean名称。但是出现错误,表示找不到特定名称的bean。我知道问题在于读取属性值,因为在硬编码值时,它正在工作。请帮助我,因为我需要从属性文件读取值。下面的代码片段: 应用程序属性 event.testRequest=TEST_REQUEST 服务等级 @Service("${event.testRequest}") // This is not working, getting "No bean named 'TEST_REQUE

我试图使用属性占位符值定义服务bean名称。但是出现错误,表示找不到特定名称的bean。我知道问题在于读取属性值,因为在硬编码值时,它正在工作。请帮助我,因为我需要从属性文件读取值。下面的代码片段:

应用程序属性

event.testRequest=TEST_REQUEST
服务等级

@Service("${event.testRequest}") // This is not working, getting "No bean named 'TEST_REQUEST' available" error
// @Service("TEST_REQUEST")     // This is working
public class TestRequestExecutor extends DefaultExecutionService {
...
}
另外,为了确认属性值是否正确读取,我尝试使用
@value(${event.testRequest}”)私有字符串值
,其中我得到了预期的值“TEST_REQUEST”。不知道如何将其与@Service annotation一起使用

编辑: 为了详细说明外部化服务bean名称的需要,我使用工厂模式来获取基于事件名称(事件名称,如Event1、Event2..)的实现。如果事件名称发生了更改,则更改将仅出现在属性文件上,而不是使用属性占位符的服务bean名称上

    @RestController
    public class RequestProcessController {
    
    @Autowired
    private ExecutorFactory executorFactory;
    ..
    ExecutionService executionService = executorFactory.getExecutionService(request.getEventType());
    executionService.executeRequest(request);
..
}


@Component
public class ExecutorFactory {

private BeanFactory beanFactory;

public ExecutionService getExecutionService(String eventType) {
  return beanFactory.getBean(eventType, DefaultExecutionService.class);
}
这里的
DefaultExecutionService
有如下不同的实现

@Service("${event.first}")
public class Event1Executor extends DefaultExecutionService {..}
..
@Service("${event.second}")
public class Event2Executor extends DefaultExecutionService {..}

event.first = Event1
event.second = Event2
所以基本上在将来,如果Event1名称更新为EventOne,我只需要更新属性文件,而不是服务类


非常感谢任何帮助!谢谢

有些东西你可以试试。尽管如此,我不建议这样做

尝试创建
BeanNameGenerator
并使用
BeanNameGenerator(BeanNameGenerator BeanNameGenerator)
方法将其提供给
SpringApplicationBuilder
。如果您感到好奇,这里是默认实现的一个示例

如果我理解正确,您有多个此服务的实现,您必须根据属性文件中提供的名称选择一个。如果是这样的话,请看一看。如果这些实现依赖于不同的配置文件,请查看

详细解释后编辑:

我认为实现这一点最简单的方法是注册您自己的bean。因此,请从您的执行者中删除
@Service
注释。然后,使用
DefaultListableBeanFactory
为执行者注册自己的
BeanDefinition

代码如下所示:

@Value("${event.first}")
String event1;

DefaultListableBeanFactory context = .. //Get BeanFactory

GenericBeanDefinition gbd = new GenericBeanDefinition();
gbd.setBeanClass(Event1Executor.class);
gbd.getPropertyValues().addPropertyValue("someProperty", "someValue");

context.registerBeanDefinition(event1, gbd);
Event1Executor bean = (Event1Executor) context.getBean(event1);
您可能可以使用
BeanFactoryAware
来获取bean工厂,如果您想在注册bean之前设置其他参数,则可以使用
BeanDefinitionBuilder

好的,现在它已清除

我认为您可以通过更改实现来实现这种行为:

@AllArgsConstructor // note, its not a component - I'll use @Configuration
public class ExecutorFactory {
   private final Map<String, DefaultExecutionService> executorByEventName;

   public DefaultExecutorService  getExecutionService(String eventType) {
        return executorByEventName.get(eventType);
   }

不必与bean工厂一起工作,代码< > ExtutoRealStudio,而应考虑创建以下实现:

@AllArgsConstructor // note, its not a component - I'll use @Configuration
public class ExecutorFactory {
   private final Map<String, DefaultExecutionService> executorByEventName;

   public DefaultExecutorService  getExecutionService(String eventType) {
        return executorByEventName.get(eventType);
   }
然后,您可以从Java配置类创建一个
ExecutorFactory
,这就是为什么我没有在答案开头的
ExecutorFactory
类上添加
@Component
/
@Service
注释的原因

@Configuration
public class MyConfiguration {
    @Bean
    public ExecutorFactory executorFactory(Map<String, DefaultExecutorService> 
        allServicesByQualifierName, MyConfigurationProperties config) {
        Map<String, DefaultExecutorService> map = new HashMap<>();
        allServicesByQualifierName.forEach((qualifierName, serviceBean) -> {
             String actualEventName = config.getMappedEventName(qualifierName);
             map.put(actualEventName, serviceBean);
        });
        return new ExecutorFactory(map);    
    }
} 
然后访问应该支持获取所有事件的方法的配置。这可以通过不同的方式实现,最自然的方式可能是使用
@ConfigurationProperties
注释并将
application.yaml
的事件映射映射到Java中的映射。你可以阅读技术细节


作为旁注,虽然我使用了
@配置
方法,因为它看起来更清晰,但也可以在
执行器工厂
上使用
@服务
,但是我展示的类似逻辑将成为执行器工厂(构造函数或后期构造方法)的一部分,您仍然可以将bean名称映射注入实际bean,并将配置属性注入构造函数,这取决于您自己决定

您不能在
@Component
注释中使用占位符(
@Service
就是其中之一)`。在我的书中,动态地分配一个名字也没有什么意义。你能详细说明一下为什么你真的需要这样做吗?spring需要引用springbean的名称,以获得正确的依赖项注入解析规则。我从来没有见过让它们充满活力的必要。我相信,如果你将这个问题表述出来,还有其他方法可以解决这个问题…@MarkBramnik我们有不同的实现方式。我们需要将其外部化以从属性文件中读取,因为每个占位符的名称(这些是事件名称)将来可能不同,并且它将在属性文件中更新。因此,我们可以使用现有代码,而不是进行代码更改,因为服务引用的是属性值。希望清楚。不是很清楚,所以您有不同的DefaultExecutionService实现,对吗?假设它可以工作,您在哪里使用已解析的bean名称,我的意思是,如果spring能够从配置文件解析@Service(“someService”)或@Service(“anotherService”),您在应用程序中在哪里使用字符串“someService”或“anotherService”?如果您只需要加载多个实现中的一个,您可以使用\@ConditionalOnProperty,在其他一些情况下,使用\@Profile可能会很方便(虽然在引擎盖下是一样的),但它的技术与您所要求的不同…@MarkBramnik请参阅我文章中的编辑部分。我已经详细解释过了。如果还不清楚,请告诉我。请看编辑部分,我已经详细解释了。谢谢你的详细说明。我会仔细检查你的方法,看看效果如何。
evt1 --> bean of type TestRequestExecutor1 
evt2 --> bean of type TestRequestExecutor2