Como estender o Spring com seu tipo de repositório usando o Infinispan como exemplo

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.








All Articles