SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)

本文介绍如何利用Spring框架的@PostConstruct注解解决项目启动时Manager类中Mapper接口注入为null的问题,实现优雅的数据初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 说明

有些时候我们需要一个管理类,类似 xxxManager 来处理共享的基础数据,它要在项目启动时就进行初始化且查询数据库,而且查询语句不复杂,写一整套的Service或者使用MyBatis的查询API有点儿繁琐,此时写一个简单的Mapper接口就优雅许多。

2. 代码实现

为了简洁,删掉部分不必要的备注,下边是Mapper接口:

/**
 * 集中管理非单表操作的SQL
 */
@Mapper
public interface ComplexSqlMapper {
    // 查询 view_manager 表全部数据
    @Select(" select * from view_manager ")
    List<ViewEntity> getViewManagerAllList();
}

下面是管理类,这里没有使用Doc注释:

/**
 * 查询并处理 view_manager 表数据(为了简洁这里是示例代码 真正的处理要比这个复杂)
 */
@Component
public class ViewManager {
	// Mapper 注入
	@Resource
    private ComplexSqlMapper complexSqlMapper;
    // 处理结果保存
    private Map<String, String> views = new HashMap();
    // 初始化时查询 view_manager 表数据
    private ViewManager() {
    	List<ViewEntity> viewEntityList= complexSqlMapper.getViewManagerAllList();
    	// 处理数据
    	viewEntityList.forEach(ve-> {
            views.put(ve.getId(), ve.getName());
        });
    }
}

3. 原因解析

项目启动时有NPE,Debug才发现 complexSqlMapper 是null。我首先想到的是 Mapper 文件没有被检测到,然后确认了启动类里的 org.mybatis.spring.annotation.MapperScan 信息:

@SpringBootApplication
@MapperScan(basePackages = "com.example.demo.**.mapper")
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

并没有写错路径,那我们仔细回看 ViewManager 类就会发现 complexSqlMapper 是当作类的成员变量被注入的,那也就是说需要先实例化 ViewManager 类然后再注入 complexSqlMapper,可是我们在类的构造器里就调用了 complexSqlMapper 此时构造器没有执行完,ViewManager 也就没被实例化,那么 complexSqlMapper 自然是个null。至此通过构造器使用 Mapper 查询数据的计划落空了。

4. 问题解决

那就没有方法了吗 ❓ 怎么可能 ❗️ Spring早就想到了你想到的 😏 使用 @PostConstruct 注解的方法将会在依赖注入完成后被自动调用。

改造后的代码【这里只贴出核心代码 无关备注也省略了】

@Component
public class ViewManager {

 	@Resource
    private ComplexSqlMapper complexSqlMapper;
    private Map<String, String> views = new HashMap();

    @PostConstruct
    private void init() {
        List<ViewEntity> viewEntityList= complexSqlMapper.getViewManagerAllList();
        viewEntityList.forEach(ve-> {
            views.put(ve.getId(), ve.getName());
        });
	}
}

改造后的代码,项目启动时由于 @Component 注解,ViewManager 类会被实例化并交给容器管理,此时使用的是 ViewManager 类的无参构造器。而 @PostConstruct 注解的 init 方法会待 complexSqlMapper 注入成功后调用。至此,xxxManager 的功能得以实现。

在Spring Boot的单元测试中,如果你使用`@Resource`注解进行依赖注入,发现注入的对象总是为`null`,可能是由于以下几个原因: 1. **缺少@RunWith(SpringRunner.class)**:你需要使用`SpringRunner`运行测试,这是Spring Boot提供的测试runner,它会启动一个完整的Spring应用上下文来处理依赖注入。 ```java @RunWith(SpringRunner.class) public class YourTestClass { // ... } ``` 2. **非扫描路径**:确保测试位于Spring扫描路径下,或者手动添加到`@ComponentScan`中,以便Spring能够找到并创建相应的bean。 3. **MockBean代替**:如果测试的是某个特定的bean,并不想实际注入,可以使用`@MockBean`或`@SpyBean`创建mock对象。 4. **延迟初始化**:某些bean可能需要在应用程序生命周期的后期才完成初始化,此时可以在构造函数或者`@PostConstruct`方法中尝试获取资源。 5. **测试环境配置**:检查是否设置了正确的环境(如生产、开发),因为不同的环境可能对bean的加载策略有所不同。 6. **@Autowired排除**:有时候,可能其他配置导致了资源冲突,这时你可以考虑在`@Autowired`前加上`qualifiers`属性,指定特定的bean来避免冲突。 7. **显式声明**:如果`@Resource`无法定位到bean,尝试显式地通过`ApplicationContext`获取,例如 `ApplicationContext context = SpringApplication.getApplication().getApplicationContext();` 然后再注入
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

yuanzhengme.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值