SpringBoot调用到期邮件发送接口报emailrepo为null错误
问题根因
报错Cannot invoke "com.emailschedulerfinal.repository.EmailRepository.findDueEmails()" because "this.emailrepo" is null和Repository层查询写法、返回值逻辑无关,核心原因是依赖注入完全失效,你手动创建的Service对象不归Spring容器管理,容器不会为其注入标记了@Autowired的Repository实例。
代码存在的具体问题
- Controller层手动实例化Service:你在
EmailController中通过new SchedulerService(null)创建Service对象,这种方式生成的对象脱离Spring容器管控,对象内所有@Autowired标记的依赖都不会被自动赋值,默认就是null,调用时必然触发空指针。 - Service类未标记为Spring托管Bean:
SchedulerService类上没有添加@Service注解,Spring启动时不会扫描到这个类,自然也不会为其生成可注入的Bean实例。 - 类型匹配和返回值错误(非本次空指针诱因,但修复空指针后会触发其他报错):
- 实体类命名不符合Java大驼峰规范,类名
actionitems首字母小写,JPQL查询时无法匹配到实体类;且实体类主键为int类型,和JpaRepository泛型中声明的Long主键类型不匹配。 - Repository层自定义查询返回值错误:JPQL查询的是单个
email字符串字段,返回值应为List<String>,而非List<String[]>,后续数组转换逻辑会抛出类型转换异常。 JavaMailSender未注入:你手动给Service构造器传入null作为JavaMailSender参数,后续调用邮件发送方法时会再次触发空指针。
- 实体类命名不符合Java大驼峰规范,类名
完整修复方案
按顺序修改对应层代码即可:
- 修正实体类
@Entity @Table(name = "actionitems") // 对应数据库实际表名,无需修改 public class ActionItems { // 类名改为大驼峰命名 @Id @GeneratedValue private Long id; // 主键类型和Repository泛型保持一致 private String action; private String email; private Date duedate; // 补全所有属性的getter、setter }
- 修正Repository层
public interface EmailRepository extends JpaRepository<ActionItems, Long> { // JPQL中使用实体类名和属性名,返回值匹配单字段查询结果 @Query("select a.email from ActionItems a where a.duedate < CURRENT_TIMESTAMP") List<String> findDueEmails(); }
- 修正Service层,添加托管注解,使用构造器注入
@Service // 标记为Spring托管的Service层Bean public class SchedulerService { private final JavaMailSender javaMailSender; private final EmailRepository emailrepo; // 单构造函数场景下Spring自动注入参数,无需额外加@Autowired public SchedulerService(JavaMailSender javaMailSender, EmailRepository emailrepo) { this.javaMailSender = javaMailSender; this.emailrepo = emailrepo; } public List<String> findDueEmailsFromDB() { return emailrepo.findDueEmails(); } public void sendEmailIds() { List<String> toMailList = findDueEmailsFromDB(); String[] toMails = toMailList.toArray(new String[0]); System.out.println("查询到的待通知邮箱:"); for (String mail : toMails) { System.out.println(mail); } SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setTo(toMails); mailMessage.setSubject("到期事项提醒"); mailMessage.setText("您有待处理的到期事项,请及时跟进。"); mailMessage.setFrom("test@gmail.com"); javaMailSender.send(mailMessage); } }
- 修正Controller层,注入托管的Service实例,禁止手动new
@RestController public class EmailController { private final SchedulerService schedulerService; // 构造器注入Spring管理的Service Bean public EmailController(SchedulerService schedulerService) { this.schedulerService = schedulerService; } @RequestMapping("/send-due-emails") public String send() { try { schedulerService.sendEmailIds(); } catch (MailException mailException) { mailException.printStackTrace(); } return "邮件发送任务执行完成"; } }
注意事项
- Spring项目中,所有被
@RestController、@Service、@Repository、@Component标记的托管Bean,禁止手动通过new关键字实例化使用,手动创建的对象不会走容器的依赖注入流程,内部依赖全部为null。 - Spring Data JPA默认使用JPQL编写查询语句,语法规则是面向实体类而非数据库表,需要和你定义的实体类名、实体属性名完全对应,和数据库表名、字段名没有直接关联。
内容的提问来源于stack exchange,提问作者user19211969
相关产品推荐
相关产品推荐

