英文:
Spring Boot Inject Bean Map with Generic Type
问题
Spring Framework 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 }
英文:
Spring Framework 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?
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:
{ AEntity.class, ARepository }, { BEntity.class, BRepository }
答案1
得分: 0
以下是翻译好的代码部分:
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);
	}
}
请注意,我已经将代码部分进行了翻译,确保代码逻辑和结构的一致性。如果您有任何其他问题或需要进一步的帮助,请随时提问。
英文:
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:
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);
	}
}
答案2
得分: 0
这是我的解决方案:
    @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作为一个实用工具来获取类的泛型类型。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论