@@ -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"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user