Por que escrever sobre isso?
Este é meu primeiro artigo, no qual tentarei descrever a experiência prática que ganhei com o Spring Repository sob o capô do framework. Não encontrei artigos prontos sobre este tópico na Internet em russo ou em inglês, havia apenas alguns repositórios de origem no github e o código-fonte do próprio Spring. Portanto, eu decidi, por que não escrever, de repente o tópico de escrever seus próprios tipos de repositórios para o Spring é relevante para outra pessoa.
Não considerarei a programação para Infinispan em detalhes, os detalhes de implementação sempre podem ser visualizados no código-fonte especificado no final do artigo. A ênfase principal é colocada no emparelhamento do mecanismo Spring Boot Repository e um novo tipo de repositório.
Como tudo começou
Enquanto trabalhava em um dos projetos, um dos arquitetos teve a ideia de que você pode escrever seus próprios tipos de repositórios por analogia, como isso é feito em diferentes módulos Spring (por exemplo, JPARepository, KeyValueRepository, CassandraRepository, etc.). Como uma implementação de teste, decidimos trabalhar com dados via Infinispan .
Naturalmente, os arquitetos são pessoas ocupadas, então o desenvolvedor Java foi designado para implementar a ideia, ou seja, para mim.
Quando comecei a trabalhar no tópico na Internet, o Google teimosamente distribuiu quase um artigo sobre como é maravilhoso usar JPARepository em todos os tipos com exemplos triviais. Havia ainda menos informações em KeyValueRepository. StackOverFlow tem tristes perguntas sem resposta sobre um tópico semelhante. Não há nada a fazer, eu tive que ir para as fontes do Spring.
Infinispan
Se falarmos brevemente sobre Infinispan, então este é apenas um armazenamento de dados distribuído na forma de um valor-chave, e tudo isso é constantemente armazenado em cache na memória. Sobrecarregamos o Infinispan, os dados são zerados.
, - KeyValueRepository, , Spring. , Infinispan ( Hazelcast, ), , KeyValueRepository ConcurrentHashMap.
Spring - EnableMapRepositories.
@SpringBootApplication
@EnableMapRepositories("my.person.package.for.entities")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
EnableInfinispanRepositories.
, , map infinispan, , .
EnableInfinispanRepositories
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(InfinispanRepositoriesRegistrar.class)
public @interface EnableInfinispanRepositories {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
ComponentScan.Filter[] excludeFilters() default {};
ComponentScan.Filter[] includeFilters() default {};
String repositoryImplementationPostfix() default "Impl";
String namedQueriesLocation() default "";
QueryLookupStrategy.Key queryLookupStrategy() default
QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND;
Class<?> repositoryFactoryBeanClass() default
KeyValueRepositoryFactoryBean.class;
Class<?> repositoryBaseClass() default
DefaultRepositoryBaseClass.class;
String keyValueTemplateRef() default "infinispanKeyValueTemplate";
boolean considerNestedRepositories() default false;
}
EnableMapRepositories, , , .
@Import(MapRepositoriesRegistrar.class)
public @interface EnableMapRepositories {
}
MapRepositoriesRegistar.
public class MapRepositoriesRegistrar extends
RepositoryBeanDefinitionRegistrarSupport {
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableMapRepositories.class;
}
@Override
protected RepositoryConfigurationExtension getExtension() {
return new MapRepositoryConfigurationExtension();
}
}
. Registar , . , .
InfinispaRepositoriesRegistar.
@NoArgsConstructor
public class InfinispanRepositoriesRegistrar extends
RepositoryBeanDefinitionRegistrarSupport {
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableInfinispanRepositories.class;
}
@Override
protected RepositoryConfigurationExtension getExtension() {
return new InfinispanRepositoryConfigurationExtension();
}
}
, .
public class MapRepositoryConfigurationExtension extends
KeyValueRepositoryConfigurationExtension {
@Override
public String getModuleName() {
return "Map";
}
@Override
protected String getModulePrefix() {
return "map";
}
@Override
protected String getDefaultKeyValueTemplateRef() {
return "mapKeyValueTemplate";
}
@Override
protected AbstractBeanDefinition getDefaultKeyValueTemplateBeanDefinition(RepositoryConfigurationSource configurationSource) {
BeanDefinitionBuilder adapterBuilder = BeanDefinitionBuilder
.rootBeanDefinition(MapKeyValueAdapter.class);
adapterBuilder.addConstructorArgValue(
getMapTypeToUse(configurationSource));
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.rootBeanDefinition(KeyValueTemplate.class);
...
}
...
}
MapKeyValueAdapter , HashMap. KeyValueTemplate .
Infinispan, ConfigurationExtension, , // , Infinispan.
InfinispanRepositoriesConfigurationExtension
@NoArgsConstructor
public class InfinispanRepositoryConfigurationExtension
extends KeyValueRepositoryConfigurationExtension {
@Override
public String getModuleName() {
return "Infinispan";
}
@Override
protected String getModulePrefix() {
return "infinispan";
}
@Override
protected String getDefaultKeyValueTemplateRef() {
return "infinispanKeyValueTemplate";
}
@Override
protected Collection<Class<?>> getIdentifyingTypes() {
return Collections.singleton(InfinispanRepository.class);
}
@Override
protected AbstractBeanDefinition getDefaultKeyValueTemplateBeanDefinition(RepositoryConfigurationSource configurationSource) {
RootBeanDefinition infinispanKeyValueAdapterDefinition =
new RootBeanDefinition(InfinispanKeyValueAdapter.class);
RootBeanDefinition keyValueTemplateDefinition =
new RootBeanDefinition(KeyValueTemplate.class);
ConstructorArgumentValues constructorArgumentValuesForKeyValueTemplate = new ConstructorArgumentValues();
constructorArgumentValuesForKeyValueTemplate
.addGenericArgumentValue(infinispanKeyValueAdapterDefinition);
keyValueTemplateDefinition.setConstructorArgumentValues(
constructorArgumentValuesForKeyValueTemplate);
return keyValueTemplateDefinition;
}
}
ConfigurationExtension getIdentifyingTypes(), (. ).
@NoRepositoryBean
public interface InfinispanRepository <T, ID> extends
PagingAndSortingRepository<T, ID> {
}
, KeyValueTemplate, .
@Configuration
public class InfinispanConfiguration extends CachingConfigurerSupport {
@Autowired
private ApplicationContext applicationContext;
@Bean
public InfinispanKeyValueAdapter getInfinispanAdapter() {
return new InfinispanKeyValueAdapter(
applicationContext.getBean(CacheManager.class)
);
}
@Bean("infinispanKeyValueTemplate")
public KeyValueTemplate getInfinispanKeyValueTemplate() {
return new KeyValueTemplate(getInfinispanAdapter());
}
}
.
, , Spring- , , , .
Resumo
Tendo escrito apenas 6 de nossas classes, temos um novo tipo de repositório que pode funcionar com o Infinispan como um armazenamento de dados. E este novo tipo de repositório funciona de forma muito semelhante aos repositórios Spring padrão.
O kit de fontes completo pode ser encontrado no meu github .
As fontes Spring Data KeyValue também podem ser vistas no github .
Se você tiver comentários construtivos sobre essa implementação, escreva-os ou faça uma solicitação de pull no projeto original.