Spring Boot工厂模式下RouteFactory Bean注入失败及空指针问题
我在Spring Boot应用中结合工厂设计模式与JpaRepository实现Route(道路)的创建,遇到了依赖注入和空指针相关问题,以下是详细代码与错误信息:
实体类Route
@Entity @Table(name = "route", schema="location") @Getter @Setter @Data @AllArgsConstructor @NoArgsConstructor public class Route implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "route_generator") private long id; @Column(name = "route_type") private String routeType; @Column(name = "route_name") private String routeName; @Column(name = "route_width") private long routeWidth; @Column(name = "description") private String description; }
RouteRepository仓库接口
public interface RouteRepository extends JpaRepository<Route, Long>{ Iterable<Route> findByRouteType(String route_type); int createRouteByType(Route route); int deleteRouteByRouteType(String routeType); //List<Route> findRoutesByQuartierId(Long quartierId); }
RouteFactory工厂接口
public interface RouteFactory { public <T> RouteRepository getRouteService(String routeType); }
初始服务类实现(以nationalRoad为例)
@Service("nationaleRoad") public class CountryRouteService implements RouteRepository{ @Override public int createRouteByType(Route route) { Route _route = new Route(); _route.setRouteType("nationaleRoad"); _route.setRouteName(route.getRouteName()); _route.setRouteName(route.getRouteWidth()*4); // 注:此处重复调用setRouteName,应为setRouteWidth _route.setDescription(route.getDescription()); save(_route); return 1 ; }; @Override public int deleteRouteByRouteType(String routeType) { // TODO Auto-generated method stub return 0; } // 其他JpaRepository方法的实现... }
regionalRoad类型的服务类结构类似,但存在类名重复(同样命名为CountryRouteService)的问题。
初始控制器代码
@Slf4j @CrossOrigin(origins = "http://localhost:8080") @RestController @RequestMapping("/api") public class RouteController { private static final Logger log = LoggerFactory.getLogger(RouteController.class); private final RouteFactory factory; public RouteController(RouteFactory factory) { this.factory = factory; } @PostMapping("/route/") public void createRoute(@RequestBody Route route) { log.info("Logging the Routefactory {}", factory); factory.getRouteService(route.getRouteType()).createRouteByType(route); System.out.println("Successfully Published the road = ' " + route + " ' to the RegistrationTopic"); } }
初始错误信息
No qualifying bean of type 'com.emura_group.country_geo_tracing.factory.RouteFactory' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
伴随日志:
Bean creation exception on singleton FactoryBean type check: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration': No bean named 'org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry' available.
已尝试的无效操作:
- 将控制器构造注入改为
@Autowired RouteFactory factory - 给RouteFactory添加
@Service/@Component/@Repository注解
修改后的代码(编辑后)
修改后的服务类(不再实现RouteRepository)
@Service("nationaleRoad") public class CountryRouteService{ @Autowired RouteRepository routeRepository; public int createRouteByType(Route route) { Route _route = new Route(); _route.setRouteType("nationaleRoad"); _route.setRouteName(route.getRouteName()); _route.setRouteName(route.getRouteWidth()*4); // 仍存在重复setRouteName的问题 _route.setDescription(route.getDescription()); routeRepository.save(_route); return 1 ; }; public int deleteRouteByRouteType(String routeType) { // TODO Auto-generated method stub return 0; } // 其他方法... }
修改后的控制器
@Slf4j @CrossOrigin(origins = "http://localhost:8080") @RestController @RequestMapping("/api") public class RouteController { private static final Logger log = LoggerFactory.getLogger(RouteController.class); RouteFactory factory; @PostMapping("/route/") public void createRoute(@RequestBody Route route) { log.info("Logging the Routefactory {}", factory); factory.getRouteService(route.getRouteType()).createRouteByRouteType(route); System.out.println("Successfully created the road = ' " + route + " ' to the RegistrationTopic"); } }
修改后新错误
java.lang.NullPointerException: Cannot invoke "com.emura_group.country_geo_tracing.factory.RouteFactory.getRouteService(String)" because "this.factory" is null
再次尝试@Autowired注入RouteFactory,回到最初的找不到bean错误。
解决方案
1. 实现RouteFactory的具体类,让Spring能识别并创建Bean
RouteFactory是接口,Spring无法直接实例化,必须提供实现类并标记为Spring组件:
@Component public class RouteFactoryImpl implements RouteFactory { private final Map<String, RouteService> routeServiceMap; // Spring自动注入所有实现RouteService接口的Bean,key为Bean名称 public RouteFactoryImpl(Map<String, RouteService> routeServiceMap) { this.routeServiceMap = routeServiceMap; } @Override public RouteService getRouteService(String routeType) { RouteService service = routeServiceMap.get(routeType); if (service == null) { throw new IllegalArgumentException("不支持的路由类型: " + routeType); } return service; } }
2. 定义统一的RouteService接口,规范服务类
解决之前服务类类名重复、职责混乱的问题,定义统一接口:
public interface RouteService { int createRouteByType(Route route); int deleteRouteByRouteType(String routeType); }
然后让各路由类型的服务实现该接口:
@Service("nationaleRoad") public class NationalRouteService implements RouteService { @Autowired private RouteRepository routeRepository; @Override public int createRouteByType(Route route) { Route _route = new Route(); _route.setRouteType("nationaleRoad"); _route.setRouteName(route.getRouteName()); _route.setRouteWidth(route.getRouteWidth() * 4); // 修正之前的setRouteName错误 _route.setDescription(route.getDescription()); routeRepository.save(_route); return 1; } @Override public int deleteRouteByRouteType(String routeType) { return routeRepository.deleteRouteByRouteType(routeType); } }
@Service("regionaleRoad") public class RegionalRouteService implements RouteService { @Autowired private RouteRepository routeRepository; @Override public int createRouteByType(Route route) { Route _route = new Route(); _route.setRouteType("regionaleRoad"); _route.setRouteName(route.getRouteName()); _route.setRouteWidth(route.getRouteWidth() * 2); // 修正setRouteWidth _route.setDescription(route.getDescription()); routeRepository.save(_route); return 1; } @Override public int deleteRouteByRouteType(String routeType) { return routeRepository.deleteRouteByRouteType(routeType); } }
3. 修正RouteFactory接口泛型
调整接口返回统一的RouteService类型,避免类型转换问题:
public interface RouteFactory { RouteService getRouteService(String routeType); }
4. 修正控制器注入方式
使用构造注入(Spring推荐方式)确保RouteFactory被正确注入:
@Slf4j @CrossOrigin(origins = "http://localhost:8080") @RestController @RequestMapping("/api") public class RouteController { private static final Logger log = LoggerFactory.getLogger(RouteController.class); private final RouteFactory factory; public RouteController(RouteFactory factory) { this.factory = factory; } @PostMapping("/route/") public void createRoute(@RequestBody Route route) { log.info("Logging the Routefactory {}", factory); RouteService service = factory.getRouteService(route.getRouteType()); service.createRouteByType(route); System.out.println("Successfully created the road = ' " + route + " '"); } }
5. 修复RouteRepository自定义方法
RouteRepository中的自定义方法需要添加@Query注解实现,比如deleteRouteByRouteType:
@Modifying @Query("DELETE FROM Route r WHERE r.routeType = :routeType") int deleteRouteByRouteType(@Param("routeType") String routeType);
createRouteByType方法建议删除,因为创建逻辑已在服务层实现,避免职责混淆。
6. 解决事务相关日志错误
确保项目引入spring-boot-starter-data-jpa依赖,并配置正确的数据源:
Maven依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
application.properties配置示例:
spring.datasource.url=jdbc:mysql://localhost:3306/your_db spring.datasource.username=root spring.datasource.password=your_password spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true
内容的提问来源于stack exchange,提问作者Himmels DJ

