当前位置: 首页 > 知识库问答 >
问题:

Spring Boot 2-在bean初始化之前执行一些操作

赏育
2023-03-14

我想在初始化bean之前,从类路径中的属性文件或外部位置加载属性。这些属性也是Bean初始化的一部分。我无法自动从Spring的标准application.properties或其自定义中提取属性,因为同一个属性文件必须可由多个部署程序访问。

我知道Spring应用程序事件;事实上,我已经在挂起ContextRefleShedEvent,以便在Spring上下文初始化之后执行一些任务(bean也在这个阶段初始化)。

对于我的问题陈述,从Spring文档的描述来看,ApplicationEnvironmentPrepareDevent看起来很有希望,但是钩子不起作用。


@SpringBootApplication
public class App {

    public static void main(String[] args) throws IOException {
        SpringApplication.run(App.class, args);
    }


    @EventListener
    public void onStartUp(ContextRefreshedEvent event) {
        System.out.println("ContextRefreshedEvent");    // WORKS
    }

    @EventListener
    public void onShutDown(ContextClosedEvent event) {
        System.out.println("ContextClosedEvent");   // WORKS
    }

    @EventListener
    public void onEvent6(ApplicationStartedEvent event) {
        System.out.println("ApplicationStartedEvent");  // WORKS BUT AFTER ContextRefreshedEvent
    }


    @EventListener
    public void onEvent3(ApplicationReadyEvent event) {
        System.out.println("ApplicationReadyEvent");    // WORKS WORKS BUT AFTER ContextRefreshedEvent
    }


    public void onEvent1(ApplicationEnvironmentPreparedEvent event) {
        System.out.println("ApplicationEnvironmentPreparedEvent");  // DOESN'T WORK
    }


    @EventListener
    public void onEvent2(ApplicationContextInitializedEvent event) {
        System.out.println("ApplicationContextInitializedEvent");   // DOESN'T WORK
    }


    @EventListener
    public void onEvent4(ApplicationContextInitializedEvent event) {
        System.out.println("ApplicationContextInitializedEvent");
    }

    @EventListener
    public void onEvent5(ContextStartedEvent event) {
        System.out.println("ContextStartedEvent");
    }

}

正如M.Deinum在评论中建议的那样,我尝试添加一个应用程序上下文初始化器,如下所示。似乎也不起作用。

    public static void main(String[] args) {
        new SpringApplicationBuilder()
                .sources(App.class)
                .initializers(applicationContext -> {
                    System.out.println("INSIDE CUSTOM APPLICATION INITIALIZER");
                })
                .run(args);

    }

虽然我的问题陈述是关于加载属性的,但我的问题/好奇实际上是关于如何在类初始化为bean并放入Spring IoC容器之前运行一些代码。现在,这些bean在初始化期间需要一些属性值,我不能/不想自动连接它们,原因如下:

虽然这些可以通过使用Spring概要文件和优先顺序来实现,但我希望对其进行编程控制(我还将维护我自己的属性存储库)。例如,我将编写一个名为myproputil的方便实用程序,并访问它们,如下所示:

public class MyPropUtil {
     private static Map<String, Properties> repository;

     public static initialize(..) {
         ....
     }

     public static String getDomainProperty(String key) {
        return repository.get("domain").getProperty(key);
     }

     public static String getAppProperty(String key) {
         return repository.get("app").getProperty(key);
     }

     public static String getAndAddBasePathToAppPropertyValue(String key) {
        ...
     }

}

@Configuration
public class MyComponent {

    @Bean
    public SomeClass getSomeClassBean() {
        SomeClass obj = new SomeClass();
        obj.someProp1(MyPropUtil.getDomainProperty('domainkey1'));
        obj.someProp2(MyPropUtil.getAppProperty('appkey1'));
        // For some properties
         obj.someProp2(MyPropUtil.getAndAddBasePathToAppPropertyValue('some.relative.path.value'));
        ....
        return obj;
    }

}

从文档来看,ApplicationEventsApplicationInitializers似乎符合我的需要,但我无法使它们适用于我的问题声明。

共有1个答案

汝墨一
2023-03-14

派对有点晚了,但希望我能提供一个解决你更新的问题声明。

这将关注如何在类初始化为bean并放入Spring IoC容器之前运行一些代码的问题

我注意到的一个问题是,您是通过@EventListener注释定义应用程序事件的。

只有在启动所有bean时才调用这些注释,因为这些注释是由EventListenerMethodProcessor处理的,而EventListenerMethodProcessor仅在上下文就绪时才触发(请参见SmartInitializingSingleton#AfterSingletonSinStantiated)

因此,在上下文准备好之前发生的一些事件。例如ContextStartedEvent,ApplicationContextInitializedEvent将无法到达侦听器。

相反,您可以做的是直接为这些事件扩展接口

@Slf4j
public class AllEvent implements ApplicationListener<ApplicationEvent> {

    @Override
    public void onApplicationEvent(final ApplicationEvent event) {
        log.info("I am a {}", event.getClass().getSimpleName());
    }

注意缺少@组件。甚至bean实例化也可以在其中一些事件之后发生。如果您使用@component,那么您将得到以下日志

I am a DataSourceSchemaCreatedEvent
I am a ContextRefreshedEvent
I am a ServletWebServerInitializedEvent
I am a ApplicationStartedEvent
I am a ApplicationReadyEvent

仍然比注释侦听器更好、更即时,但仍然不会接收初始化事件。为此,您需要做的是按照这里的说明操作

总而言之,

  • 创建目录资源/meta-inf
  • 创建文件spring.factors
  • org.springframework.context.applicationlistener=full.path.to.my.class.allevent

结果:-

I am a ApplicationContextInitializedEvent
I am a ApplicationPreparedEvent
I am a DataSourceSchemaCreatedEvent
I am a ContextRefreshedEvent
I am a ServletWebServerInitializedEvent
I am a ApplicationStartedEvent
I am a ApplicationReadyEvent

特别是,ApplicationContextInitializedEvent应该允许您执行所需的任何每实例化任务。

 类似资料:
  • 问题内容: 我的页面上有一个表单。该表单包含一个文本框和一个提交按钮。 提交表单后,通过单击按钮或在文本框中按Enter,我要进行查找(在这种情况下,使用Bing Maps对邮政编码进行地理编码),然后像往常一样将表单提交到服务器。 我当前的方法是将提交事件的处理程序添加到一个表单中,然后在完成后调用Submit(),但是我无法使它正常工作,并且无法调试问题: 问题答案: 是你的朋友在这里。在完成

  • 我试图理解@bolov对删除默认构造函数问题的第一个公认答案。对象仍然可以创建......有时[1] 似乎我发现了一个错误,所以它搞乱了整个解释。 @bolov解释了为什么这段代码能够在c 11中成功编译: 场景A 以及为什么这段代码无法在c 11中编译: 场景C 他说,重点是第一个foo是聚合,第二个foo不是聚合。 然后他给出了cppreference的摘录: T类型对象的列表初始化的影响是:

  • 我想为测试目的创建一个选项列表。起初,我是这样做的: 然后,我重构代码如下: 有更好的方法吗?

  • 我找不到任何关于这个具体案例的具体SO帖子,所以我想问一下我认为是/否的问题。 以下是JLS§12.4.2(Java SE 8),清单6-7: 我的问题是:这是否意味着子类的final static变量在超类的静态初始化之前初始化(假设final static作为其声明的一部分初始化)?

  • 问题内容: 我想创建用于测试目的的选项列表。首先,我这样做: 然后,我将代码重构如下: 有一个更好的方法吗? 问题答案: 实际上,初始化的“最佳”方法可能ArrayList是你编写的方法,因为它无需以List任何方式创建新方法: 问题是引用该实例需要大量输入。 还有其他选择,例如使用实例初始化程序(也称为“双括号初始化”)创建匿名内部类: 但是,我不太喜欢这种方法,因为最终得到的是ArrayLis

  • 我在想Spring中bean的懒惰初始化。对我来说,这里的“懒惰”是否意味着当一个bean被引用时会被创建并不十分清楚。 我认为Spring中的延迟初始化支持是不同的。我认为这是一个基于“方法调用”的惰性创建。我的意思是,每当对该方法调用任何方法时,都会创建该方法。 我认为这可以通过创建特定bean的代理实例并对任何方法调用进行初始化来轻松解决。 我是否遗漏了一些东西?为什么没有实施?这个概念有什