如何在Entity实体类中通过@Autowired注入RestTemplate Bean
问题原因
@Autowired 注入RestTemplate为null的核心原因是JPA实体类的生命周期由JPA持久层框架(如Hibernate)管理,不归Spring IoC容器管控:不管是Hibernate从数据库查询组装实体实例,还是手动new ICOrder(),Spring都不会参与这些实例的创建过程,自然不会为标注了@Autowired的属性完成依赖注入,哪怕加了@Transient标记该字段不参与表映射也不会生效。
实现方案
推荐方案:拆分逻辑到Spring托管的业务层
这是最符合分层设计规范的实现,无额外耦合:
- 将实体类
start方法中调用REST服务的逻辑,迁移到@Service标注的业务层组件中。Service本身由Spring管理,可以直接注入配置好的RestTemplate实例,不会出现注入为null的问题 - 实体类只保留和数据库字段映射、核心领域数据计算相关的逻辑,不要耦合外部REST/RPC调用这类基础设施逻辑,后续单元测试Mock也更方便
示例业务层代码:
@Service public class OrderService { @Autowired private RestTemplate restTemplate; public void processOrderStart(ICOrder order, Context context, String userId) { // 在此处调用restTemplate完成REST请求,处理完成后再操作实体即可 // ResponseEntity<?> resp = restTemplate.postForEntity(...); order.start(context, userId); } }
兜底方案:手动从Spring上下文获取Bean(非特殊场景不推荐)
如果确实要把REST调用逻辑留在实体类中不想拆分,可以通过Spring上下文工具类手动获取容器中的RestTemplate Bean,不需要自己new实例:
- 编写Spring上下文持有工具类,交给Spring管理:
@Component public class SpringContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext ctx) throws BeansException { applicationContext = ctx; } public static <T> T getBean(Class<T> clazz) { return applicationContext.getBean(clazz); } }
- 调整实体类代码,去掉字段上的@Autowired注解,在方法内手动获取Bean:
@Entity @Table(name = "IC_ORDER") public class ICOrder implements Serializable { @Id @GenericGenerator(name = "UUIDGenerator", strategy = "uuid2") @GeneratedValue(generator = "UUIDGenerator") @Type(type = "uuid-char") UUID id; @Transient private RestTemplate restTemplate; @SneakyThrows int start(Context context,String userId) { // 直接获取容器中配置好的RestTemplate实例 RestTemplate restTemplate = SpringContextHolder.getBean(RestTemplate.class); // 调用restTemplate.postForEntity执行业务即可 } }
注意:这种方式会让实体类强耦合Spring框架,单元测试时需要额外Mock Spring上下文,除非是存量代码无法拆分的场景,否则不要这么写。
另外还有一种基于Hibernate @Configurable注解+AspectJ编译期织入的实现方式,可以让Hibernate创建实体实例时自动完成Spring依赖注入,但需要额外引入AspectJ依赖、配置编译织入规则,改造成本很高,常规业务开发不建议使用。
不要直接在实体类中通过new RestTemplate()创建实例,这种方式生成的对象不会加载你在配置类中通过RestTemplateBuilder添加的消息转换器、拦截器、连接池配置,和Spring容器中托管的RestTemplate实例行为不一致。
内容的提问来源于stack exchange,提问作者VKP
相关产品推荐
相关产品推荐

