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

huangapple go评论73阅读模式
英文:

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&lt;AEntity, Long&gt; {}
public interface BRepository extends JpaRepository&lt;BEntity, Long&gt; {}

And in my service component:

public class TestService {
  @Autowired
  private Map&lt;Class, JpaRepository&lt;?, ?&gt;&gt; jpaRepositories;

  public &lt;T&gt; List&lt;T&gt; findAll(T entity) {
    Class entityClass = entity.getClass();
    JpaRepository&lt;?, ?&gt; 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&lt;JpaRepository&lt;?, ?&gt;&gt; jpaRepositories;

	@Override
	public void run(ApplicationArguments args) throws Exception {
		Function&lt;JpaRepository&lt;?, ?&gt;, Class&lt;?&gt;&gt; repositoryClass = r -&gt; {
			if (SimpleJpaRepository.class.isAssignableFrom(ProxyUtils.getUserClass(r))) {
				Method method = ReflectionUtils.findMethod(SimpleJpaRepository.class, &quot;getDomainClass&quot;);
				ReflectionUtils.makeAccessible(method);
				return (Class&lt;?&gt;) ReflectionUtils.invokeMethod(method, AopProxyUtils.getSingletonTarget(r));
			}
			
			return Void.class;
		};
		Map&lt;Class&lt;?&gt;, JpaRepository&lt;?, ?&gt;&gt; jpaRepositoriesByClass = jpaRepositories.stream().collect(Collectors.toMap(repositoryClass, Function.identity()));
		log.info(&quot;{}&quot;, 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&lt;Class&lt;?&gt;, JpaRepository&lt;?, ?&gt;&gt; jpaRepositoryMap(
List&lt;JpaRepository&lt;?, ?&gt;&gt; jpaRepositories
) {
return jpaRepositories.stream()
.collect(Collectors.toMap(
jpaRepository -&gt; resolveGenericType(jpaRepository, 0),
Function.identity()
));
}
private Class&lt;?&gt; 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.

huangapple
  • 本文由 发表于 2020年9月4日 22:17:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/63742922.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定