Spring Boot中调用SongRepo方法时this.songRepo为何为null?
我需要实现服务层查询PostgreSQL数据库所有歌曲的功能,已在服务层实现SongServiceInterface接口,但在main方法实例化SongService调用方法,或通过/songs端点发送HTTP请求时,均抛出空指针异常:
Caused by: java.lang.NullPointerException: Cannot invoke "com.techm.react.Wasteland.repository.SongRepo.findAll()" because "this.songRepo" is null.
注意:应用启动时可正常将对象持久化到数据库,但调用repo/service内的方法会触发空字段错误。
实体类(Model)
package com.techm.react.Wasteland.models; import lombok.*; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; import javax.persistence.*; import java.sql.Time; @Entity @Table(name = "song") @NoArgsConstructor @AllArgsConstructor @Getter @Setter @EqualsAndHashCode @ToString public class Song { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @Column(name = "title") private String title; @JoinColumn(name = "album") @ManyToOne @OnDelete(action = OnDeleteAction.CASCADE) private Album album; @Column(name = "artists") private String artists; @Column(name = "track") private int track; @Column(name = "track_length") private Time length; }
数据传输对象(DTO)
package com.techm.react.Wasteland.dto; import com.techm.react.Wasteland.models.Album; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.sql.Time; @Data @NoArgsConstructor @AllArgsConstructor public class SongDTO { private String title; private AlbumDTO album; private String artists; private int track; private Time length; }
仓库接口(Repo)
package com.techm.react.Wasteland.repository; import com.techm.react.Wasteland.models.Song; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface SongRepo extends JpaRepository<Song, Integer> { public abstract List<Song> findAll(); public abstract List<Song> findByArtist(); public abstract Song findByTrack(); }
服务类(Service)
package com.techm.react.Wasteland.service; import com.techm.react.Wasteland.dto.SongDTO; import com.techm.react.Wasteland.models.Song; import com.techm.react.Wasteland.repository.SongRepo; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service @Configurable public class SongService implements SongServiceInterface{ @Autowired SongRepo songRepo; @Autowired private ModelMapper modelMapper; public List<SongDTO> findAllSongs() { List<SongDTO> songDTOS = new ArrayList<>(); List<Song> songs = songRepo.findAll(); for(Song s: songs) { songDTOS.add(modelMapper.map(s, SongDTO.class)); } return songDTOS; } public SongDTO getSongByTitle(String title) throws NoSuchFieldException { SongDTO song = new SongDTO(); if(title == song.getTitle()){ return song; } else throw new NoSuchFieldException("The song by that title does not exist"); } public SongDTO findByTrack(int track) { SongDTO song = new SongDTO(); if(song.getTrack() == track) { return song; } return null; } }
主启动类(Main)
package com.techm.react.Wasteland; import com.techm.react.Wasteland.controller.AlbumController; import com.techm.react.Wasteland.controller.SongController; import com.techm.react.Wasteland.service.SongService; import org.modelmapper.ModelMapper; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication @ComponentScan({"com.server", "com.server.config"}) @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) public class WastelandApplication { @Bean public ModelMapper modelMapper() { ModelMapper modelMapper = new ModelMapper(); return modelMapper; } public static void main(String[] args) { SpringApplication.run(WastelandApplication.class, args); SongService songService = new SongService(); System.out.println(songService.findAllSongs()); } }
问题根源
- 手动实例化服务类导致依赖注入失效:main方法中用
new SongService()创建对象,该对象不属于Spring容器管理,Spring无法自动注入SongRepo和ModelMapper,导致songRepo为null。 - 组件扫描路径错误:
@ComponentScan指定的{"com.server", "com.server.config"}未覆盖代码实际包路径com.techm.react.Wasteland,Spring无法扫描到Repo、Service等组件,这些Bean无法被容器管理。 - 错误排除数据源自动配置:
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})禁用了Spring Boot的数据源自动配置,会导致JPA相关组件初始化异常,间接影响Repo注入。
修复步骤
修正组件扫描路径:
移除自定义@ComponentScan(@SpringBootApplication默认扫描主类所在包及其子包,足够覆盖所有组件);若需保留,需补充实际包路径:@ComponentScan({"com.techm.react.Wasteland", "com.server", "com.server.config"})恢复数据源自动配置:
删除@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}),确保Spring Boot自动配置数据源和JPA组件,让SongRepo正常被容器创建并注入。从Spring容器获取服务实例:
main方法中不要手动new对象,通过应用上下文获取Bean:public static void main(String[] args) { ApplicationContext context = SpringApplication.run(WastelandApplication.class, args); SongService songService = context.getBean(SongService.class); System.out.println(songService.findAllSongs()); }优化仓库接口:
JpaRepository已自带findAll()方法,无需重复声明;同时修正参数缺失的查询方法,符合Spring Data JPA命名规范:public interface SongRepo extends JpaRepository<Song, Integer> { List<Song> findByArtists(String artists); Song findByTrack(int track); Song findByTitle(String title); }修复服务类业务逻辑:
修正getSongByTitle和findByTrack的错误逻辑,改为调用Repo查询数据库:public SongDTO getSongByTitle(String title) throws NoSuchFieldException { Song song = songRepo.findByTitle(title); if(song != null){ return modelMapper.map(song, SongDTO.class); } throw new NoSuchFieldException("The song by that title does not exist"); } public SongDTO findByTrack(int track) { Song song = songRepo.findByTrack(track); return song != null ? modelMapper.map(song, SongDTO.class) : null; }
内容的提问来源于stack exchange,提问作者Shayne McPherson

