Comment étendre Spring avec votre type de référentiel en utilisant Infinispan comme exemple

Pourquoi écrire à ce sujet?

C'est mon premier article, dans lequel j'essaierai de décrire l'expérience pratique que j'ai acquise avec le référentiel Spring sous le capot du framework. Je n'ai pas trouvé d'articles prêts à l'emploi sur ce sujet sur Internet ni en russe ni en anglais, il n'y avait que quelques référentiels sources sur github, et le code source de Spring lui-même. Par conséquent, j'ai décidé, pourquoi ne pas écrire, tout à coup le sujet de l'écriture de vos propres types de référentiels pour Spring est pertinent pour quelqu'un d'autre.





Je ne considérerai pas la programmation pour Infinispan en détail, les détails de mise en œuvre peuvent toujours être consultés dans le code source spécifié à la fin de l'article. L'accent principal est mis sur l'appariement du mécanisme de référentiel Spring Boot et d'un nouveau type de référentiel.





Comment tout a commencé

En travaillant sur l'un des projets, l'un des architectes a eu l'idée que vous pouvez écrire vos propres types de référentiels par analogie, comme cela se fait dans différents modules Spring (par exemple, JPARepository, KeyValueRepository, CassandraRepository, etc.). En tant qu'implémentation d' essai, nous avons décidé de choisir de travailler avec des données via Infinispan .





Naturellement, les architectes sont des gens occupés, c'est pourquoi le développeur Java a été chargé de mettre en œuvre l'idée, c.-à-d. tome.





Quand j'ai commencé à travailler sur le sujet sur Internet, Google a obstinément donné presque un article sur la façon dont il est merveilleux d'utiliser JPARepository dans toutes sortes avec des exemples triviaux. Il y avait encore moins d'informations sur KeyValueRepository. StackOverFlow a de tristes questions sans réponse sur un sujet similaire. Il n'y a rien à faire, j'ai dû aller dans les sources Spring.





Infinispan

Si nous parlons brièvement d'Infinispan, il ne s'agit que d'un stockage de données distribué sous la forme d'une valeur-clé, et tout cela est constamment mis en cache en mémoire. On surcharge Infinispan, les données sont toutes mises à zéro.





, - 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- , , , .





Sommaire

N'ayant écrit que 6 de nos classes, nous avons un nouveau type de référentiel qui peut fonctionner avec Infinispan en tant que magasin de données. Et ce nouveau type de référentiel fonctionne très bien comme les référentiels Spring standard.





Le kit source complet peut être trouvé sur mon github .





Les sources Spring Data KeyValue peuvent également être vues sur github .





Si vous avez des commentaires constructifs sur cette implémentation, écrivez dans les commentaires, ou vous pouvez faire une pull request dans le projet d'origine.








All Articles