Why write about it?
This is my first article, in which I will try to describe the practical experience I gained with the Spring Repository under the hood of the framework. I did not find ready-made articles about this topic on the Internet either in Russian or in English, there were only a few source repositories on github, well, the source code of Spring itself. Therefore, I decided, why not write, suddenly the topic of writing your own types of repositories for Spring is relevant for someone else.
I will not consider programming for Infinispan in detail, the implementation details can always be found in the source code specified at the end of the article. The main emphasis is on the pairing of the Spring Boot Repository mechanism and a new type of repository.
How it all began
While working on one of the projects, one of the architect had the idea that you can write your own types of repositories by analogy, as it is done in different Spring modules (for example, JPARepository, KeyValueRepository, CassandraRepository, etc.). As a trial implementation, we decided to choose working with data via Infinispan .
Naturally, architects are busy people, so the Java developer was assigned to implement the idea, i.e. to me.
When I started working on the topic on the Internet, Google stubbornly gave out almost one article about how wonderful it is to use JPARepository in all kinds with trivial examples. There was even less information on KeyValueRepository. StackOverFlow has sad unanswered questions on a similar topic. There is nothing to do, I had to go into the Spring sources.
Infinispan
If we talk briefly about Infinispan, then this is just a distributed data storage in the form of a key-value, and all this is constantly cached in memory. We overload Infinispan, the data is all zeroed.
, - 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- , , , .
Summary
Having written only 6 of our classes, we got a new type of repository that can work with Infinispan as a data store. And this new type of repository works very much like standard Spring repositories.
The complete source kit can be found on my github .
Spring Data KeyValue sources can also be seen on github .
If you have constructive comments on this implementation, then write in the comments, or you can make a pull request in the original project.