依赖包@PostConstruct初始化无效排查

问题描述

业务包引用了一个公共包,公共包里使用了@PostConstruct赋值属性,项目启动后报错,断点排查项目启动时未进入此方法,多方排查后,发现是启动类未添加@ComponentScan注解,导致类注册失败。

@Component
public class Test {
    @Value("${basename:language}")
    private String baseName;
    private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN");
    private static final Locale LOCALE_EN_US = new Locale("en", "US");

    private static ResourceBundle RESOURCE_ZH_CN;
    private static ResourceBundle RESOURCE_EN_US;

    @PostConstruct
    private void init() {
        String BASE_NAME = this.baseName;
        RESOURCE_ZH_CN = ResourceBundle.getBundle(BASE_NAME, LOCALE_ZH_CN);
        RESOURCE_EN_US = ResourceBundle.getBundle(BASE_NAME, LOCALE_EN_US);
    }
}    

原因

在Spring框架中,@ComponentScan 注解用于指定Spring在初始化时应该扫描哪些包以查找带有 @Component 及其衍生注解(如 @Service, @Repository, @Controller 等)的类。如果你没有在配置中明确添加 @ComponentScan 注解,Spring将默认只扫描主应用类(带有 @SpringBootApplication 注解的类)所在的包及其子包。

如果你的组件(即被 @Component 等注解标记的类)位于主应用类所在包的外部或不在其子包路径下,Spring将无法自动发现这些组件,因此这些组件不会被自动注册到Spring容器中。

扩展

当你使用 @SpringBootApplication 注解一个应用启动类时,这个注解实际上是一个复合注解,它包括 @SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan。如果你没有在 @ComponentScan 中显式指定 basePackages 或 basePackageClasses,则Spring Boot 默认扫描启动类所在的包及其子包。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    // 其他属性和方法
}

如果 basePackages 和 basePackageClasses 都是默认值(空),Spring将使用声明 @ComponentScan 的类的包作为扫描的起点。

源码中的实现
在Spring Boot的自动配置过程中,@SpringBootApplication 注解的处理是关键。这个注解包含了 @ComponentScan,而且通常不需要显式指定扫描路径。当Spring应用上下文初始化时,它会创建一个 AnnotationConfigApplicationContext 或 AnnotationConfigServletWebServerApplicationContext,这取决于你的应用类型。在这个过程中,Spring会解析配置类,包括 @ComponentScan 注解。

具体到源码实现,Spring使用 ClassPathBeanDefinitionScanner 类来执行实际的路径扫描。这个类在初始化时会接收一个 BeanDefinitionRegistry,用于注册扫描到的bean定义。扫描过程是通过调用 scan 方法实现的,该方法最终会调用 findCandidateComponents 方法来查找路径下所有符合条件的类。

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            // 注册过程
        }
    }
    return beanDefinitions;
}
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jc_hsx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值