Autowired注解在跨包自定义SimpleJpaRepository子类中注入失败排查
问题描述
我有一个Spring Boot应用,在handler类里用@Autowired注入com.example.utils.IDGenerator能正常初始化,但在com.example.customutils.JtJPARepositoryImpl(继承SimpleJpaRepository)里同样用@Autowired注入IDGenerator,变量始终为null。
原因分析
- Spring Data JPA Repository的特殊创建机制:
JtJPARepositoryImpl作为Repository的基类,其实例是由Spring Data JPA的动态代理工厂生成的,不属于普通Spring Bean范畴。Spring默认不会对这类动态生成的类执行@Autowired成员变量的注入逻辑。 - 构造函数的特殊性:当前
JtJPARepositoryImpl的构造函数是给Spring Data框架调用的,仅用于传入JpaEntityInformation和EntityManager,框架不会自动处理类中其他@Autowired标注的成员变量。 - 组件扫描不生效:虽然
@ComponentScan包含了com.example.customutils包,但JtJPARepositoryImpl没有标注@Component,Spring不会把它当作普通Bean来处理依赖注入。
解决方案
方案1:改用构造函数注入(推荐)
修改JtJPARepositoryImpl的构造函数,把IDGenerator作为参数传入,Spring Data会在创建Repository实例时自动注入依赖:
public class JtJPARepositoryImpl<T extends BaseEntity, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements JtJPARepository<T, ID>, Serializable { private final IDGenerator idGenerator; // 将IDGenerator加入构造函数 public JtJPARepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager, IDGenerator idGenerator) { super(entityInformation, entityManager); this.idGenerator = idGenerator; } }
注:需确保IDGenerator是Spring容器中的有效Bean(当前已满足,因为handler类可正常注入)。
方案2:手动从ApplicationContext获取Bean
如果不想修改构造函数,可让JtJPARepositoryImpl实现ApplicationContextAware接口,手动获取IDGenerator实例:
public class JtJPARepositoryImpl<T extends BaseEntity, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements JtJPARepository<T, ID>, Serializable, ApplicationContextAware { private IDGenerator idGenerator; @Autowired public JtJPARepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.idGenerator = applicationContext.getBean(IDGenerator.class); } }
方案3:使用@Configurable(需额外配置)
给JtJPARepositoryImpl添加@Configurable注解,结合AspectJ编译时织入,让动态生成的类支持Spring依赖注入。但这种方式需要额外配置AspectJ,步骤繁琐,不推荐作为首选方案。
相关代码片段
com.example.customutils.JtJPARepositoryImpl(原代码)
public class JtJPARepositoryImpl<T extends BaseEntity, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements JtJPARepository<T, ID>, Serializable { @Autowired private IDGenerator idGenerator; @Autowired public JtJPARepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); } }
com.example.serviceapplication.ServiceApplication
@Slf4j @SpringBootApplication @ImportResource({"classpath:applicationContext.xml"}) @ComponentScan(basePackages = { "com.example.serviceapplication", "com.example.utils", "com.example.customutils" }) @EnableJpaRepositories(basePackages = "com.jumbotail.serviceapplication.repository", repositoryBaseClass = JtJPARepositoryImpl.class) public class ServiceApplication { @Inject private ServiceProperties ServiceProperties; private final ApplicationContext applicationContext; public ServiceApplication(ApplicationContext applicationContext) { ServiceProperties baseConfiguration = new ServiceProperties(); baseConfiguration.setDynamoDBConfiguration(new DynamoDBConfiguration(10000,10000)); this.applicationContext = applicationContext; DynamoDBClient dynamoDBClient = new DynamoDBClient(baseConfiguration); ((GenericWebApplicationContext)this.applicationContext).registerBean(BaseConfiguration.class,baseConfiguration); ((GenericWebApplicationContext)this.applicationContext).registerBean(DynamoDBClient.class,baseConfiguration); } public static void main(String[] args) { SpringApplication.run(ServiceApplication.class, args); log.info("Started Service"); } @PostConstruct public void init() { log.info("Initializing application with Service properties- {}", ServiceProperties); ApplicationConfiguration.initialize(ServiceProperties.getEnvironment()); } @Bean public BaseConfiguration getBaseConfiguration() { BaseConfiguration baseConfiguration = new BaseConfiguration(); baseConfiguration.setEnvironment(ServiceProperties.getEnvironment()); return baseConfiguration; } @Bean public HealthCheckConfiguration healthCheckConfiguration() { return serviceProperties.getHealthCheckConfiguration(); } }
com.example.utils.IDGenerator
@Slf4j @Component @Qualifier("IDGenerator") public class IDGenerator { private static final String IDCOUNTER_TABLE = "IDCounter"; private static final String IDCOUNTER_KEY_NAME = "namespace"; private static final String IDCOUNTER_VALUE_NAME = "counter"; private static final String IDCOUNTER_VERSION_NAME = "version"; private final DynamoDBClient dynamoDBClient; public IDGenerator(DynamoDBClient dynamoDBClient) { this.dynamoDBClient = dynamoDBClient; } }
内容的提问来源于stack exchange,提问作者ADA Home
相关产品推荐
相关产品推荐

