Spring 项目中在启动阶段加载配置并初始化 Bean 的几种方法

发布于:2025-05-12 ⋅ 阅读:(25) ⋅ 点赞:(0)

最近在整合拉卡拉支付的 SDK 时发现初始化失败,然后找了下也找到问题出在哪,就想着换一个方法初始化算了,所以就去查了下几种初始化的方法和区别

先看我下初始化失败的方法,也是我之前一直在用的方法 @PostConstruct

import com.lkl.laop.sdk.Config;
import com.lkl.laop.sdk.LKLSDK;
import com.lkl.laop.sdk.exception.SDKException;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import java.io.FileNotFoundException;
import java.net.URL;
import java.net.URLDecoder;

/**
 * @author Sakura
 * @date 2025/4/29 16:58
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
public class LklSdkAutoConfig {

    private final LklSdkProperties properties;

    @PostConstruct
    public void initLklSdk() {
        try {
            Config config = new Config();
            config.setAppId(properties.getAppId());
            config.setSerialNo(properties.getSerialNo());
            config.setPriKeyPath(getPath(properties.getPriKeyPath()));
            config.setLklCerPath(getPath(properties.getLklCerPath()));
            config.setLklNotifyCerPath(getPath(properties.getLklNotifyCerPath()));
            config.setSm4Key(properties.getSm4Key());
            config.setServerUrl(properties.getServerUrl());

            LKLSDK.init(config);
            log.info("拉卡拉 SDK 初始化成功");
        } catch (SDKException e) {
            log.error("拉卡拉 SDK 初始化失败", e);
            throw new RuntimeException("拉卡拉 SDK 初始化失败", e);
        }
    }

    private String getPath(String resourceName) {
        try {
            URL url = this.getClass().getClassLoader().getResource(resourceName);
            if (url == null) {
                throw new FileNotFoundException(resourceName + " not found in classpath");
            }
            return URLDecoder.decode(url.getPath(), "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException("找不到资源文件: " + resourceName, e);
        }
    }
}

上面这个方法应该是正常的,我也用下面的方法检测了容器中是否注册了这个类,最后都发类注册成功了,@PostConstruct 注解没有正常执行

@SpringBootApplication
public class Application implements CommandLineRunner {

    @Autowired
    private ApplicationContext context;

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

    @Override
    public void run(String... args) {
        String[] beans = context.getBeanDefinitionNames();
        Arrays.sort(beans);
        for (String bean : beans) {
            if (bean.toLowerCase().contains("lkl")) {
                System.out.println("Bean: " + bean);
            }
        }
    }
}

然后就换了一种方式 InitializingBean,写法如下

import com.lkl.laop.sdk.Config;
import com.lkl.laop.sdk.LKLSDK;
import com.lkl.laop.sdk.exception.SDKException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration;

import java.io.FileNotFoundException;
import java.net.URL;
import java.net.URLDecoder;

/**
 * @author Sakura
 * @date 2025/4/29 16:58
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
public class LklSdkAutoConfig implements InitializingBean {

    private final LklSdkProperties properties;

    @Override
    public void afterPropertiesSet() {
        log.info("afterPropertiesSet 初始化...");
        try {
            Config config = new Config();
            config.setAppId(properties.getAppId());
            config.setSerialNo(properties.getSerialNo());
            config.setPriKeyPath(getPath(properties.getPriKeyPath()));
            config.setLklCerPath(getPath(properties.getLklCerPath()));
            config.setLklNotifyCerPath(getPath(properties.getLklNotifyCerPath()));
            config.setSm4Key(properties.getSm4Key());
            config.setServerUrl(properties.getServerUrl());

            LKLSDK.init(config);
            log.info("拉卡拉 SDK 初始化成功");
        } catch (SDKException e) {
            log.error("拉卡拉 SDK 初始化失败", e);
            throw new RuntimeException("拉卡拉 SDK 初始化失败", e);
        }
    }

    private String getPath(String resourceName) {
        try {
            URL url = this.getClass().getClassLoader().getResource(resourceName);
            if (url == null) {
                throw new FileNotFoundException(resourceName + " not found in classpath");
            }
            return URLDecoder.decode(url.getPath(), "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException("找不到资源文件: " + resourceName, e);
        }
    }
}

我这边换成 InitializingBean 方式后就能正常初始化了,当然还有其它的几种方式,下面简单介绍一下

1.实现 InitializingBean 接口
优点:安全、明确,依赖注入后执行,适合有初始化依赖的组件。
推荐度:⭐⭐⭐⭐⭐

public class MyBean implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化逻辑
    }
}

2.使用 @PostConstruct 注解
优点:语义明确,常用于简单初始化。
缺点:受 Spring 代理机制影响(如某些 AOP 或 CGLIB 场景会失效)。
推荐度:⭐⭐⭐(简单场景下使用)

@PostConstruct
public void init() {
    // 初始化逻辑
}

3.使用 @Bean(initMethod = “…”) 指定初始化方法
优点:对第三方类/非 Spring Bean 有控制权。
推荐度:⭐⭐⭐(适用于工厂方法创建的 Bean)

@Bean(initMethod = "init")
public MyService myService() {
    return new MyService();
}

4.实现 ApplicationRunner 或 CommandLineRunner 接口
用途:程序完全启动后执行任务(如注册服务、检查状态等)。
推荐度:⭐⭐⭐⭐(用于延迟初始化、异步启动)

@Component
public class MyRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        // 程序启动后执行(可做初始化任务)
    }
}

5.监听 Spring 生命周期事件:ApplicationListener
适用:容器级别的监听与操作。
推荐度:⭐⭐⭐(用于复杂初始化或框架级支持)

@Component
public class MyInitListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // Spring 容器刷新完成后执行
    }
}

6.@ConfigurationProperties + @EnableConfigurationProperties
搭配方式:与 @Component, @Configuration, @Bean 联合使用。
推荐度:⭐⭐⭐⭐⭐(配置驱动初始化的核心机制)

@ConfigurationProperties(prefix = "my")
public class MyProperties {
    private String key;
    ...
}

网站公告

今日签到

点亮在社区的每一天
去签到