Spring Boot升级后自定义Repository实现及BeanFactory获取问题
我刚好在之前的项目里处理过Spring Boot 1.4.x到1.5.x的升级,碰到过和你一模一样的问题——Spring Data Commons 1.13.x确实对Repository工厂的扩展逻辑做了不少调整,不过别担心,完全可以实现你要的「针对实现Special接口的实体用自定义Repository,其他用默认实现」,同时还能正常获取BeanFactory,下面是一步步的解决方案:
1. 重构自定义RepositoryFactory
新版本的JpaRepositoryFactory对getTargetRepository方法的签名做了调整,现在需要基于RepositoryInformation来创建实例,而且我们可以通过构造函数直接传入BeanFactory,不用再靠反射硬塞参数:
public class MyCustomRepositoryFactory extends JpaRepositoryFactory { private final BeanFactory beanFactory; // 构造函数注入EntityManager和BeanFactory public MyCustomRepositoryFactory(EntityManager entityManager, BeanFactory beanFactory) { super(entityManager); this.beanFactory = beanFactory; } @Override protected Object getTargetRepository(RepositoryInformation repoInfo) { Class<?> entityType = repoInfo.getDomainType(); // 判断当前实体是否实现Special接口 if (Special.class.isAssignableFrom(entityType)) { // 用框架提供的反射方法创建自定义实现,自动匹配构造函数参数 return getTargetRepositoryViaReflection(repoInfo, getEntityManager(), beanFactory); } // 非Special实体返回默认的SimpleJpaRepository return super.getTargetRepository(repoInfo); } @Override protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { Class<?> entityType = metadata.getDomainType(); return Special.class.isAssignableFrom(entityType) ? MyCustomRepositoryImpl.class : super.getRepositoryBaseClass(metadata); } }
2. 调整RepositoryFactoryBean获取BeanFactory
新版本的JpaRepositoryFactoryBean实现了BeanFactoryAware接口,我们可以通过重写setBeanFactory方法直接拿到BeanFactory,再传递给自定义的RepositoryFactory:
public class MyCustomJpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> { private BeanFactory beanFactory; public MyCustomJpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) { super(repositoryInterface); } @Override protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { // 把EntityManager和BeanFactory一起传给自定义工厂 return new MyCustomRepositoryFactory(entityManager, beanFactory); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { super.setBeanFactory(beanFactory); this.beanFactory = beanFactory; } }
3. 更新自定义RepositoryImpl的构造函数
让MyCustomRepositoryImpl的构造函数接收BeanFactory参数,这样就能在里面直接使用Spring容器里的Bean了:
public class MyCustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements MyCustomRepository<T, ID> { private final BeanFactory beanFactory; private final EntityManager entityManager; // 构造函数注入必要的参数 public MyCustomRepositoryImpl(JpaEntityInformation<T, ID> entityInfo, EntityManager entityManager, BeanFactory beanFactory) { super(entityInfo, entityManager); this.entityManager = entityManager; this.beanFactory = beanFactory; } // 重写需要定制的方法,比如save @Override public <S extends T> S save(S entity) { if (entity instanceof Special) { // 这里可以用BeanFactory获取你需要的服务,做定制化处理 SpecialEntityHandler handler = beanFactory.getBean(SpecialEntityHandler.class); handler.processBeforeSave((Special) entity); } return super.save(entity); } // 其他需要重写的find、delete方法同理... }
4. 配置Spring使用自定义FactoryBean
最后在你的JPA配置类里,指定使用我们的自定义FactoryBean:
@Configuration @EnableJpaRepositories( repositoryFactoryBeanClass = MyCustomJpaRepositoryFactoryBean.class, basePackages = "com.your.project.repositories" // 替换成你的Repository包路径 ) public class JpaConfiguration { // 其他JPA相关配置(比如EntityManagerFactory、TransactionManager)保持不变 }
为什么原来的方式不行?
Spring Data Commons 1.13.x对Repository工厂的扩展做了规范,原来的一些方法签名被调整,而且官方更推荐通过BeanFactoryAware接口来获取BeanFactory,替代之前的反射注入方式,这样既符合Spring的规范,也避免了版本升级带来的兼容性问题。
内容的提问来源于stack exchange,提问作者Guy Smorodinsky

