简体   繁体   English

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

[英]Spring Boot Inject Bean Map with Generic Type

Spring Framework 5弹簧框架 5

I have an instance of a Database Entity, and I want to use the corrosponding repository for that entity.我有一个数据库实体的实例,我想使用该实体的对应存储库。 How do I inject all JPA Repositories mapped by the entity as the key?如何注入实体映射的所有 JPA 存储库作为键?

For example:例如:

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

And in my service component:在我的服务组件中:

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();
  }
}

I want jpaRepositories to be injected with these values:我希望jpaRepositories被注入这些值:

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

That kind of mapping is not available at runtime by default.默认情况下,这种映射在运行时不可用。 If you like a hacky approach and REALLY need it, you can create a map like this for JPA repositories:如果您喜欢 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);
    }
}

Here's my solution:这是我的解决方案:

@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 has ResolvableType as a utility to get generic types of classes. Spring 有ResolvableType作为获取类的泛型类型的实用程序。

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

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