繁体   English   中英

Spring Boot 使用泛型类型注入 Bean Map

[英]Spring Boot Inject Bean Map with Generic Type

弹簧框架 5

我有一个数据库实体的实例,我想使用该实体的对应存储库。 如何注入实体映射的所有 JPA 存储库作为键?

例如:

public interface ARepository extends JpaRepository<AEntity, Long> {}
public interface BRepository extends JpaRepository<BEntity, Long> {}

在我的服务组件中:

public class TestService {
  @Autowired
  private Map<Class, JpaRepository<?, ?>> jpaRepositories;

  public <T> List<T> findAll(T entity) {
    Class entityClass = entity.getClass();
    JpaRepository<?, ?> jpaRepository = jpaRepositories.get(entityClass);
    return jpaRepository.findAll();
  }
}

我希望jpaRepositories被注入这些值:

{ AEntity.class, ARepository }, { BEntity.class, BRepository }

默认情况下,这种映射在运行时不可用。 如果您喜欢 hacky 方法并且真的需要它,您可以为 JPA 存储库创建这样的映射:

package com.example.repo;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.util.ProxyUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class AppRunner implements ApplicationRunner {
    private static final Logger log = LoggerFactory.getLogger(AppRunner.class);

    private final Collection<JpaRepository<?, ?>> jpaRepositories;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Function<JpaRepository<?, ?>, Class<?>> repositoryClass = r -> {
            if (SimpleJpaRepository.class.isAssignableFrom(ProxyUtils.getUserClass(r))) {
                Method method = ReflectionUtils.findMethod(SimpleJpaRepository.class, "getDomainClass");
                ReflectionUtils.makeAccessible(method);
                return (Class<?>) ReflectionUtils.invokeMethod(method, AopProxyUtils.getSingletonTarget(r));
            }
            
            return Void.class;
        };
        Map<Class<?>, JpaRepository<?, ?>> jpaRepositoriesByClass = jpaRepositories.stream().collect(Collectors.toMap(repositoryClass, Function.identity()));
        log.info("{}", jpaRepositoriesByClass);
    }
}

这是我的解决方案:

@Bean
public Map<Class<?>, JpaRepository<?, ?>> jpaRepositoryMap(
        List<JpaRepository<?, ?>> jpaRepositories
) {
  return jpaRepositories.stream()
          .collect(Collectors.toMap(
                  jpaRepository -> resolveGenericType(jpaRepository, 0),
                  Function.identity()
          ));
}

private Class<?> resolveGenericType(Object object, int index) {
  return ResolvableType.forClass(JpaRepository.class, object.getClass())
          .getGenerics()[index].resolve();
}

Spring 有ResolvableType作为获取类的泛型类型的实用程序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM