Resolve package cycle between repository and aot packages.
Closes #2707
This commit is contained in:
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.aot;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
|
||||
/**
|
||||
* {@link AotContext} specific to Spring Data {@link org.springframework.data.repository.Repository} infrastructure.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
* @see AotContext
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface AotRepositoryContext extends AotContext {
|
||||
|
||||
/**
|
||||
* @return the {@link String bean name} of the repository / factory bean.
|
||||
*/
|
||||
String getBeanName();
|
||||
|
||||
/**
|
||||
* @return a {@link Set} of {@link String base packages} to search for repositories.
|
||||
*/
|
||||
Set<String> getBasePackages();
|
||||
|
||||
/**
|
||||
* @return the {@link Annotation} types used to identify domain types.
|
||||
*/
|
||||
Set<Class<? extends Annotation>> getIdentifyingAnnotations();
|
||||
|
||||
/**
|
||||
* @return {@link RepositoryInformation metadata} about the repository itself.
|
||||
* @see org.springframework.data.repository.core.RepositoryInformation
|
||||
*/
|
||||
RepositoryInformation getRepositoryInformation();
|
||||
|
||||
/**
|
||||
* @return all {@link MergedAnnotation annotations} reachable from the repository.
|
||||
* @see org.springframework.core.annotation.MergedAnnotation
|
||||
*/
|
||||
Set<MergedAnnotation<Annotation>> getResolvedAnnotations();
|
||||
|
||||
/**
|
||||
* @return all {@link Class types} reachable from the repository.
|
||||
*/
|
||||
Set<Class<?>> getResolvedTypes();
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.aot;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryInformationSupport;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
|
||||
/**
|
||||
* {@link RepositoryInformation} based on {@link RepositoryMetadata} collected at build time.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
class AotRepositoryInformation extends RepositoryInformationSupport implements RepositoryInformation {
|
||||
|
||||
private final Supplier<Collection<RepositoryFragment<?>>> fragments;
|
||||
|
||||
AotRepositoryInformation(Supplier<RepositoryMetadata> repositoryMetadata, Supplier<Class<?>> repositoryBaseClass,
|
||||
Supplier<Collection<RepositoryFragment<?>>> fragments) {
|
||||
|
||||
super(repositoryMetadata, repositoryBaseClass);
|
||||
this.fragments = fragments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCustomMethod(Method method) {
|
||||
// TODO:
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBaseClassMethod(Method method) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getTargetClassMethod(Method method) {
|
||||
// TODO
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return configured repository fragments.
|
||||
* @since 3.0
|
||||
*/
|
||||
public Set<RepositoryFragment<?>> getFragments() {
|
||||
return new LinkedHashSet<>(fragments.get());
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,6 @@ import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.core.DecoratingProxy;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.domain.ReactiveAuditorAware;
|
||||
import org.springframework.data.repository.util.ReactiveWrappers;
|
||||
import org.springframework.data.repository.util.ReactiveWrappers.ReactiveLibrary;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -40,6 +38,9 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
class AuditingBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
|
||||
|
||||
private static final boolean PROJECT_REACTOR_PRESENT = ClassUtils.isPresent("reactor.core.publisher.Flux",
|
||||
AuditingBeanRegistrationAotProcessor.class.getClassLoader());
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
@@ -49,7 +50,7 @@ class AuditingBeanRegistrationAotProcessor implements BeanRegistrationAotProcess
|
||||
generationContext.getRuntimeHints());
|
||||
}
|
||||
|
||||
if (ReactiveWrappers.isAvailable(ReactiveLibrary.PROJECT_REACTOR) && isReactiveAuditorAware(registeredBean)) {
|
||||
if (PROJECT_REACTOR_PRESENT && isReactiveAuditorAware(registeredBean)) {
|
||||
return (generationContext, beanRegistrationCode) -> registerSpringProxy(ReactiveAuditorAware.class,
|
||||
generationContext.getRuntimeHints());
|
||||
}
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.aot;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.util.Lazy;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link AotRepositoryContext}
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
* @see AotRepositoryContext
|
||||
* @since 3.0
|
||||
*/
|
||||
class DefaultAotRepositoryContext implements AotRepositoryContext {
|
||||
|
||||
private final AotContext aotContext;
|
||||
private final Lazy<Set<MergedAnnotation<Annotation>>> resolvedAnnotations = Lazy.of(this::discoverAnnotations);
|
||||
private final Lazy<Set<Class<?>>> managedTypes = Lazy.of(this::discoverTypes);
|
||||
|
||||
private RepositoryInformation repositoryInformation;
|
||||
private Set<String> basePackages;
|
||||
private Set<Class<? extends Annotation>> identifyingAnnotations;
|
||||
private String beanName;
|
||||
|
||||
public DefaultAotRepositoryContext(AotContext aotContext) {
|
||||
this.aotContext = aotContext;
|
||||
}
|
||||
|
||||
public AotContext getAotContext() {
|
||||
return aotContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return getAotContext().getBeanFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getBasePackages() {
|
||||
return basePackages;
|
||||
}
|
||||
|
||||
public void setBasePackages(Set<String> basePackages) {
|
||||
this.basePackages = basePackages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<? extends Annotation>> getIdentifyingAnnotations() {
|
||||
return identifyingAnnotations;
|
||||
}
|
||||
|
||||
public void setIdentifyingAnnotations(Set<Class<? extends Annotation>> identifyingAnnotations) {
|
||||
this.identifyingAnnotations = identifyingAnnotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryInformation getRepositoryInformation() {
|
||||
return repositoryInformation;
|
||||
}
|
||||
|
||||
public void setRepositoryInformation(RepositoryInformation repositoryInformation) {
|
||||
this.repositoryInformation = repositoryInformation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<MergedAnnotation<Annotation>> getResolvedAnnotations() {
|
||||
return resolvedAnnotations.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> getResolvedTypes() {
|
||||
return managedTypes.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeIntrospector introspectType(String typeName) {
|
||||
return aotContext.introspectType(typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntrospectedBeanDefinition introspectBeanDefinition(String beanName) {
|
||||
return aotContext.introspectBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
protected Set<MergedAnnotation<Annotation>> discoverAnnotations() {
|
||||
|
||||
Set<MergedAnnotation<Annotation>> annotations = getResolvedTypes().stream()
|
||||
.flatMap(type -> TypeUtils.resolveUsedAnnotations(type).stream())
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
|
||||
annotations.addAll(TypeUtils.resolveUsedAnnotations(repositoryInformation.getRepositoryInterface()));
|
||||
|
||||
return annotations;
|
||||
}
|
||||
|
||||
protected Set<Class<?>> discoverTypes() {
|
||||
|
||||
Set<Class<?>> types = new LinkedHashSet<>(TypeCollector.inspect(repositoryInformation.getDomainType()).list());
|
||||
|
||||
repositoryInformation.getQueryMethods()
|
||||
.flatMap(it -> TypeUtils.resolveTypesInSignature(repositoryInformation.getRepositoryInterface(), it).stream())
|
||||
.flatMap(it -> TypeCollector.inspect(it).list().stream()).forEach(types::add);
|
||||
|
||||
if (!getIdentifyingAnnotations().isEmpty()) {
|
||||
|
||||
Set<Class<?>> classes = aotContext.getTypeScanner().scanPackages(getBasePackages())
|
||||
.forTypesAnnotatedWith(getIdentifyingAnnotations()).collectAsSet();
|
||||
types.addAll(TypeCollector.inspect(classes).list());
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.aot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.data.repository.config.RepositoryConfiguration;
|
||||
import org.springframework.data.repository.config.RepositoryFragmentConfigurationProvider;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Reader used to extract {@link RepositoryInformation} from {@link RepositoryConfiguration}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
* @since 3.0
|
||||
*/
|
||||
class RepositoryBeanDefinitionReader {
|
||||
|
||||
static RepositoryInformation readRepositoryInformation(RepositoryConfiguration<?> metadata,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
return new AotRepositoryInformation(metadataSupplier(metadata, beanFactory),
|
||||
repositoryBaseClass(metadata, beanFactory), fragments(metadata, beanFactory));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private static Supplier<Collection<RepositoryFragment<?>>> fragments(RepositoryConfiguration<?> metadata,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
if (metadata instanceof RepositoryFragmentConfigurationProvider provider) {
|
||||
|
||||
return Lazy.of(() -> {
|
||||
return provider.getFragmentConfiguration().stream().flatMap(it -> {
|
||||
|
||||
List<RepositoryFragment<?>> fragments = new ArrayList<>(1);
|
||||
|
||||
// TODO: Implemented accepts an Object, not a class.
|
||||
fragments.add(RepositoryFragment.implemented(forName(it.getClassName(), beanFactory)));
|
||||
fragments.add(RepositoryFragment.structural(forName(it.getInterfaceName(), beanFactory)));
|
||||
|
||||
return fragments.stream();
|
||||
}).collect(Collectors.toList());
|
||||
});
|
||||
}
|
||||
|
||||
return Lazy.of(Collections::emptyList);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private static Supplier<Class<?>> repositoryBaseClass(RepositoryConfiguration metadata,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
return Lazy.of(() -> (Class<?>) metadata.getRepositoryBaseClassName().map(it -> forName(it.toString(), beanFactory))
|
||||
.orElseGet(() -> {
|
||||
// TODO: retrieve the default without loading the actual RepositoryBeanFactory
|
||||
return Object.class;
|
||||
}));
|
||||
}
|
||||
|
||||
static Supplier<org.springframework.data.repository.core.RepositoryMetadata> metadataSupplier(
|
||||
RepositoryConfiguration<?> metadata, ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
return Lazy.of(() -> new DefaultRepositoryMetadata(forName(metadata.getRepositoryInterface(), beanFactory)));
|
||||
}
|
||||
|
||||
static Class<?> forName(String name, ConfigurableListableBeanFactory beanFactory) {
|
||||
try {
|
||||
return ClassUtils.forName(name, beanFactory.getBeanClassLoader());
|
||||
} catch (ClassNotFoundException cause) {
|
||||
throw new TypeNotPresentException(name, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,403 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.aot;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.aop.SpringProxy;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationCode;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.DecoratingProxy;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.data.projection.EntityProjectionIntrospector;
|
||||
import org.springframework.data.projection.TargetAware;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.config.RepositoryConfiguration;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link BeanRegistrationAotContribution} used to contribute repository registrations.
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 3.0
|
||||
*/
|
||||
public class RepositoryRegistrationAotContribution implements BeanRegistrationAotContribution {
|
||||
|
||||
private static final String KOTLIN_COROUTINE_REPOSITORY_TYPE_NAME = "org.springframework.data.repository.kotlin.CoroutineCrudRepository";
|
||||
|
||||
/**
|
||||
* Factory method used to construct a new instance of {@link RepositoryRegistrationAotContribution} initialized with
|
||||
* the given, required {@link RepositoryRegistrationAotProcessor} from which this contribution was created.
|
||||
*
|
||||
* @param repositoryRegistrationAotProcessor reference back to the {@link RepositoryRegistrationAotProcessor} from
|
||||
* which this contribution was created.
|
||||
* @return a new instance of {@link RepositoryRegistrationAotContribution}.
|
||||
* @throws IllegalArgumentException if the {@link RepositoryRegistrationAotProcessor} is {@literal null}.
|
||||
* @see org.springframework.data.aot.RepositoryRegistrationAotProcessor
|
||||
*/
|
||||
public static RepositoryRegistrationAotContribution fromProcessor(
|
||||
RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor) {
|
||||
|
||||
return new RepositoryRegistrationAotContribution(repositoryRegistrationAotProcessor);
|
||||
}
|
||||
|
||||
private AotRepositoryContext repositoryContext;
|
||||
|
||||
private BiConsumer<AotRepositoryContext, GenerationContext> moduleContribution;
|
||||
|
||||
private final RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link RepositoryRegistrationAotContribution} initialized with the given, required
|
||||
* {@link RepositoryRegistrationAotProcessor} from which this contribution was created.
|
||||
*
|
||||
* @param repositoryRegistrationAotProcessor reference back to the {@link RepositoryRegistrationAotProcessor} from
|
||||
* which this contribution was created.
|
||||
* @throws IllegalArgumentException if the {@link RepositoryRegistrationAotProcessor} is {@literal null}.
|
||||
* @see org.springframework.data.aot.RepositoryRegistrationAotProcessor
|
||||
*/
|
||||
protected RepositoryRegistrationAotContribution(
|
||||
RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor) {
|
||||
|
||||
Assert.notNull(repositoryRegistrationAotProcessor, "RepositoryRegistrationAotProcessor must not be null");
|
||||
|
||||
this.repositoryRegistrationAotProcessor = repositoryRegistrationAotProcessor;
|
||||
}
|
||||
|
||||
protected ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return getRepositoryRegistrationAotProcessor().getBeanFactory();
|
||||
}
|
||||
|
||||
protected Optional<BiConsumer<AotRepositoryContext, GenerationContext>> getModuleContribution() {
|
||||
return Optional.ofNullable(this.moduleContribution);
|
||||
}
|
||||
|
||||
protected AotRepositoryContext getRepositoryContext() {
|
||||
|
||||
Assert.state(this.repositoryContext != null,
|
||||
"The AOT RepositoryContext was not properly initialized; did you call the forBean(:RegisteredBean) method");
|
||||
|
||||
return this.repositoryContext;
|
||||
}
|
||||
|
||||
protected RepositoryRegistrationAotProcessor getRepositoryRegistrationAotProcessor() {
|
||||
return this.repositoryRegistrationAotProcessor;
|
||||
}
|
||||
|
||||
public RepositoryInformation getRepositoryInformation() {
|
||||
return getRepositoryContext().getRepositoryInformation();
|
||||
}
|
||||
|
||||
private void logTrace(String message, Object... arguments) {
|
||||
getRepositoryRegistrationAotProcessor().logTrace(message, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link RepositoryRegistrationAotContribution} for given, required {@link RegisteredBean} representing the
|
||||
* {@link Repository} registered in the bean registry.
|
||||
*
|
||||
* @param repositoryBean {@link RegisteredBean} for the {@link Repository}; must not be {@literal null}.
|
||||
* @return a {@link RepositoryRegistrationAotContribution} to contribute AOT metadata and code for the
|
||||
* {@link Repository} {@link RegisteredBean}.
|
||||
* @throws IllegalArgumentException if the {@link RegisteredBean} is {@literal null}.
|
||||
* @see org.springframework.beans.factory.support.RegisteredBean
|
||||
*/
|
||||
public RepositoryRegistrationAotContribution forBean(RegisteredBean repositoryBean) {
|
||||
|
||||
Assert.notNull(repositoryBean, "The RegisteredBean for the repository must not be null");
|
||||
|
||||
RepositoryConfiguration<?> repositoryMetadata = getRepositoryRegistrationAotProcessor()
|
||||
.getRepositoryMetadata(repositoryBean);
|
||||
|
||||
this.repositoryContext = buildAotRepositoryContext(repositoryBean, repositoryMetadata);
|
||||
|
||||
enhanceRepositoryBeanDefinition(repositoryBean, repositoryMetadata, this.repositoryContext);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
protected DefaultAotRepositoryContext buildAotRepositoryContext(RegisteredBean bean,
|
||||
RepositoryConfiguration<?> repositoryMetadata) {
|
||||
|
||||
RepositoryInformation repositoryInformation = resolveRepositoryInformation(repositoryMetadata);
|
||||
|
||||
DefaultAotRepositoryContext repositoryContext = new DefaultAotRepositoryContext(
|
||||
AotContext.from(this.getBeanFactory()));
|
||||
|
||||
repositoryContext.setBeanName(bean.getBeanName());
|
||||
repositoryContext.setBasePackages(repositoryMetadata.getBasePackages().toSet());
|
||||
repositoryContext.setIdentifyingAnnotations(resolveIdentifyingAnnotations());
|
||||
repositoryContext.setRepositoryInformation(repositoryInformation);
|
||||
|
||||
return repositoryContext;
|
||||
}
|
||||
|
||||
private Set<Class<? extends Annotation>> resolveIdentifyingAnnotations() {
|
||||
|
||||
Set<Class<? extends Annotation>> identifyingAnnotations = Collections.emptySet();
|
||||
|
||||
try {
|
||||
// TODO: Getting all beans of type RepositoryConfigurationExtensionSupport will have the effect that
|
||||
// if the user is currently operating in multi-store mode, then all identifying annotations from
|
||||
// all stores will be included in the resulting Set.
|
||||
// When using AOT, is multi-store mode allowed? I don't see why not, but does this work correctly
|
||||
// with AOT, ATM?
|
||||
Map<String, RepositoryConfigurationExtensionSupport> repositoryConfigurationExtensionBeans = getBeanFactory()
|
||||
.getBeansOfType(RepositoryConfigurationExtensionSupport.class);
|
||||
|
||||
// repositoryConfigurationExtensionBeans.values().stream()
|
||||
// .map(RepositoryConfigurationExtensionSupport::getIdentifyingAnnotations)
|
||||
// .flatMap(Collection::stream)
|
||||
// .collect(Collectors.toCollection(() -> identifyingAnnotations));
|
||||
|
||||
} catch (Throwable ignore) {
|
||||
// Possible BeansException because no bean exists of type RepositoryConfigurationExtension,
|
||||
// which included non-Singletons and occurred during eager initialization.
|
||||
}
|
||||
|
||||
return identifyingAnnotations;
|
||||
}
|
||||
|
||||
private RepositoryInformation resolveRepositoryInformation(RepositoryConfiguration<?> repositoryMetadata) {
|
||||
return RepositoryBeanDefinitionReader.readRepositoryInformation(repositoryMetadata, getBeanFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helps the AOT processing render the {@link FactoryBean} type correctly that is used to tell the outcome of the
|
||||
* {@link FactoryBean}. We just need to set the target {@link Repository} {@link Class type} of the
|
||||
* {@link RepositoryFactoryBeanSupport} while keeping the actual ID and DomainType set to {@link Object}. If the
|
||||
* generic type signature does not match, then we do not try to resolve and remap the types, but rather set the
|
||||
* {@literal factoryBeanObjectType} attribute on the {@link RootBeanDefinition}.
|
||||
*/
|
||||
protected void enhanceRepositoryBeanDefinition(RegisteredBean repositoryBean,
|
||||
RepositoryConfiguration<?> repositoryMetadata, AotRepositoryContext repositoryContext) {
|
||||
|
||||
logTrace(String.format("Enhancing repository factory bean definition [%s]", repositoryBean.getBeanName()));
|
||||
|
||||
Class<?> repositoryFactoryBeanType = repositoryContext
|
||||
.introspectType(repositoryMetadata.getRepositoryFactoryBeanClassName()).resolveType()
|
||||
.orElse(RepositoryFactoryBeanSupport.class);
|
||||
|
||||
ResolvableType resolvedRepositoryFactoryBeanType = ResolvableType.forClass(repositoryFactoryBeanType);
|
||||
|
||||
RootBeanDefinition repositoryBeanDefinition = repositoryBean.getMergedBeanDefinition();
|
||||
|
||||
if (isRepositoryWithTypeParameters(resolvedRepositoryFactoryBeanType)) {
|
||||
repositoryBeanDefinition.setTargetType(ResolvableType.forClassWithGenerics(repositoryFactoryBeanType,
|
||||
repositoryContext.getRepositoryInformation().getRepositoryInterface(), Object.class, Object.class));
|
||||
} else {
|
||||
repositoryBeanDefinition.setTargetType(resolvedRepositoryFactoryBeanType);
|
||||
repositoryBeanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
|
||||
repositoryContext.getRepositoryInformation().getRepositoryInterface());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRepositoryWithTypeParameters(ResolvableType type) {
|
||||
return type.getGenerics().length == 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BiConsumer Callback} for data module specific contributions.
|
||||
*
|
||||
* @param moduleContribution {@link BiConsumer} used by data modules to submit contributions; can be {@literal null}.
|
||||
* @return this.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public RepositoryRegistrationAotContribution withModuleContribution(
|
||||
@Nullable BiConsumer<AotRepositoryContext, GenerationContext> moduleContribution) {
|
||||
this.moduleContribution = moduleContribution;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
|
||||
|
||||
contributeRepositoryInfo(this.repositoryContext, generationContext);
|
||||
getModuleContribution().ifPresent(it -> it.accept(getRepositoryContext(), generationContext));
|
||||
}
|
||||
|
||||
private void contributeRepositoryInfo(AotRepositoryContext repositoryContext, GenerationContext contribution) {
|
||||
|
||||
RepositoryInformation repositoryInformation = getRepositoryInformation();
|
||||
|
||||
logTrace("Contributing repository information for [%s]", repositoryInformation.getRepositoryInterface());
|
||||
|
||||
// TODO: is this the way?
|
||||
contribution.getRuntimeHints().reflection()
|
||||
.registerType(repositoryInformation.getRepositoryInterface(),
|
||||
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.registerType(repositoryInformation.getRepositoryBaseClass(),
|
||||
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
|
||||
TypeContributor.contribute(repositoryInformation.getDomainType(), contribution);
|
||||
|
||||
// Repository Fragments
|
||||
for (RepositoryFragment<?> fragment : getRepositoryInformation().getFragments()) {
|
||||
|
||||
Class<?> repositoryFragmentType = fragment.getSignatureContributor();
|
||||
|
||||
contribution.getRuntimeHints().reflection().registerType(repositoryFragmentType, hint -> {
|
||||
|
||||
hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
|
||||
if (!repositoryFragmentType.isInterface()) {
|
||||
hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Repository Proxy
|
||||
contribution.getRuntimeHints().proxies().registerJdkProxy(repositoryInformation.getRepositoryInterface(),
|
||||
SpringProxy.class, Advised.class, DecoratingProxy.class);
|
||||
|
||||
// Transactional Repository Proxy
|
||||
// repositoryContext.ifTransactionManagerPresent(transactionManagerBeanNames -> {
|
||||
|
||||
// TODO: Is the following double JDK Proxy registration above necessary or would a single JDK Proxy
|
||||
// registration suffice?
|
||||
// In other words, simply having a single JDK Proxy registration either with or without
|
||||
// the additional Serializable TypeReference?
|
||||
// NOTE: Using a single JDK Proxy registration causes the
|
||||
// simpleRepositoryWithTxManagerNoKotlinNoReactiveButComponent() test case method to fail.
|
||||
List<TypeReference> transactionalRepositoryProxyTypeReferences = transactionalRepositoryProxyTypeReferences(
|
||||
repositoryInformation);
|
||||
|
||||
contribution.getRuntimeHints().proxies()
|
||||
.registerJdkProxy(transactionalRepositoryProxyTypeReferences.toArray(new TypeReference[0]));
|
||||
|
||||
if (isComponentAnnotatedRepository(repositoryInformation)) {
|
||||
transactionalRepositoryProxyTypeReferences.add(TypeReference.of(Serializable.class));
|
||||
contribution.getRuntimeHints().proxies()
|
||||
.registerJdkProxy(transactionalRepositoryProxyTypeReferences.toArray(new TypeReference[0]));
|
||||
}
|
||||
// });
|
||||
|
||||
// Reactive Repositories
|
||||
if (repositoryInformation.isReactiveRepository()) {
|
||||
// TODO: do we still need this and how to configure it?
|
||||
// registry.initialization().add(NativeInitializationEntry.ofBuildTimeType(configuration.getRepositoryInterface()));
|
||||
}
|
||||
|
||||
// Kotlin
|
||||
if (isKotlinCoroutineRepository(repositoryContext, repositoryInformation)) {
|
||||
contribution.getRuntimeHints().reflection().registerTypes(kotlinRepositoryReflectionTypeReferences(),
|
||||
hint -> hint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
}
|
||||
|
||||
// Repository query methods
|
||||
repositoryInformation.getQueryMethods().map(repositoryInformation::getReturnedDomainClass)
|
||||
.filter(Class::isInterface).forEach(type -> {
|
||||
if (EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy().test(type,
|
||||
repositoryInformation.getDomainType())) {
|
||||
contributeProjection(type, contribution);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isComponentAnnotatedRepository(RepositoryInformation repositoryInformation) {
|
||||
return AnnotationUtils.findAnnotation(repositoryInformation.getRepositoryInterface(), Component.class) != null;
|
||||
}
|
||||
|
||||
private boolean isKotlinCoroutineRepository(AotRepositoryContext repositoryContext,
|
||||
RepositoryInformation repositoryInformation) {
|
||||
|
||||
return repositoryContext.introspectType(KOTLIN_COROUTINE_REPOSITORY_TYPE_NAME).resolveType()
|
||||
.filter(it -> ClassUtils.isAssignable(it, repositoryInformation.getRepositoryInterface())).isPresent();
|
||||
}
|
||||
|
||||
private List<TypeReference> kotlinRepositoryReflectionTypeReferences() {
|
||||
|
||||
return new ArrayList<>(
|
||||
Arrays.asList(TypeReference.of("org.springframework.data.repository.kotlin.CoroutineCrudRepository"),
|
||||
TypeReference.of(Repository.class), //
|
||||
TypeReference.of(Iterable.class), //
|
||||
TypeReference.of("kotlinx.coroutines.flow.Flow"), //
|
||||
TypeReference.of("kotlin.collections.Iterable"), //
|
||||
TypeReference.of("kotlin.Unit"), //
|
||||
TypeReference.of("kotlin.Long"), //
|
||||
TypeReference.of("kotlin.Boolean")));
|
||||
}
|
||||
|
||||
private List<TypeReference> transactionalRepositoryProxyTypeReferences(RepositoryInformation repositoryInformation) {
|
||||
|
||||
return new ArrayList<>(Arrays.asList(TypeReference.of(repositoryInformation.getRepositoryInterface()),
|
||||
TypeReference.of(Repository.class), //
|
||||
TypeReference.of("org.springframework.transaction.interceptor.TransactionalProxy"), //
|
||||
TypeReference.of("org.springframework.aop.framework.Advised"), //
|
||||
TypeReference.of(DecoratingProxy.class)));
|
||||
}
|
||||
|
||||
private void contributeProjection(Class<?> type, GenerationContext generationContext) {
|
||||
|
||||
generationContext.getRuntimeHints().proxies().registerJdkProxy(type, TargetAware.class, SpringProxy.class,
|
||||
DecoratingProxy.class);
|
||||
}
|
||||
|
||||
static boolean isJavaOrPrimitiveType(Class<?> type) {
|
||||
return TypeUtils.type(type).isPartOf("java") //
|
||||
|| ClassUtils.isPrimitiveOrWrapper(type) //
|
||||
|| ClassUtils.isPrimitiveArray(type); //
|
||||
}
|
||||
|
||||
static boolean isSpringDataManagedAnnotation(@Nullable MergedAnnotation<?> annotation) {
|
||||
|
||||
return annotation != null && (isInSpringDataNamespace(annotation.getType())
|
||||
|| annotation.getMetaTypes().stream().anyMatch(RepositoryRegistrationAotContribution::isInSpringDataNamespace));
|
||||
}
|
||||
|
||||
static boolean isInSpringDataNamespace(Class<?> type) {
|
||||
return type.getPackage().getName().startsWith(TypeContributor.DATA_NAMESPACE);
|
||||
}
|
||||
|
||||
static void contributeType(Class<?> type, GenerationContext generationContext) {
|
||||
TypeContributor.contribute(type, it -> true, generationContext);
|
||||
}
|
||||
|
||||
// TODO What was this meant to be used for? Was this type filter maybe meant to be used in
|
||||
// the TypeContributor.contribute(:Class, :Predicate :GenerationContext) method
|
||||
// used in the contributeType(..) method above?
|
||||
public Predicate<Class<?>> typeFilter() { // like only document ones. // TODO: As in MongoDB?
|
||||
return it -> true;
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.aot;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.data.repository.config.RepositoryConfiguration;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link BeanRegistrationAotProcessor} responsible processing and providing AOT configuration for repositories.
|
||||
* <p>
|
||||
* Processes {@link RepositoryFactoryBeanSupport repository factory beans} to provide generic type information to the
|
||||
* AOT tooling to allow deriving target type from the {@link RootBeanDefinition bean definition}. If generic types do
|
||||
* not match due to customization of the factory bean by the user, at least the target repository type is provided via
|
||||
* the {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE}.
|
||||
* </p>
|
||||
* <p>
|
||||
* With {@link RepositoryRegistrationAotProcessor#contribute(AotRepositoryContext, GenerationContext)}, stores can
|
||||
* provide custom logic for contributing additional (eg. reflection) configuration. By default, reflection configuration
|
||||
* will be added for types reachable from the repository declaration and query methods as well as all used
|
||||
* {@link Annotation annotations} from the {@literal org.springframework.data} namespace.
|
||||
* </p>
|
||||
* The processor is typically configured via {@link RepositoryConfigurationExtension#getRepositoryAotProcessor()} and
|
||||
* gets added by the {@link org.springframework.data.repository.config.RepositoryConfigurationDelegate}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
* @since 3.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class RepositoryRegistrationAotProcessor implements BeanRegistrationAotProcessor, BeanFactoryAware {
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Map<String, RepositoryConfiguration<?>> configMap;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean bean) {
|
||||
|
||||
return isRepositoryBean(bean) ? newRepositoryRegistrationAotContribution(bean) : null;
|
||||
}
|
||||
|
||||
protected void contribute(AotRepositoryContext repositoryContext, GenerationContext generationContext) {
|
||||
|
||||
repositoryContext.getResolvedTypes().stream()
|
||||
.filter(it -> !RepositoryRegistrationAotContribution.isJavaOrPrimitiveType(it))
|
||||
.forEach(it -> RepositoryRegistrationAotContribution.contributeType(it, generationContext));
|
||||
|
||||
repositoryContext.getResolvedAnnotations().stream()
|
||||
.filter(RepositoryRegistrationAotContribution::isSpringDataManagedAnnotation).map(MergedAnnotation::getType)
|
||||
.forEach(it -> RepositoryRegistrationAotContribution.contributeType(it, generationContext));
|
||||
}
|
||||
|
||||
private boolean isRepositoryBean(RegisteredBean bean) {
|
||||
return getConfigMap().containsKey(bean.getBeanName());
|
||||
}
|
||||
|
||||
protected RepositoryRegistrationAotContribution newRepositoryRegistrationAotContribution(
|
||||
RegisteredBean repositoryBean) {
|
||||
|
||||
RepositoryRegistrationAotContribution contribution = RepositoryRegistrationAotContribution.fromProcessor(this)
|
||||
.forBean(repositoryBean);
|
||||
|
||||
return contribution.withModuleContribution(this::contribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
|
||||
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
|
||||
() -> "AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
|
||||
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
protected ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
public void setConfigMap(@Nullable Map<String, RepositoryConfiguration<?>> configMap) {
|
||||
this.configMap = configMap;
|
||||
}
|
||||
|
||||
public Map<String, RepositoryConfiguration<?>> getConfigMap() {
|
||||
return nullSafeMap(this.configMap);
|
||||
}
|
||||
|
||||
private <K, V> Map<K, V> nullSafeMap(@Nullable Map<K, V> map) {
|
||||
return map != null ? map : Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected RepositoryConfiguration<?> getRepositoryMetadata(RegisteredBean bean) {
|
||||
return getConfigMap().get(nullSafeBeanName(bean));
|
||||
}
|
||||
|
||||
private String nullSafeBeanName(RegisteredBean bean) {
|
||||
|
||||
String beanName = bean.getBeanName();
|
||||
|
||||
return StringUtils.hasText(beanName) ? beanName : "";
|
||||
}
|
||||
|
||||
protected Log getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
private void logAt(Predicate<Log> logLevelPredicate, BiConsumer<Log, String> logOperation, String message,
|
||||
Object... arguments) {
|
||||
|
||||
Log logger = getLogger();
|
||||
|
||||
if (logLevelPredicate.test(logger)) {
|
||||
logOperation.accept(logger, String.format(message, arguments));
|
||||
}
|
||||
}
|
||||
|
||||
protected void logDebug(String message, Object... arguments) {
|
||||
logAt(Log::isDebugEnabled, Log::debug, message, arguments);
|
||||
}
|
||||
|
||||
protected void logTrace(String message, Object... arguments) {
|
||||
logAt(Log::isTraceEnabled, Log::trace, message, arguments);
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.aot.hint;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragmentsFactoryBean;
|
||||
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
|
||||
import org.springframework.data.repository.query.QueryByExampleExecutor;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} holding required hints to bootstrap data repositories. <br />
|
||||
* Already registered via {@literal aot.factories}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
class RepositoryRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
|
||||
// repository infrastructure
|
||||
hints.reflection().registerTypes(Arrays.asList( //
|
||||
TypeReference.of(RepositoryFactoryBeanSupport.class), //
|
||||
TypeReference.of(RepositoryFragmentsFactoryBean.class), //
|
||||
TypeReference.of(RepositoryFragment.class), //
|
||||
TypeReference.of(TransactionalRepositoryFactoryBeanSupport.class), //
|
||||
TypeReference.of(QueryByExampleExecutor.class), //
|
||||
TypeReference.of(MappingContext.class), //
|
||||
TypeReference.of(RepositoryMetadata.class) //
|
||||
), builder -> {
|
||||
builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
});
|
||||
|
||||
// named queries
|
||||
hints.reflection().registerTypes(Arrays.asList( //
|
||||
TypeReference.of(Properties.class), //
|
||||
TypeReference.of(BeanFactory.class), //
|
||||
TypeReference.of(InputStreamSource[].class) //
|
||||
), builder -> builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
|
||||
//
|
||||
hints.reflection().registerType(Throwable.class,
|
||||
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS));
|
||||
|
||||
// SpEL support
|
||||
hints.reflection().registerType(
|
||||
TypeReference.of("org.springframework.data.projection.SpelEvaluatingMethodInterceptor$TargetWrapper"),
|
||||
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
|
||||
// annotated queries
|
||||
hints.proxies().registerJdkProxy( //
|
||||
TypeReference.of("org.springframework.data.annotation.QueryAnnotation"));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
* Predefined Runtime Hints.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.data.aot.hint;
|
||||
Reference in New Issue
Block a user