/*
* Copyright 2008-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.repository.core.support;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.DefaultEvaluationContextProvider;
import org.springframework.data.repository.query.EvaluationContextProvider;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.util.Lazy;
import org.springframework.util.Assert;
/**
* Adapter for Springs {@link FactoryBean} interface to allow easy setup of repository factories via Spring
* configuration.
*
* @param <T> the type of the repository
* @author Oliver Gierke
* @author Thomas Darimont
*/
public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID>
implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T>, BeanClassLoaderAware,
BeanFactoryAware, ApplicationEventPublisherAware {
private final Class<? extends T> repositoryInterface;
private RepositoryFactorySupport factory;
private Key queryLookupStrategyKey;
private Optional<Class<?>> repositoryBaseClass = Optional.empty();
private Optional<Object> customImplementation = Optional.empty();
private NamedQueries namedQueries;
private Optional<MappingContext<?, ?>> mappingContext;
private ClassLoader classLoader;
private BeanFactory beanFactory;
private boolean lazyInit = false;
private EvaluationContextProvider evaluationContextProvider = DefaultEvaluationContextProvider.INSTANCE;
private ApplicationEventPublisher publisher;
private Lazy<T> repository;
private RepositoryMetadata repositoryMetadata;
/**
* Creates a new {@link RepositoryFactoryBeanSupport} for the given repository interface.
*
* @param repositoryInterface must not be {@literal null}.
*/
protected RepositoryFactoryBeanSupport(Class<? extends T> repositoryInterface) {
Assert.notNull(repositoryInterface, "Repository interface must not be null!");
this.repositoryInterface = repositoryInterface;
}
/**
* Configures the repository base class to be used.
*
* @param repositoryBaseClass the repositoryBaseClass to set, can be {@literal null}.
* @since 1.11
*/
public void setRepositoryBaseClass(Class<?> repositoryBaseClass) {
this.repositoryBaseClass = Optional.ofNullable(repositoryBaseClass);
}
/**
* Set the {@link QueryLookupStrategy.Key} to be used.
*
* @param queryLookupStrategyKey
*/
public void setQueryLookupStrategyKey(Key queryLookupStrategyKey) {
this.queryLookupStrategyKey = queryLookupStrategyKey;
}
/**
* Setter to inject a custom repository implementation.
*
* @param customImplementation
*/
public void setCustomImplementation(Object customImplementation) {
this.customImplementation = Optional.ofNullable(customImplementation);
}
/**
* Setter to inject a {@link NamedQueries} instance.
*
* @param namedQueries the namedQueries to set
*/
public void setNamedQueries(NamedQueries namedQueries) {
this.namedQueries = namedQueries;
}
/**
* Configures the {@link MappingContext} to be used to lookup {@link PersistentEntity} instances for
* {@link #getPersistentEntity()}.
*
* @param mappingContext
*/
protected void setMappingContext(MappingContext<?, ?> mappingContext) {
this.mappingContext = Optional.ofNullable(mappingContext);
}
/**
* Sets the {@link EvaluationContextProvider} to be used to evaluate SpEL expressions in manually defined queries.
*
* @param evaluationContextProvider can be {@literal null}, defaults to
* {@link DefaultEvaluationContextProvider#INSTANCE}.
*/
public void setEvaluationContextProvider(EvaluationContextProvider evaluationContextProvider) {
this.evaluationContextProvider = evaluationContextProvider == null ? DefaultEvaluationContextProvider.INSTANCE
: evaluationContextProvider;
}
/**
* Configures whether to initialize the repository proxy lazily. This defaults to {@literal false}.
*
* @param lazy whether to initialize the repository proxy lazily. This defaults to {@literal false}.
*/
public void setLazyInit(boolean lazy) {
this.lazyInit = lazy;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader)
*/
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
*/
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactoryInformation#getEntityInformation()
*/
@SuppressWarnings("unchecked")
public EntityInformation<S, ID> getEntityInformation() {
return (EntityInformation<S, ID>) factory.getEntityInformation(repositoryMetadata.getDomainType());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactoryInformation#getRepositoryInformation()
*/
public RepositoryInformation getRepositoryInformation() {
return this.factory.getRepositoryInformation(repositoryMetadata, customImplementation.map(Object::getClass));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactoryInformation#getPersistentEntity()
*/
public PersistentEntity<?, ?> getPersistentEntity() {
return mappingContext.orElseThrow(() -> new IllegalStateException("No MappingContext available!"))
.getRequiredPersistentEntity(repositoryMetadata.getDomainType());
}
/* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactoryInformation#getQueryMethods()
*/
public List<QueryMethod> getQueryMethods() {
return factory.getQueryMethods();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
public T getObject() {
return this.repository.get();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public Class<? extends T> getObjectType() {
return repositoryInterface;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
public boolean isSingleton() {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() {
this.factory = createRepositoryFactory();
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
this.factory.setNamedQueries(namedQueries);
this.factory.setEvaluationContextProvider(evaluationContextProvider);
this.factory.setBeanClassLoader(classLoader);
this.factory.setBeanFactory(beanFactory);
if (publisher != null) {
this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
}
repositoryBaseClass.ifPresent(it -> this.factory.setRepositoryBaseClass(it));
this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface);
this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, customImplementation));
if (!lazyInit) {
this.repository.get();
}
}
/**
* Create the actual {@link RepositoryFactorySupport} instance.
*
* @return
*/
protected abstract RepositoryFactorySupport createRepositoryFactory();
}