@@ -1,152 +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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Types managed by a Spring Data implementation.
|
||||
*
|
||||
* Used to predefine a set of know entities that might need processing during the Spring container,
|
||||
* Spring Data Repository initialization phase.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
* @see java.lang.FunctionalInterface
|
||||
* @since 3.0
|
||||
*/
|
||||
// TODO: Does this need to be in org.springframework.data? Maybe move to org.springframework.data.domain?
|
||||
@FunctionalInterface
|
||||
public interface ManagedTypes {
|
||||
|
||||
/**
|
||||
* Factory method used to construct a new instance of {@link ManagedTypes} containing no {@link Class types}.
|
||||
*
|
||||
* @return an empty {@link ManagedTypes} instance.
|
||||
* @see java.util.Collections#emptySet()
|
||||
* @see #of(Iterable)
|
||||
*/
|
||||
@NonNull
|
||||
static ManagedTypes empty() {
|
||||
return of(Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to return a {@literal null-safe} instance of {@link ManagedTypes}.
|
||||
*
|
||||
* @param types {@link ManagedTypes} to evaluate.
|
||||
* @return the given {@link ManagedTypes} if not {@literal null}
|
||||
* or an {@link #empty()} {@link ManagedTypes} instance.
|
||||
* @see #empty()
|
||||
*/
|
||||
@NonNull
|
||||
static ManagedTypes nullSafeManagedTypes(@Nullable ManagedTypes types) {
|
||||
return types != null ? types : empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to construct {@link ManagedTypes} from the given, required {@link Iterable}
|
||||
* of {@link Class types}.
|
||||
*
|
||||
* @param types {@link Iterable} of {@link Class types} used to initialize the {@link ManagedTypes};
|
||||
* must not be {@literal null}.
|
||||
* @return new instance of {@link ManagedTypes} initialized the given, required {@link Iterable}
|
||||
* of {@link Class types}.
|
||||
* @see java.lang.Iterable
|
||||
* @see #of(Stream)
|
||||
* @see #of(Supplier)
|
||||
*/
|
||||
@NonNull
|
||||
static ManagedTypes of(@NonNull Iterable<? extends Class<?>> types) {
|
||||
return types::forEach;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to construct {@link ManagedTypes} from the given, required {@link Stream}
|
||||
* of {@link Class types}.
|
||||
*
|
||||
* @param types {@link Stream} of {@link Class types} used to initialize the {@link ManagedTypes};
|
||||
* must not be {@literal null}.
|
||||
* @return new instance of {@link ManagedTypes} initialized the given, required {@link Stream}
|
||||
* of {@link Class types}.
|
||||
* @see java.util.stream.Stream
|
||||
* @see #of(Iterable)
|
||||
* @see #of(Supplier)
|
||||
*/
|
||||
@NonNull
|
||||
static ManagedTypes of(@NonNull Stream<? extends Class<?>> types) {
|
||||
return types::forEach;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to construct {@link ManagedTypes} from the given, required {@link Supplier} of
|
||||
* an {@link Iterable} of {@link Class types}.
|
||||
*
|
||||
* @param dataProvider {@link Supplier} of an {@link Iterable} of {@link Class types} used to lazily initialize
|
||||
* the {@link ManagedTypes}; must not be {@literal null}.
|
||||
* @return new instance of {@link ManagedTypes} initialized the given, required {@link Supplier} of
|
||||
* an {@link Iterable} of {@link Class types}.
|
||||
* @see java.util.function.Supplier
|
||||
* @see java.lang.Iterable
|
||||
* @see #of(Iterable)
|
||||
* @see #of(Stream)
|
||||
*/
|
||||
@NonNull
|
||||
static ManagedTypes of(@NonNull Supplier<Iterable<? extends Class<?>>> dataProvider) {
|
||||
|
||||
return new ManagedTypes() {
|
||||
|
||||
final Lazy<Iterable<? extends Class<?>>> lazyProvider = Lazy.of(dataProvider);
|
||||
|
||||
@Override
|
||||
public void forEach(@NonNull Consumer<Class<?>> action) {
|
||||
lazyProvider.get().forEach(action);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given {@link Consumer action} to each of the {@link Class types} contained in
|
||||
* this {@link ManagedTypes} instance.
|
||||
*
|
||||
* @param action {@link Consumer} defining the action to perform on the {@link Class types}
|
||||
* contained in this {@link ManagedTypes} instance; must not be {@literal null}.
|
||||
* @see java.util.function.Consumer
|
||||
*/
|
||||
void forEach(Consumer<Class<?>> action);
|
||||
|
||||
/**
|
||||
* Returns all the {@link ManagedTypes} in a {@link List}.
|
||||
*
|
||||
* @return these {@link ManagedTypes} in a {@link List}; never {@literal null}.
|
||||
* @see java.util.List
|
||||
*/
|
||||
default List<Class<?>> toList() {
|
||||
|
||||
List<Class<?>> list = new ArrayList<>(100);
|
||||
forEach(list::add);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.data.util.TypeScanner;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -118,7 +119,7 @@ public interface AotContext {
|
||||
*/
|
||||
@NonNull
|
||||
default TypeScanner getTypeScanner() {
|
||||
return new TypeScanner(getClassLoader());
|
||||
return TypeScanner.typeScanner(getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,14 +130,12 @@ public interface AotContext {
|
||||
* model {@link Class types}; must not be {@literal null}.
|
||||
* @param packageNames {@link Collection} of {@link String package names} to scan.
|
||||
* @return a {@link Set} of {@link Class types} found during the scan.
|
||||
* @see TypeScanner#scanForTypesAnnotatedWith(Class[])
|
||||
* @see TypeScanner.Scanner#inPackages(Collection)
|
||||
* @see #getTypeScanner()
|
||||
*/
|
||||
default Set<Class<?>> scanPackageForTypes(@NonNull Collection<Class<? extends Annotation>> identifyingAnnotations,
|
||||
Collection<String> packageNames) {
|
||||
|
||||
return getTypeScanner().scanForTypesAnnotatedWith(identifyingAnnotations).inPackages(packageNames);
|
||||
return getTypeScanner().scanPackages(packageNames).forTypesAnnotatedWith(identifyingAnnotations).collectAsSet();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,105 +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.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.aop.SpringProxy;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
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.DecoratingProxy;
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
public class DataRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
/*
|
||||
NativeHint(trigger = MappingContext.class,
|
||||
types = {
|
||||
@TypeHint(types = {
|
||||
RepositoryFactoryBeanSupport.class,
|
||||
RepositoryFragmentsFactoryBean.class,
|
||||
RepositoryFragment.class,
|
||||
TransactionalRepositoryFactoryBeanSupport.class,
|
||||
QueryByExampleExecutor.class,
|
||||
MappingContext.class,
|
||||
RepositoryMetadata.class,
|
||||
RepositoryMetadata.class,
|
||||
}),
|
||||
@TypeHint(types = {ReadingConverter.class, WritingConverter.class}),
|
||||
@TypeHint(types = {Properties.class, BeanFactory.class, InputStreamSource[].class}),
|
||||
@TypeHint(types = Throwable.class, access = { TypeAccess.DECLARED_CONSTRUCTORS, TypeAccess.DECLARED_FIELDS}),
|
||||
@TypeHint(typeNames = {
|
||||
"org.springframework.data.projection.SpelEvaluatingMethodInterceptor$TargetWrapper",
|
||||
}, access = { TypeAccess.DECLARED_CONSTRUCTORS, TypeAccess.DECLARED_METHODS, TypeAccess.PUBLIC_METHODS })
|
||||
},
|
||||
jdkProxies = @JdkProxyHint(typeNames = {
|
||||
"org.springframework.data.annotation.QueryAnnotation",
|
||||
"org.springframework.core.annotation.SynthesizedAnnotation" }
|
||||
),
|
||||
initialization = @InitializationHint(types = AbstractMappingContext.class, initTime = InitializationTime.BUILD)
|
||||
)
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
|
||||
System.out.println("Spring data Runtime Hints");
|
||||
|
||||
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);
|
||||
});
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
hints.proxies().registerJdkProxy(TypeReference.of("org.springframework.data.annotation.QueryAnnotation"),
|
||||
TypeReference.of("org.springframework.core.annotation.SynthesizedAnnotation"));
|
||||
|
||||
hints.proxies().registerJdkProxy(TypeReference.of(AuditorAware.class), TypeReference.of(SpringProxy.class), TypeReference.of(Advised.class), TypeReference.of(DecoratingProxy.class));
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.data.aot.TypeScanner.Scanner;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.util.Lazy;
|
||||
|
||||
@@ -128,12 +127,10 @@ class DefaultAotRepositoryContext implements AotRepositoryContext {
|
||||
|
||||
if (!getIdentifyingAnnotations().isEmpty()) {
|
||||
|
||||
Scanner typeScanner = aotContext.getTypeScanner().scanForTypesAnnotatedWith(getIdentifyingAnnotations());
|
||||
Set<Class<?>> classes = typeScanner.inPackages(getBasePackages());
|
||||
Set<Class<?>> classes = aotContext.getTypeScanner().scanPackages(getBasePackages()).forTypesAnnotatedWith(getIdentifyingAnnotations()).collectAsSet();
|
||||
types.addAll(TypeCollector.inspect(classes).list());
|
||||
}
|
||||
|
||||
// context.get
|
||||
return types;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,25 +20,21 @@ import java.util.Set;
|
||||
|
||||
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.aot.BeanRegistrationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.data.ManagedTypes;
|
||||
import org.springframework.data.domain.ManagedTypes;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link BeanRegistrationAotProcessor} handling {@link #getModulePrefix() module prefixed} {@link ManagedTypes}
|
||||
* instances. This AOT processor allows store specific handling of discovered types to be registered.
|
||||
* {@link BeanRegistrationAotProcessor} handling {@link #getModuleIdentifier() module} {@link ManagedTypes} instances.
|
||||
* This AOT processor allows store specific handling of discovered types to be registered.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
@@ -46,28 +42,21 @@ import org.springframework.util.StringUtils;
|
||||
* @see org.springframework.beans.factory.aot.BeanRegistrationAotProcessor
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ManagedTypesRegistrationAotProcessor implements BeanRegistrationAotProcessor, BeanFactoryAware {
|
||||
public class ManagedTypesBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Nullable
|
||||
private String modulePrefix;
|
||||
@Nullable private String moduleIdentifier;
|
||||
|
||||
@Override
|
||||
public BeanRegistrationAotContribution processAheadOfTime(@NonNull RegisteredBean registeredBean) {
|
||||
return contribute(registeredBean.getMergedBeanDefinition(), registeredBean.getBeanClass(), registeredBean.getBeanName());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unused")
|
||||
protected BeanRegistrationAotContribution contribute(@NonNull RootBeanDefinition beanDefinition,
|
||||
@NonNull Class<?> beanType, @NonNull String beanName) {
|
||||
if (!isMatch(registeredBean.getBeanClass(), registeredBean.getBeanName())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return isMatch(beanType, beanName)
|
||||
? contribute(AotContext.from(beanFactory), beanFactory.getBean(beanName, ManagedTypes.class))
|
||||
: null;
|
||||
BeanFactory beanFactory = registeredBean.getBeanFactory();
|
||||
return contribute(AotContext.from(registeredBean.getBeanFactory()),
|
||||
beanFactory.getBean(registeredBean.getBeanName(), ManagedTypes.class));
|
||||
}
|
||||
|
||||
protected boolean isMatch(@Nullable Class<?> beanType, @Nullable String beanName) {
|
||||
@@ -79,16 +68,16 @@ public class ManagedTypesRegistrationAotProcessor implements BeanRegistrationAot
|
||||
}
|
||||
|
||||
protected boolean matchesPrefix(@Nullable String beanName) {
|
||||
return StringUtils.startsWithIgnoreCase(beanName, getModulePrefix());
|
||||
return StringUtils.startsWithIgnoreCase(beanName, getModuleIdentifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to provide a customized flavor of {@link BeanRegistrationAotContribution}. By overriding this method
|
||||
* calls to {@link #contributeType(ResolvableType, GenerationContext)} might no longer be issued.
|
||||
* Hook to provide a customized flavor of {@link BeanRegistrationAotContribution}. By overriding this method calls to
|
||||
* {@link #contributeType(ResolvableType, GenerationContext)} might no longer be issued.
|
||||
*
|
||||
* @param aotContext never {@literal null}.
|
||||
* @param managedTypes never {@literal null}.
|
||||
* @return new instance of {@link ManagedTypesRegistrationAotProcessor} or {@literal null} if nothing to do.
|
||||
* @return new instance of {@link ManagedTypesBeanRegistrationAotProcessor} or {@literal null} if nothing to do.
|
||||
*/
|
||||
@Nullable
|
||||
protected BeanRegistrationAotContribution contribute(AotContext aotContext, ManagedTypes managedTypes) {
|
||||
@@ -111,21 +100,16 @@ public class ManagedTypesRegistrationAotProcessor implements BeanRegistrationAot
|
||||
|
||||
TypeContributor.contribute(type.toClass(), annotationNamespaces, generationContext);
|
||||
|
||||
TypeUtils.resolveUsedAnnotations(type.toClass()).forEach(annotation ->
|
||||
TypeContributor.contribute(annotation.getType(), annotationNamespaces, generationContext));
|
||||
TypeUtils.resolveUsedAnnotations(type.toClass()).forEach(
|
||||
annotation -> TypeContributor.contribute(annotation.getType(), annotationNamespaces, generationContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public void setModulePrefix(@Nullable String modulePrefix) {
|
||||
this.modulePrefix = modulePrefix;
|
||||
public void setModuleIdentifier(@Nullable String moduleIdentifier) {
|
||||
this.moduleIdentifier = moduleIdentifier;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getModulePrefix() {
|
||||
return this.modulePrefix;
|
||||
public String getModuleIdentifier() {
|
||||
return this.moduleIdentifier;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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,
|
||||
@@ -22,7 +22,7 @@ import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationCode;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.data.ManagedTypes;
|
||||
import org.springframework.data.domain.ManagedTypes;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ public class ManagedTypesRegistrationAotContribution implements BeanRegistration
|
||||
|
||||
@NonNull
|
||||
protected ManagedTypes getManagedTypes() {
|
||||
return ManagedTypes.nullSafeManagedTypes(this.managedTypes);
|
||||
return managedTypes == null ? ManagedTypes.empty() : managedTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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,
|
||||
|
||||
@@ -26,8 +26,6 @@ public class PublicMethodReflectiveProcessor extends SimpleReflectiveProcessor {
|
||||
|
||||
@Override
|
||||
protected void registerTypeHint(ReflectionHints hints, Class<?> type) {
|
||||
System.out.println("invoking reflective hint annotation for : " + type);
|
||||
hints.registerType(type, builder -> builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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,
|
||||
@@ -197,10 +197,10 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
Map<String, RepositoryConfigurationExtensionSupport> repositoryConfigurationExtensionBeans =
|
||||
getBeanFactory().getBeansOfType(RepositoryConfigurationExtensionSupport.class);
|
||||
|
||||
repositoryConfigurationExtensionBeans.values().stream()
|
||||
.map(RepositoryConfigurationExtensionSupport::getIdentifyingAnnotations)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toCollection(() -> identifyingAnnotations));
|
||||
// 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,
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.aot;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
@@ -27,14 +28,16 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.data.ManagedTypes;
|
||||
import org.springframework.data.domain.ManagedTypes;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryInitializationAotProcessor} implementation used to encapsulate common data infrastructure concerns
|
||||
* and preprocess the {@link ConfigurableListableBeanFactory} ahead of the AOT compilation in order to prepare
|
||||
* the Spring Data {@link BeanDefinition BeanDefinitions} for AOT processing.
|
||||
* and preprocess the {@link ConfigurableListableBeanFactory} ahead of the AOT compilation in order to prepare the
|
||||
* Spring Data {@link BeanDefinition BeanDefinitions} for AOT processing.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
@@ -63,8 +66,11 @@ public class SpringDataBeanFactoryInitializationAotProcessor implements BeanFact
|
||||
|
||||
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
|
||||
|
||||
ValueHolder argumentValue = beanDefinition.getConstructorArgumentValues()
|
||||
.getArgumentValue(0, null, null, null);
|
||||
if(beanDefinition.getConstructorArgumentValues().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ValueHolder argumentValue = beanDefinition.getConstructorArgumentValues().getArgumentValue(0, null, null, null);
|
||||
|
||||
if (argumentValue.getValue() instanceof Supplier supplier) {
|
||||
|
||||
@@ -72,11 +78,16 @@ public class SpringDataBeanFactoryInitializationAotProcessor implements BeanFact
|
||||
logger.info(String.format("Replacing ManagedType bean definition %s.", beanName));
|
||||
}
|
||||
|
||||
BeanDefinition beanDefinitionReplacement = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ManagedTypes.class)
|
||||
.setFactoryMethod("of")
|
||||
.addConstructorArgValue(supplier.get())
|
||||
.getBeanDefinition();
|
||||
Object value = supplier.get();
|
||||
if(ObjectUtils.isArray(value)) {
|
||||
value = CollectionUtils.arrayToList(value);
|
||||
}
|
||||
if(!(value instanceof Iterable<?>)) {
|
||||
value = Collections.singleton(value);
|
||||
}
|
||||
|
||||
BeanDefinition beanDefinitionReplacement = BeanDefinitionBuilder.rootBeanDefinition(ManagedTypes.class)
|
||||
.setFactoryMethod("fromIterable").addConstructorArgValue(value).getBeanDefinition();
|
||||
|
||||
registry.removeBeanDefinition(beanName);
|
||||
registry.registerBeanDefinition(beanName, beanDefinitionReplacement);
|
||||
|
||||
@@ -1,166 +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.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Scanner used to scan for {@link Class types} that will be added to the AOT processing infrastructure.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
*/
|
||||
// TODO: Replace this with AnnotatedTypeScanner, maybe?
|
||||
public class TypeScanner {
|
||||
|
||||
/**
|
||||
* Factory method used to construct a new {@link TypeScanner} initialized with the given {@link ClassLoader}
|
||||
* used to resolve scanned {@link Class type}.
|
||||
*
|
||||
* @param classLoader {@link ClassLoader} used to resolve scanned {@link Class types}.
|
||||
* @return a new {@link TypeScanner}.
|
||||
*/
|
||||
@NonNull
|
||||
static TypeScanner scanner(@Nullable ClassLoader classLoader) {
|
||||
return new TypeScanner(classLoader);
|
||||
}
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link TypeScanner} initialized with the given {@link ClassLoader}
|
||||
* used to resolve scanned {@link Class types}.
|
||||
*
|
||||
* @param classLoader {@link ClassLoader} used to resolve scanned {@link Class types}.
|
||||
*/
|
||||
public TypeScanner(@Nullable ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans for {@link Class types} potentially annotated with any of the given {@link Annotation} types.
|
||||
*
|
||||
* @param annotations array of {@link Annotation} types used to filter the scanned {@link Class types};
|
||||
* must not be {@literal null}.
|
||||
* @return new instance of {@link Scanner}.
|
||||
* @see #scanForTypesAnnotatedWith(Collection)
|
||||
* @see Scanner
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Scanner scanForTypesAnnotatedWith(Class<? extends Annotation>... annotations) {
|
||||
return scanForTypesAnnotatedWith(Arrays.asList(annotations));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans for {@link Class types} potentially annotated with any of the given {@link Annotation} types.
|
||||
*
|
||||
* @param annotations {@link Collection} of {@link Annotation} types used to filter the scanned {@link Class types}.
|
||||
* @return new instance of {@link Scanner}.
|
||||
* @see #scanForTypesAnnotatedWith(Class[])
|
||||
* @see Scanner
|
||||
*/
|
||||
public Scanner scanForTypesAnnotatedWith(Collection<Class<? extends Annotation>> annotations) {
|
||||
return new ScannerImpl().includeTypesAnnotatedWith(annotations);
|
||||
}
|
||||
|
||||
public interface Scanner {
|
||||
|
||||
/**
|
||||
* Collects the {@link String names} of packages to scan.
|
||||
*
|
||||
* @param packageNames array of {@link String package names} ot scan; must not be {@literal null}.
|
||||
* @return the resolved, scanned {@link Class types}.
|
||||
* @see #inPackages(Collection)
|
||||
*/
|
||||
default Set<Class<?>> inPackages(String... packageNames) {
|
||||
return inPackages(Arrays.asList(packageNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the {@link String names} of packages to scan.
|
||||
*
|
||||
* @param packageNames {@link Collection} of {@link String package names} ot scan.
|
||||
* @return the resolved, scanned {@link Class types}.
|
||||
* @see #inPackages(String...)
|
||||
*/
|
||||
Set<Class<?>> inPackages(Collection<String> packageNames);
|
||||
}
|
||||
|
||||
class ScannerImpl implements Scanner {
|
||||
|
||||
ClassPathScanningCandidateComponentProvider componentProvider;
|
||||
|
||||
ScannerImpl() {
|
||||
|
||||
componentProvider = new ClassPathScanningCandidateComponentProvider(false);
|
||||
componentProvider.setEnvironment(new StandardEnvironment());
|
||||
componentProvider.setResourceLoader(new DefaultResourceLoader(classLoader));
|
||||
}
|
||||
|
||||
ScannerImpl includeTypesAnnotatedWith(Collection<Class<? extends Annotation>> annotations) {
|
||||
|
||||
nullSafeCollection(annotations).stream()
|
||||
.map(AnnotationTypeFilter::new)
|
||||
.forEach(componentProvider::addIncludeFilter);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> inPackages(Collection<String> packageNames) {
|
||||
|
||||
Set<Class<?>> types = new LinkedHashSet<>();
|
||||
|
||||
nullSafeCollection(packageNames).forEach(pkg ->
|
||||
componentProvider.findCandidateComponents(pkg).forEach(it ->
|
||||
resolveType(it.getBeanClassName()).ifPresent(types::add)));
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private <T> Collection<T> nullSafeCollection(@Nullable Collection<T> collection) {
|
||||
return collection != null ? collection : Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Class<?>> resolveType(String typeName) {
|
||||
|
||||
if (ClassUtils.isPresent(typeName, classLoader)) {
|
||||
try {
|
||||
return Optional.of(ClassUtils.forName(typeName, classLoader));
|
||||
} catch (ClassNotFoundException ignore) {
|
||||
// just do nothing
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 org.springframework.aop.SpringProxy;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.core.DecoratingProxy;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.domain.ReactiveAuditorAware;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
public class AuditingHints {
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} to be applied when auditing is enabled. Can be used along with
|
||||
* {@link org.springframework.context.annotation.ImportRuntimeHints @ImportRuntimeHints} for conditional registration.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
public static class AuditingRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
registerSpringProxy(AuditorAware.class, hints);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} to be applied when reactive auditing is enabled. Can be used along with
|
||||
* {@link org.springframework.context.annotation.ImportRuntimeHints @ImportRuntimeHints} for conditional registration.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
public static class ReactiveAuditingRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
registerSpringProxy(ReactiveAuditorAware.class, hints);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerSpringProxy(Class<?> type, RuntimeHints runtimeHints) {
|
||||
|
||||
runtimeHints.proxies().registerJdkProxy(TypeReference.of(type), TypeReference.of(SpringProxy.class),
|
||||
TypeReference.of(Advised.class), TypeReference.of(DecoratingProxy.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
public 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"), //
|
||||
TypeReference.of("org.springframework.core.annotation.SynthesizedAnnotation"));
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public class NamedQueriesBeanDefinitionBuilder {
|
||||
@SuppressWarnings("null")
|
||||
public NamedQueriesBeanDefinitionBuilder(String defaultLocation) {
|
||||
|
||||
Assert.hasText(defaultLocation, "DefaultLocation must not be null nor empty!");
|
||||
Assert.hasText(defaultLocation, "DefaultLocation must not be null nor empty");
|
||||
this.defaultLocation = defaultLocation;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public class NamedQueriesBeanDefinitionBuilder {
|
||||
*/
|
||||
public void setLocations(String locations) {
|
||||
|
||||
Assert.hasText(locations, "Locations must not be null nor empty!");
|
||||
Assert.hasText(locations, "Locations must not be null nor empty");
|
||||
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ class RepositoryBeanDefinitionBuilder {
|
||||
extension.getDefaultNamedQueryLocation());
|
||||
configuration.getNamedQueriesLocation().ifPresent(definitionBuilder::setLocations);
|
||||
|
||||
String namedQueriesBeanName = BeanDefinitionReaderUtils.uniqueBeanName(extension.getModulePrefix() + ".named-queries", registry);
|
||||
String namedQueriesBeanName = BeanDefinitionReaderUtils.uniqueBeanName(extension.getModuleIdentifier() + ".named-queries", registry);
|
||||
BeanDefinition namedQueries = definitionBuilder.build(configuration.getSource());
|
||||
registry.registerBeanDefinition(namedQueriesBeanName, namedQueries);
|
||||
|
||||
|
||||
@@ -22,12 +22,12 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aot.AotDetector;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
@@ -48,11 +48,8 @@ import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.core.metrics.ApplicationStartup;
|
||||
import org.springframework.core.metrics.StartupStep;
|
||||
import org.springframework.data.ManagedTypes;
|
||||
import org.springframework.data.aot.TypeScanner;
|
||||
import org.springframework.data.util.TypeScanner;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StopWatch;
|
||||
@@ -228,31 +225,9 @@ public class RepositoryConfigurationDelegate {
|
||||
private void registerAotComponents(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension,
|
||||
Map<String, RepositoryMetadata<?>> metadataByRepositoryBeanName) {
|
||||
|
||||
// Managed types lookup if possible
|
||||
if (extension instanceof RepositoryConfigurationExtensionSupport extensionSupport) {
|
||||
|
||||
String targetManagedTypesBeanName = String.format("%s.managed-types", extension.getModulePrefix());
|
||||
|
||||
if (!registry.isBeanNameInUse(targetManagedTypesBeanName)) {
|
||||
|
||||
// this needs to be lazy or we'd resolve types to early maybe
|
||||
Supplier<Set<Class<?>>> args = () -> {
|
||||
|
||||
Set<String> packages = metadataByRepositoryBeanName.values().stream()
|
||||
.flatMap(it -> it.getBasePackages().stream()).collect(Collectors.toSet());
|
||||
|
||||
return new TypeScanner(resourceLoader.getClassLoader())
|
||||
.scanForTypesAnnotatedWith(extensionSupport.getIdentifyingAnnotations()).inPackages(packages);
|
||||
};
|
||||
|
||||
// registry.registerBeanDefinition(targetManagedTypesBeanName, BeanDefinitionBuilder
|
||||
// .rootBeanDefinition(ManagedTypesBean.class).addConstructorArgValue(args).getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
// module-specific repository aot processor
|
||||
String repositoryAotProcessorBeanName = String.format("data-%s.repository-aot-processor" /* might be duplicate */,
|
||||
extension.getModulePrefix());
|
||||
extension.getModuleIdentifier());
|
||||
|
||||
if (!registry.isBeanNameInUse(repositoryAotProcessorBeanName)) {
|
||||
|
||||
@@ -395,18 +370,4 @@ public class RepositoryConfigurationDelegate {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ManagedTypesBean implements ManagedTypes {
|
||||
|
||||
private final Lazy<Set<Class<?>>> types;
|
||||
|
||||
public ManagedTypesBean(Supplier<Set<Class<?>>> types) {
|
||||
this.types = Lazy.of(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(@NonNull Consumer<Class<?>> action) {
|
||||
types.get().forEach(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,17 +56,7 @@ public interface RepositoryConfigurationExtension {
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
default String getModuleName() {
|
||||
return StringUtils.capitalize(getModulePrefix());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link String prefix} identifying the module.
|
||||
*
|
||||
* @return a {@link String prefix} identifying the module.
|
||||
* @see #getModuleName()
|
||||
*/
|
||||
String getModulePrefix();
|
||||
String getModuleName();
|
||||
|
||||
/**
|
||||
* Returns the {@link BeanRegistrationAotProcessor} type responsible for contributing AOT/native configuration
|
||||
@@ -111,13 +101,6 @@ public interface RepositoryConfigurationExtension {
|
||||
*/
|
||||
String getRepositoryFactoryBeanClassName();
|
||||
|
||||
/**
|
||||
* Returns the default location of the Spring Data named queries.
|
||||
*
|
||||
* @return must not be {@literal null} or empty.
|
||||
*/
|
||||
String getDefaultNamedQueryLocation();
|
||||
|
||||
/**
|
||||
* Callback to register additional bean definitions for a {@literal repositories} root node. This usually includes
|
||||
* beans you have to set up once independently of the number of repositories to be created. Will be called before any
|
||||
@@ -153,5 +136,4 @@ public interface RepositoryConfigurationExtension {
|
||||
* @param config will never be {@literal null}.
|
||||
*/
|
||||
void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config);
|
||||
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ public abstract class RepositoryConfigurationExtensionSupport implements Reposit
|
||||
|
||||
private boolean noMultiStoreSupport = false;
|
||||
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return StringUtils.capitalize(getModulePrefix());
|
||||
}
|
||||
|
||||
public <T extends RepositoryConfigurationSource> Collection<RepositoryConfiguration<T>> getRepositoryConfigurations(
|
||||
T configSource, ResourceLoader loader) {
|
||||
return getRepositoryConfigurations(configSource, loader, false);
|
||||
@@ -127,7 +132,7 @@ public abstract class RepositoryConfigurationExtensionSupport implements Reposit
|
||||
* @return
|
||||
* @since 1.9
|
||||
*/
|
||||
public Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
|
||||
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,11 @@ package org.springframework.data.util;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
@@ -28,6 +31,7 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -46,6 +50,10 @@ public class AnnotatedTypeScanner implements ResourceLoaderAware, EnvironmentAwa
|
||||
private @Nullable ResourceLoader resourceLoader;
|
||||
private @Nullable Environment environment;
|
||||
|
||||
private Consumer<ClassNotFoundException> classNotFoundAction = ex -> {
|
||||
throw new IllegalStateException(ex);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new {@link AnnotatedTypeScanner} for the given annotation types.
|
||||
*
|
||||
@@ -64,9 +72,18 @@ public class AnnotatedTypeScanner implements ResourceLoaderAware, EnvironmentAwa
|
||||
*/
|
||||
@SafeVarargs
|
||||
public AnnotatedTypeScanner(boolean considerInterfaces, Class<? extends Annotation>... annotationTypes) {
|
||||
this(considerInterfaces, Arrays.asList(annotationTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param considerInterfaces
|
||||
* @param annotationTypes
|
||||
* @since 3.0
|
||||
*/
|
||||
public AnnotatedTypeScanner(boolean considerInterfaces, Collection<Class<? extends Annotation>> annotationTypes) {
|
||||
|
||||
this.annotationTypess = Arrays.asList(annotationTypes);
|
||||
this.considerInterfaces = considerInterfaces;
|
||||
this.annotationTypess = annotationTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,11 +96,15 @@ public class AnnotatedTypeScanner implements ResourceLoaderAware, EnvironmentAwa
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
void setClassNotFoundAction(Consumer<ClassNotFoundException> classNotFoundAction) {
|
||||
this.classNotFoundAction = classNotFoundAction;
|
||||
}
|
||||
|
||||
public Set<Class<?>> findTypes(String... basePackages) {
|
||||
return findTypes(Arrays.asList(basePackages));
|
||||
}
|
||||
|
||||
public Set<Class<?>> findTypes(Iterable<String> basePackages) {
|
||||
Set<Class<?>> findTypes(Iterable<String> basePackages, Collection<TypeFilter> filters) {
|
||||
|
||||
ClassPathScanningCandidateComponentProvider provider = new InterfaceAwareScanner(considerInterfaces);
|
||||
|
||||
@@ -95,9 +116,7 @@ public class AnnotatedTypeScanner implements ResourceLoaderAware, EnvironmentAwa
|
||||
provider.setEnvironment(environment);
|
||||
}
|
||||
|
||||
for (Class<? extends Annotation> annotationType : annotationTypess) {
|
||||
provider.addIncludeFilter(new AnnotationTypeFilter(annotationType, true, considerInterfaces));
|
||||
}
|
||||
filters.forEach(provider::addIncludeFilter);
|
||||
|
||||
Set<Class<?>> types = new HashSet<>();
|
||||
|
||||
@@ -112,13 +131,13 @@ public class AnnotatedTypeScanner implements ResourceLoaderAware, EnvironmentAwa
|
||||
|
||||
if (beanClassName == null) {
|
||||
throw new IllegalStateException(
|
||||
String.format("Unable to obtain bean class name from bean definition %s", definition));
|
||||
String.format("Unable to obtain bean class name from bean definition %s!", definition));
|
||||
}
|
||||
|
||||
try {
|
||||
types.add(ClassUtils.forName(beanClassName, classLoader));
|
||||
} catch (ClassNotFoundException o_O) {
|
||||
throw new IllegalStateException(o_O);
|
||||
classNotFoundAction.accept(o_O);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,6 +145,10 @@ public class AnnotatedTypeScanner implements ResourceLoaderAware, EnvironmentAwa
|
||||
return types;
|
||||
}
|
||||
|
||||
public Set<Class<?>> findTypes(Iterable<String> basePackages) {
|
||||
return findTypes(basePackages, Streamable.of(annotationTypess).stream().map(annotation -> new AnnotationTypeFilter(annotation, true, considerInterfaces)).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom extension of {@link ClassPathScanningCandidateComponentProvider} to make sure interfaces to not get dropped
|
||||
* from scanning results.
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
|
||||
/**
|
||||
* A caching {@link TypeScanner} implementation delegating to {@link AnnotatedTypeScanner}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
class DelegatingTypeScanner implements TypeScanner {
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
private final Environment environment;
|
||||
|
||||
private Collection<String> packageNames;
|
||||
private Collection<TypeFilter> includeFilters;
|
||||
|
||||
private Consumer<ClassNotFoundException> classNotFoundAction;
|
||||
|
||||
private Lazy<Set<Class<?>>> scanResult = Lazy.of(this::collect);
|
||||
|
||||
DelegatingTypeScanner(ResourceLoader resourceLoader) {
|
||||
this(new StandardEnvironment(), resourceLoader);
|
||||
}
|
||||
|
||||
DelegatingTypeScanner(Environment environment, ResourceLoader resourceLoader) {
|
||||
|
||||
this(environment, resourceLoader, Collections.emptyList(),
|
||||
Collections.singleton((metadataReader, readerFactory) -> true), error -> {});
|
||||
}
|
||||
|
||||
DelegatingTypeScanner(Environment environment, ResourceLoader resourceLoader, Collection<String> packageNames,
|
||||
Collection<TypeFilter> includeFilters, Consumer<ClassNotFoundException> classNotFoundAction) {
|
||||
|
||||
this.environment = environment;
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.packageNames = new ArrayList<>(packageNames);
|
||||
this.includeFilters = new ArrayList<>(includeFilters);
|
||||
this.classNotFoundAction = classNotFoundAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeScanner scanPackages(Collection<String> packageNames) {
|
||||
return new DelegatingTypeScanner(environment, resourceLoader, packageNames, includeFilters, classNotFoundAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeScanner forTypesAnnotatedWith(Collection<Class<? extends Annotation>> annotations) {
|
||||
|
||||
return new DelegatingTypeScanner(environment, resourceLoader, packageNames,
|
||||
annotations.stream().map(DelegatingTypeScanner::annotationFilter).collect(Collectors.toSet()),
|
||||
classNotFoundAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeScanner onClassNotFound(Consumer<ClassNotFoundException> action) {
|
||||
return new DelegatingTypeScanner(environment, resourceLoader, packageNames, includeFilters, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> collectAsSet() {
|
||||
return scanResult.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<Class<?>> action) {
|
||||
collectAsSet().forEach(action);
|
||||
}
|
||||
|
||||
private Set<Class<?>> collect() {
|
||||
|
||||
AnnotatedTypeScanner annotatedTypeScanner = new AnnotatedTypeScanner(false, Collections.emptyList());
|
||||
annotatedTypeScanner.setResourceLoader(resourceLoader);
|
||||
annotatedTypeScanner.setEnvironment(environment);
|
||||
if (classNotFoundAction != null) {
|
||||
annotatedTypeScanner.setClassNotFoundAction(classNotFoundAction);
|
||||
}
|
||||
return annotatedTypeScanner.findTypes(packageNames, includeFilters);
|
||||
}
|
||||
|
||||
private static AnnotationTypeFilter annotationFilter(Class<? extends Annotation> annotation) {
|
||||
return new AnnotationTypeFilter(annotation, true, false);
|
||||
}
|
||||
}
|
||||
139
src/main/java/org/springframework/data/util/TypeScanner.java
Normal file
139
src/main/java/org/springframework/data/util/TypeScanner.java
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A scanner that searches the classpath for matching types within given target packages.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface TypeScanner {
|
||||
|
||||
/**
|
||||
* Create a new {@link TypeScanner} using the given {@link ClassLoader}.
|
||||
*
|
||||
* @param classLoader must not be {@literal null}.
|
||||
* @return new instance of {@link TypeScanner}.
|
||||
*/
|
||||
static TypeScanner typeScanner(ClassLoader classLoader) {
|
||||
|
||||
Assert.notNull(classLoader, "ClassLoader must not be null!");
|
||||
return typeScanner(new DefaultResourceLoader(classLoader));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TypeScanner} using the given {@link ResourceLoader}.
|
||||
*
|
||||
* @param resourceLoader must not be {@literal null}.
|
||||
* @return new instance of {@link TypeScanner}.
|
||||
*/
|
||||
static TypeScanner typeScanner(ResourceLoader resourceLoader) {
|
||||
|
||||
Assert.notNull(resourceLoader, "ResourceLoader must not be null!");
|
||||
return new DelegatingTypeScanner(resourceLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TypeScanner} using the given {@link ApplicationContext}.
|
||||
*
|
||||
* @param context must not be {@literal null}.
|
||||
* @return new instance of {@link TypeScanner}.
|
||||
*/
|
||||
static TypeScanner typeScanner(ApplicationContext context) {
|
||||
|
||||
Assert.notNull(context, "Context must not be null!");
|
||||
return new DelegatingTypeScanner(context.getEnvironment(), context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the {@link String names} of packages to scan.
|
||||
*
|
||||
* @param packageNames array of {@link String package names} to scan; Must not be {@literal null}.
|
||||
* @return new instance of {@link TypeScanner}.
|
||||
* @see #scanPackages(Collection)
|
||||
*/
|
||||
default TypeScanner scanPackages(String... packageNames) {
|
||||
return scanPackages(Arrays.asList(packageNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the {@link String names} of packages to scan.
|
||||
*
|
||||
* @param packageNames {@link Collection} of {@link String package names} to scan.
|
||||
* @return new instance of {@link TypeScanner}.
|
||||
* @see #scanPackages(String...)
|
||||
*/
|
||||
TypeScanner scanPackages(Collection<String> packageNames);
|
||||
|
||||
/**
|
||||
* Define annotations identifying types to include in the scan result.
|
||||
*
|
||||
* @param annotations must not be {@literal null}.
|
||||
* @return new instance of {@link TypeScanner}.
|
||||
* @see #forTypesAnnotatedWith(Collection)
|
||||
*/
|
||||
default TypeScanner forTypesAnnotatedWith(Class<? extends Annotation>... annotations) {
|
||||
return forTypesAnnotatedWith(Arrays.asList(annotations));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define annotations identifying types to include in the scan result.
|
||||
*
|
||||
* @param annotations must not be {@literal null}.
|
||||
* @return new instance of {@link TypeScanner}.
|
||||
*/
|
||||
TypeScanner forTypesAnnotatedWith(Collection<Class<? extends Annotation>> annotations);
|
||||
|
||||
/**
|
||||
* Define what happens in the case of a {@link ClassNotFoundException}.
|
||||
*
|
||||
* @param action must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
TypeScanner onClassNotFound(Consumer<ClassNotFoundException> action);
|
||||
|
||||
/**
|
||||
* Obtain the scan result.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
default Set<Class<?>> collectAsSet() {
|
||||
|
||||
LinkedHashSet<Class<?>> result = new LinkedHashSet<>();
|
||||
forEach(result::add);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the given action for each element found while scanning.
|
||||
*
|
||||
* @param action must not be {@literal null}.
|
||||
*/
|
||||
void forEach(Consumer<Class<?>> action);
|
||||
}
|
||||
Reference in New Issue
Block a user