Open up AuditingBeanDefinitionRegistrarSupport.registerAuditHandlerBeanDefinition(…) to allow additional bean registrations.
Original Pull Request: #2624
This commit is contained in:
committed by
Christoph Strobl
parent
4e23153816
commit
0a423ac3f5
@@ -17,6 +17,7 @@ package org.springframework.data.aot;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
@@ -26,26 +27,23 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
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;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* The context in which the AOT processing happens.
|
||||
*
|
||||
* Grants access to the {@link ConfigurableListableBeanFactory beanFactory} and {@link ClassLoader}. Holds a few
|
||||
* convenience methods to check if a type {@link #isTypePresent(String) is present} and allows resolution of them.
|
||||
*
|
||||
* <strong>WARNING:</strong> Unstable internal API!
|
||||
* The context in which the AOT processing happens. Grants access to the {@link ConfigurableListableBeanFactory
|
||||
* beanFactory} and {@link ClassLoader}. Holds a few convenience methods to check if a type
|
||||
* {@link #isTypePresent(String) is present} and allows resolution of them throug {@link TypeIntrospector} and
|
||||
* {@link IntrospectedBeanDefinition}.
|
||||
* <p>
|
||||
* Mainly for internal use within the framework.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
|
||||
* @author Mark Paluch
|
||||
* @see BeanFactory
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface AotContext {
|
||||
@@ -57,22 +55,11 @@ public interface AotContext {
|
||||
* @return a new instance of {@link AotContext}.
|
||||
* @see BeanFactory
|
||||
*/
|
||||
static AotContext from(@NonNull BeanFactory beanFactory) {
|
||||
static AotContext from(BeanFactory beanFactory) {
|
||||
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
|
||||
return new AotContext() {
|
||||
|
||||
private final ConfigurableListableBeanFactory bf = beanFactory instanceof ConfigurableListableBeanFactory
|
||||
? (ConfigurableListableBeanFactory) beanFactory
|
||||
: new DefaultListableBeanFactory(beanFactory);
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return bf;
|
||||
}
|
||||
};
|
||||
return new DefaultAotContext(beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,10 +71,9 @@ public interface AotContext {
|
||||
ConfigurableListableBeanFactory getBeanFactory();
|
||||
|
||||
/**
|
||||
* Returns the {@link ClassLoader} used by this {@link AotContext} to resolve {@link Class types}.
|
||||
*
|
||||
* By default, this is the same {@link ClassLoader} used by the {@link BeanFactory} to resolve {@link Class types}
|
||||
* declared in bean definitions.
|
||||
* Returns the {@link ClassLoader} used by this {@link AotContext} to resolve {@link Class types}. By default, this is
|
||||
* the same {@link ClassLoader} used by the {@link BeanFactory} to resolve {@link Class types} declared in bean
|
||||
* definitions.
|
||||
*
|
||||
* @return the {@link ClassLoader} used by this {@link AotContext} to resolve {@link Class types}.
|
||||
* @see ConfigurableListableBeanFactory#getBeanClassLoader()
|
||||
@@ -98,184 +84,196 @@ public interface AotContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@link String named} {@link Class type} is present on the application classpath.
|
||||
* Returns the required {@link ClassLoader} used by this {@link AotContext} to resolve {@link Class types}. By
|
||||
* default, this is the same {@link ClassLoader} used by the {@link BeanFactory} to resolve {@link Class types}
|
||||
* declared in bean definitions.
|
||||
*
|
||||
* @return the {@link ClassLoader} used by this {@link AotContext} to resolve {@link Class types}.
|
||||
* @throws IllegalStateException if no {@link ClassLoader} is available.
|
||||
*/
|
||||
default ClassLoader getRequiredClassLoader() {
|
||||
|
||||
ClassLoader loader = getClassLoader();
|
||||
|
||||
if (loader == null) {
|
||||
throw new IllegalStateException("Required ClassLoader is not available");
|
||||
}
|
||||
|
||||
return loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link TypeIntrospector} to obtain further detail about a {@link Class type} given its fully-qualified
|
||||
* type name
|
||||
*
|
||||
* @param typeName {@link String name} of the {@link Class type} to evaluate; must not be {@literal null}.
|
||||
* @return {@literal true} if the given {@link String named} {@link Class type} is present
|
||||
* on the application classpath.
|
||||
* @see #getClassLoader()
|
||||
* @return the type introspector for further type-based introspection.
|
||||
*/
|
||||
default boolean isTypePresent(@NonNull String typeName) {
|
||||
return ClassUtils.isPresent(typeName, getClassLoader());
|
||||
}
|
||||
TypeIntrospector introspectType(String typeName);
|
||||
|
||||
/**
|
||||
* Returns a new {@link TypeScanner} used to scan for {@link Class types} that will be contributed to the AOT
|
||||
* processing infrastructure.
|
||||
*
|
||||
* @return a {@link TypeScanner} used to scan for {@link Class types} that will be contributed to the AOT
|
||||
* processing infrastructure.
|
||||
* @return a {@link TypeScanner} used to scan for {@link Class types} that will be contributed to the AOT processing
|
||||
* infrastructure.
|
||||
* @see TypeScanner
|
||||
*/
|
||||
@NonNull
|
||||
default TypeScanner getTypeScanner() {
|
||||
return TypeScanner.typeScanner(getClassLoader());
|
||||
return TypeScanner.typeScanner(getRequiredClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans for {@link Class types} in the given {@link String named packages} annotated with the store-specific
|
||||
* {@link Annotation identifying annotations}.
|
||||
*
|
||||
* @param identifyingAnnotations {@link Collection} of {@link Annotation Annotations} identifying store-specific
|
||||
* model {@link Class types}; must not be {@literal null}.
|
||||
* @param identifyingAnnotations {@link Collection} of {@link Annotation Annotations} identifying store-specific 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 #getTypeScanner()
|
||||
*/
|
||||
default Set<Class<?>> scanPackageForTypes(@NonNull Collection<Class<? extends Annotation>> identifyingAnnotations,
|
||||
default Set<Class<?>> scanPackageForTypes(Collection<Class<? extends Annotation>> identifyingAnnotations,
|
||||
Collection<String> packageNames) {
|
||||
|
||||
return getTypeScanner().scanPackages(packageNames).forTypesAnnotatedWith(identifyingAnnotations).collectAsSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the required {@link String named} {@link Class type}.
|
||||
* Returns a {@link IntrospectedBeanDefinition} to obtain further detail about the underlying bean definition. A
|
||||
* introspected bean definition can also point to an absent bean definition.
|
||||
*
|
||||
* @param typeName {@link String} containing the {@literal fully-qualified class name} of the {@link Class type}
|
||||
* to resolve; must not be {@literal null}.
|
||||
* @return a resolved {@link Class type} for the given, required {@link String name}.
|
||||
* @throws TypeNotPresentException if the {@link String named} {@link Class type} cannot be found.
|
||||
* @param reference {@link BeanReference} to the managed bean.
|
||||
* @return the introspected bean definition.
|
||||
*/
|
||||
@NonNull
|
||||
default Class<?> resolveRequiredType(@NonNull String typeName) throws TypeNotPresentException {
|
||||
|
||||
try {
|
||||
return ClassUtils.forName(typeName, getClassLoader());
|
||||
} catch (ClassNotFoundException cause) {
|
||||
throw new TypeNotPresentException(typeName, cause);
|
||||
}
|
||||
default IntrospectedBeanDefinition introspectBeanDefinition(BeanReference reference) {
|
||||
return introspectBeanDefinition(reference.getBeanName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the given {@link String named} {@link Class type} if present.
|
||||
* Returns a {@link IntrospectedBeanDefinition} to obtain further detail about the underlying bean definition. A
|
||||
* introspected bean definition can also point to an absent bean definition.
|
||||
*
|
||||
* @param typeName {@link String} containing the {@literal fully-qualified class name} of the {@link Class type}
|
||||
* to resolve; must not be {@literal null}.
|
||||
* @return an {@link Optional} value containing the {@link Class type}
|
||||
* if the {@link String fully-qualified class name} is present on the application classpath.
|
||||
* @see #isTypePresent(String)
|
||||
* @see #resolveRequiredType(String)
|
||||
* @see java.util.Optional
|
||||
* @param beanName {@link String} containing the {@literal name} of the bean to evaluate; must not be {@literal null}.
|
||||
* @return the introspected bean definition.
|
||||
*/
|
||||
default Optional<Class<?>> resolveType(@NonNull String typeName) {
|
||||
|
||||
return isTypePresent(typeName)
|
||||
? Optional.of(resolveRequiredType(typeName))
|
||||
: Optional.empty();
|
||||
}
|
||||
IntrospectedBeanDefinition introspectBeanDefinition(String beanName);
|
||||
|
||||
/**
|
||||
* Resolves the {@link BeanDefinition bean's} defined {@link Class type}.
|
||||
*
|
||||
* @param beanReference {@link BeanReference} to the managed bean.
|
||||
* @return the {@link Class type} of the {@link BeanReference referenced bean} if defined; may be {@literal null}.
|
||||
* @see BeanReference
|
||||
* Type-based introspector to resolve {@link Class} from a type name and to introspect the bean factory for presence
|
||||
* of beans.
|
||||
*/
|
||||
@Nullable
|
||||
default Class<?> resolveType(@NonNull BeanReference beanReference) {
|
||||
return getBeanFactory().getType(beanReference.getBeanName(), false);
|
||||
}
|
||||
interface TypeIntrospector {
|
||||
|
||||
/**
|
||||
* Gets the {@link BeanDefinition} for the given, required {@link String named bean}.
|
||||
*
|
||||
* @param beanName {@link String} containing the {@literal name} of the bean; must not be {@literal null}.
|
||||
* @return the {@link BeanDefinition} for the given, required {@link String named bean}.
|
||||
* @throws NoSuchBeanDefinitionException if a {@link BeanDefinition} cannot be found for
|
||||
* the {@link String named bean}.
|
||||
* @see BeanDefinition
|
||||
*/
|
||||
@NonNull
|
||||
default BeanDefinition getBeanDefinition(@NonNull String beanName) throws NoSuchBeanDefinitionException {
|
||||
return getBeanFactory().getBeanDefinition(beanName);
|
||||
}
|
||||
/**
|
||||
* Determines whether @link Class type} is present on the application classpath.
|
||||
*
|
||||
* @return {@literal true} if the {@link Class type} is present on the application classpath.
|
||||
* @see #getClassLoader()
|
||||
*/
|
||||
boolean isTypePresent();
|
||||
|
||||
/**
|
||||
* Gets the {@link RootBeanDefinition} for the given, required {@link String bean name}.
|
||||
*
|
||||
* @param beanName {@link String} containing the {@literal name} of the bean.
|
||||
* @return the {@link RootBeanDefinition} for the given, required {@link String bean name}.
|
||||
* @throws NoSuchBeanDefinitionException if a {@link BeanDefinition} cannot be found for
|
||||
* the {@link String named bean}.
|
||||
* @throws IllegalStateException if the bean is not a {@link RootBeanDefinition root bean}.
|
||||
* @see RootBeanDefinition
|
||||
*/
|
||||
@NonNull
|
||||
default RootBeanDefinition getRootBeanDefinition(@NonNull String beanName) throws NoSuchBeanDefinitionException {
|
||||
/**
|
||||
* Resolves the required {@link String named} {@link Class type}.
|
||||
*
|
||||
* @return a resolved {@link Class type} for the given.
|
||||
* @throws TypeNotPresentException if the {@link Class type} cannot be found.
|
||||
*/
|
||||
Class<?> resolveRequiredType() throws TypeNotPresentException;
|
||||
|
||||
BeanDefinition beanDefinition = getBeanDefinition(beanName);
|
||||
/**
|
||||
* Resolves the {@link Class type} if present.
|
||||
*
|
||||
* @return an {@link Optional} value containing the {@link Class type} if the type is present on the application
|
||||
* classpath.
|
||||
* @see #isTypePresent()
|
||||
* @see #resolveRequiredType()
|
||||
* @see java.util.Optional
|
||||
*/
|
||||
Optional<Class<?>> resolveType();
|
||||
|
||||
if (beanDefinition instanceof RootBeanDefinition rootBeanDefinition) {
|
||||
return rootBeanDefinition;
|
||||
/**
|
||||
* Determines whether the {@link Class type} is declared on the application classpath and performs the given,
|
||||
* required {@link Consumer action} if present.
|
||||
*
|
||||
* @param action {@link Consumer} defining the action to perform on the resolved {@link Class type}; must not be
|
||||
* {@literal null}.
|
||||
* @see java.util.function.Consumer
|
||||
* @see #resolveType()
|
||||
*/
|
||||
default void ifTypePresent(Consumer<Class<?>> action) {
|
||||
resolveType().ifPresent(action);
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format("%s is not a root bean", beanName));
|
||||
/**
|
||||
* Determines whether the associated bean factory contains at least one bean of this type.
|
||||
*
|
||||
* @return {@literal true} if the {@link Class type} is present on the application classpath.
|
||||
*/
|
||||
boolean hasBean();
|
||||
|
||||
/**
|
||||
* Return a {@link List} containing bean names that implement this type.
|
||||
*
|
||||
* @return a {@link List} of bean names. The list is empty if the bean factory does not hold any beans of this type.
|
||||
*/
|
||||
List<String> getBeanNames();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a bean identified by the given, required {@link String name} is a
|
||||
* {@link org.springframework.beans.factory.FactoryBean}.
|
||||
*
|
||||
* @param beanName {@link String} containing the {@literal name} of the bean to evaluate;
|
||||
* must not be {@literal null}.
|
||||
* @return {@literal true} if the bean identified by the given, required {@link String name} is a
|
||||
* {@link org.springframework.beans.factory.FactoryBean}.
|
||||
* Interface defining introspection methods for bean definitions.
|
||||
*/
|
||||
default boolean isFactoryBean(@NonNull String beanName) {
|
||||
return getBeanFactory().isFactoryBean(beanName);
|
||||
interface IntrospectedBeanDefinition {
|
||||
|
||||
/**
|
||||
* Determines whether a bean definition identified by the given, required {@link String name} is present.
|
||||
*
|
||||
* @return {@literal true} if the bean definition identified by the given, required {@link String name} registered
|
||||
* with.
|
||||
*/
|
||||
boolean isPresent();
|
||||
|
||||
/**
|
||||
* Determines whether a bean identified by the given, required {@link String name} is a
|
||||
* {@link org.springframework.beans.factory.FactoryBean}.
|
||||
*
|
||||
* @return {@literal true} if the bean identified by the given, required {@link String name} is a
|
||||
* {@link org.springframework.beans.factory.FactoryBean}.
|
||||
*/
|
||||
boolean isFactoryBean();
|
||||
|
||||
/**
|
||||
* Gets the {@link BeanDefinition} for the given, required {@link String named bean}.
|
||||
*
|
||||
* @return the {@link BeanDefinition} for the given, required {@link String named bean}.
|
||||
* @throws NoSuchBeanDefinitionException if a {@link BeanDefinition} cannot be found for the {@link String named
|
||||
* bean}.
|
||||
* @see BeanDefinition
|
||||
*/
|
||||
|
||||
BeanDefinition getBeanDefinition() throws NoSuchBeanDefinitionException;
|
||||
|
||||
/**
|
||||
* Gets the {@link RootBeanDefinition} for the given, required {@link String bean name}.
|
||||
*
|
||||
* @return the {@link RootBeanDefinition} for the given, required {@link String bean name}.
|
||||
* @throws NoSuchBeanDefinitionException if a {@link BeanDefinition} cannot be found for the {@link String named
|
||||
* bean}.
|
||||
* @throws IllegalStateException if the bean is not a {@link RootBeanDefinition root bean}.
|
||||
* @see RootBeanDefinition
|
||||
*/
|
||||
RootBeanDefinition getRootBeanDefinition() throws NoSuchBeanDefinitionException;
|
||||
|
||||
/**
|
||||
* Resolves the {@link BeanDefinition bean's} defined {@link Class type}.
|
||||
*
|
||||
* @return the {@link Class type} of the {@link BeanReference referenced bean} if defined; may be {@literal null}.
|
||||
* @see BeanReference
|
||||
*/
|
||||
@Nullable
|
||||
Class<?> resolveType();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a Spring {@link org.springframework.transaction.TransactionManager} is present.
|
||||
*
|
||||
* @return {@literal true} if a Spring {@link org.springframework.transaction.TransactionManager} is present.
|
||||
*/
|
||||
default boolean isTransactionManagerPresent() {
|
||||
|
||||
return resolveType("org.springframework.transaction.TransactionManager")
|
||||
.filter(it -> !ObjectUtils.isEmpty(getBeanFactory().getBeanNamesForType(it)))
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given, required {@link String type name} is declared on the application classpath
|
||||
* and performs the given, required {@link Consumer action} if present.
|
||||
*
|
||||
* @param typeName {@link String name} of the {@link Class type} to process; must not be {@literal null}.
|
||||
* @param action {@link Consumer} defining the action to perform on the resolved {@link Class type};
|
||||
* must not be {@literal null}.
|
||||
* @see java.util.function.Consumer
|
||||
* @see #resolveType(String)
|
||||
*/
|
||||
default void ifTypePresent(@NonNull String typeName, @NonNull Consumer<Class<?>> action) {
|
||||
resolveType(typeName).ifPresent(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given {@link Consumer action} on any {@link org.springframework.transaction.TransactionManager} beans
|
||||
* defined in the application context.
|
||||
*
|
||||
* @param beanNamesConsumer {@link Consumer} defining the action to perform on
|
||||
* the {@link org.springframework.transaction.TransactionManager} beans if present; must not be {@literal null}.
|
||||
* @see java.util.function.Consumer
|
||||
*/
|
||||
default void ifTransactionManagerPresent(@NonNull Consumer<String[]> beanNamesConsumer) {
|
||||
|
||||
ifTypePresent("org.springframework.transaction.TransactionManager", txMgrType -> {
|
||||
String[] txMgrBeanNames = getBeanFactory().getBeanNamesForType(txMgrType);
|
||||
if (!ObjectUtils.isEmpty(txMgrBeanNames)) {
|
||||
beanNamesConsumer.accept(txMgrBeanNames);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,14 +25,11 @@ import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryInformationSupport;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* {@link RepositoryInformation} based on {@link RepositoryMetadata} collected at build time.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @see org.springframework.data.repository.core.RepositoryInformation
|
||||
* @see org.springframework.data.repository.core.RepositoryMetadata
|
||||
* @since 3.0
|
||||
*/
|
||||
class AotRepositoryInformation extends RepositoryInformationSupport implements RepositoryInformation {
|
||||
@@ -47,20 +44,19 @@ class AotRepositoryInformation extends RepositoryInformationSupport implements R
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCustomMethod(@NonNull Method method) {
|
||||
public boolean isCustomMethod(Method method) {
|
||||
// TODO:
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBaseClassMethod(@NonNull Method method) {
|
||||
public boolean isBaseClassMethod(Method method) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Method getTargetClassMethod(@NonNull Method method) {
|
||||
public Method getTargetClassMethod(Method method) {
|
||||
// TODO
|
||||
return method;
|
||||
}
|
||||
@@ -69,7 +65,6 @@ class AotRepositoryInformation extends RepositoryInformationSupport implements R
|
||||
* @return configured repository fragments.
|
||||
* @since 3.0
|
||||
*/
|
||||
@NonNull
|
||||
public Set<RepositoryFragment<?>> getFragments() {
|
||||
return new LinkedHashSet<>(fragments.get());
|
||||
}
|
||||
|
||||
@@ -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.aot;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Default {@link AotContext} implementation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.0
|
||||
*/
|
||||
class DefaultAotContext implements AotContext {
|
||||
|
||||
private final ConfigurableListableBeanFactory factory;
|
||||
|
||||
public DefaultAotContext(BeanFactory beanFactory) {
|
||||
factory = beanFactory instanceof ConfigurableListableBeanFactory ? (ConfigurableListableBeanFactory) beanFactory
|
||||
: new DefaultListableBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeIntrospector introspectType(String typeName) {
|
||||
return new DefaultTypeIntrospector(typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntrospectedBeanDefinition introspectBeanDefinition(String beanName) {
|
||||
return new DefaultIntrospectedBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
class DefaultTypeIntrospector implements TypeIntrospector {
|
||||
|
||||
private final String typeName;
|
||||
|
||||
DefaultTypeIntrospector(String typeName) {
|
||||
this.typeName = typeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTypePresent() {
|
||||
return ClassUtils.isPresent(typeName, getClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> resolveRequiredType() throws TypeNotPresentException {
|
||||
try {
|
||||
return ClassUtils.forName(typeName, getClassLoader());
|
||||
} catch (ClassNotFoundException cause) {
|
||||
throw new TypeNotPresentException(typeName, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Class<?>> resolveType() {
|
||||
return isTypePresent() ? Optional.of(resolveRequiredType()) : Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBean() {
|
||||
return !getBeanNames().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getBeanNames() {
|
||||
return isTypePresent() ? Arrays.asList(factory.getBeanNamesForType(resolveRequiredType()))
|
||||
: Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultIntrospectedBeanDefinition implements IntrospectedBeanDefinition {
|
||||
|
||||
private final String beanName;
|
||||
|
||||
DefaultIntrospectedBeanDefinition(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
return factory.containsBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFactoryBean() {
|
||||
return factory.isFactoryBean(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDefinition getBeanDefinition() throws NoSuchBeanDefinitionException {
|
||||
return factory.getBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootBeanDefinition getRootBeanDefinition() throws NoSuchBeanDefinitionException {
|
||||
BeanDefinition beanDefinition = getBeanDefinition();
|
||||
|
||||
if (beanDefinition instanceof RootBeanDefinition rootBeanDefinition) {
|
||||
return rootBeanDefinition;
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format("%s is not a root bean", beanName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> resolveType() {
|
||||
return factory.getType(beanName, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,26 +35,23 @@ import org.springframework.data.util.Lazy;
|
||||
*/
|
||||
class DefaultAotRepositoryContext implements AotRepositoryContext {
|
||||
|
||||
private AotContext aotContext;
|
||||
|
||||
private final AotContext aotContext;
|
||||
private final Lazy<Set<MergedAnnotation<Annotation>>> resolvedAnnotations = Lazy.of(this::discoverAnnotations);
|
||||
private final Lazy<Set<Class<?>>> managedTypes = Lazy.of(this::discoverTypes);
|
||||
|
||||
private RepositoryInformation repositoryInformation;
|
||||
|
||||
private Set<String> basePackages;
|
||||
private Set<Class<? extends Annotation>> identifyingAnnotations;
|
||||
|
||||
private String beanName;
|
||||
|
||||
public DefaultAotRepositoryContext(AotContext aotContext) {
|
||||
this.aotContext = aotContext;
|
||||
}
|
||||
|
||||
public AotContext getAotContext() {
|
||||
return aotContext;
|
||||
}
|
||||
|
||||
public void setAotContext(AotContext aotContext) {
|
||||
this.aotContext = aotContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return getAotContext().getBeanFactory();
|
||||
@@ -106,6 +103,16 @@ class DefaultAotRepositoryContext implements AotRepositoryContext {
|
||||
return managedTypes.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeIntrospector introspectType(String typeName) {
|
||||
return aotContext.introspectType(typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntrospectedBeanDefinition introspectBeanDefinition(String beanName) {
|
||||
return aotContext.introspectBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
protected Set<MergedAnnotation<Annotation>> discoverAnnotations() {
|
||||
|
||||
Set<MergedAnnotation<Annotation>> annotations = getResolvedTypes().stream()
|
||||
@@ -127,7 +134,8 @@ class DefaultAotRepositoryContext implements AotRepositoryContext {
|
||||
|
||||
if (!getIdentifyingAnnotations().isEmpty()) {
|
||||
|
||||
Set<Class<?>> classes = aotContext.getTypeScanner().scanPackages(getBasePackages()).forTypesAnnotatedWith(getIdentifyingAnnotations()).collectAsSet();
|
||||
Set<Class<?>> classes = aotContext.getTypeScanner().scanPackages(getBasePackages())
|
||||
.forTypesAnnotatedWith(getIdentifyingAnnotations()).collectAsSet();
|
||||
types.addAll(TypeCollector.inspect(classes).list());
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ 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.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
@@ -27,7 +28,6 @@ import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
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;
|
||||
@@ -38,8 +38,6 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
* @see org.springframework.beans.factory.BeanFactoryAware
|
||||
* @see org.springframework.beans.factory.aot.BeanRegistrationAotProcessor
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ManagedTypesBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
|
||||
@@ -48,7 +46,7 @@ public class ManagedTypesBeanRegistrationAotProcessor implements BeanRegistratio
|
||||
@Nullable private String moduleIdentifier;
|
||||
|
||||
@Override
|
||||
public BeanRegistrationAotContribution processAheadOfTime(@NonNull RegisteredBean registeredBean) {
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
|
||||
if (!isMatch(registeredBean.getBeanClass(), registeredBean.getBeanName())) {
|
||||
return null;
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationCode;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.data.domain.ManagedTypes;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link BeanRegistrationAotContribution} used to contribute a {@link ManagedTypes} registration.
|
||||
@@ -38,8 +38,8 @@ public class ManagedTypesRegistrationAotContribution implements BeanRegistration
|
||||
private final ManagedTypes managedTypes;
|
||||
private final BiConsumer<ResolvableType, GenerationContext> contributionAction;
|
||||
|
||||
public ManagedTypesRegistrationAotContribution(AotContext aotContext, ManagedTypes managedTypes,
|
||||
@NonNull BiConsumer<ResolvableType, GenerationContext> contributionAction) {
|
||||
public ManagedTypesRegistrationAotContribution(AotContext aotContext, @Nullable ManagedTypes managedTypes,
|
||||
BiConsumer<ResolvableType, GenerationContext> contributionAction) {
|
||||
|
||||
this.aotContext = aotContext;
|
||||
this.managedTypes = managedTypes;
|
||||
@@ -50,14 +50,12 @@ public class ManagedTypesRegistrationAotContribution implements BeanRegistration
|
||||
return this.aotContext;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected ManagedTypes getManagedTypes() {
|
||||
return managedTypes == null ? ManagedTypes.empty() : managedTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(@NonNull GenerationContext generationContext,
|
||||
@NonNull BeanRegistrationCode beanRegistrationCode) {
|
||||
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
|
||||
|
||||
List<Class<?>> types = getManagedTypes().toList();
|
||||
|
||||
|
||||
@@ -32,7 +32,11 @@ import java.util.function.Predicate;
|
||||
public abstract class Predicates {
|
||||
|
||||
public static final Predicate<Member> IS_ENUM_MEMBER = member -> member.getDeclaringClass().isEnum();
|
||||
public static final Predicate<Member> IS_HIBERNATE_MEMBER = member -> member.getName().startsWith("$$_hibernate");
|
||||
public static final Predicate<Member> IS_HIBERNATE_MEMBER = member -> member.getName().startsWith("$$_hibernate"); // this
|
||||
// should
|
||||
// go
|
||||
// into
|
||||
// JPA
|
||||
public static final Predicate<Member> IS_OBJECT_MEMBER = member -> Object.class.equals(member.getDeclaringClass());
|
||||
public static final Predicate<Member> IS_JAVA = member -> member.getDeclaringClass().getPackageName().startsWith("java.");
|
||||
public static final Predicate<Member> IS_NATIVE = member -> Modifier.isNative(member.getModifiers());
|
||||
|
||||
@@ -17,13 +17,14 @@ package org.springframework.data.aot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.data.repository.config.RepositoryFragmentConfiguration;
|
||||
import org.springframework.data.repository.config.RepositoryMetadata;
|
||||
import org.springframework.data.repository.config.RepositoryConfiguration;
|
||||
import org.springframework.data.repository.config.RepositoryFragmentConfigurationProvider;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
@@ -31,19 +32,15 @@ import org.springframework.data.util.Lazy;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Reader used to extract {@link RepositoryInformation} from {@link RepositoryMetadata}.
|
||||
* Reader used to extract {@link RepositoryInformation} from {@link RepositoryConfiguration}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.repository.config.RepositoryFragmentConfiguration
|
||||
* @see org.springframework.data.repository.config.RepositoryMetadata
|
||||
* @see org.springframework.data.repository.core.RepositoryInformation
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFragment
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class RepositoryBeanDefinitionReader {
|
||||
|
||||
static RepositoryInformation readRepositoryInformation(RepositoryMetadata<?> metadata,
|
||||
static RepositoryInformation readRepositoryInformation(RepositoryConfiguration<?> metadata,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
return new AotRepositoryInformation(metadataSupplier(metadata, beanFactory),
|
||||
@@ -51,33 +48,33 @@ class RepositoryBeanDefinitionReader {
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private static Supplier<Collection<RepositoryFragment<?>>> fragments(RepositoryMetadata metadata,
|
||||
private static Supplier<Collection<RepositoryFragment<?>>> fragments(RepositoryConfiguration<?> metadata,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
return Lazy.of(() -> (Collection<RepositoryFragment<?>>) metadata.getFragmentConfiguration().stream()
|
||||
.flatMap(it -> {
|
||||
if (metadata instanceof RepositoryFragmentConfigurationProvider provider) {
|
||||
|
||||
RepositoryFragmentConfiguration fragmentConfiguration = (RepositoryFragmentConfiguration) it;
|
||||
List<RepositoryFragment> fragments = new ArrayList<>(2);
|
||||
return Lazy.of(() -> {
|
||||
return provider.getFragmentConfiguration().stream().flatMap(it -> {
|
||||
|
||||
if (fragmentConfiguration.getClassName() != null) {
|
||||
fragments.add(RepositoryFragment.implemented(forName(fragmentConfiguration.getClassName(), beanFactory)));
|
||||
}
|
||||
if (fragmentConfiguration.getInterfaceName() != null) {
|
||||
fragments.add(RepositoryFragment.structural(forName(fragmentConfiguration.getInterfaceName(), beanFactory)));
|
||||
}
|
||||
List<RepositoryFragment<?>> fragments = new ArrayList<>(1);
|
||||
|
||||
return fragments.stream();
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
// TODO: Implemented accepts an Object, not a class.
|
||||
fragments.add(RepositoryFragment.implemented(forName(it.getClassName(), beanFactory)));
|
||||
fragments.add(RepositoryFragment.structural(forName(it.getInterfaceName(), beanFactory)));
|
||||
|
||||
return fragments.stream();
|
||||
}).collect(Collectors.toList());
|
||||
});
|
||||
}
|
||||
|
||||
return Lazy.of(Collections::emptyList);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private static Supplier<Class<?>> repositoryBaseClass(RepositoryMetadata metadata,
|
||||
private static Supplier<Class<?>> repositoryBaseClass(RepositoryConfiguration metadata,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
return Lazy.of(() ->
|
||||
(Class<?>) metadata.getRepositoryBaseClassName().map(it -> forName(it.toString(), beanFactory))
|
||||
return Lazy.of(() -> (Class<?>) metadata.getRepositoryBaseClassName().map(it -> forName(it.toString(), beanFactory))
|
||||
.orElseGet(() -> {
|
||||
// TODO: retrieve the default without loading the actual RepositoryBeanFactory
|
||||
return Object.class;
|
||||
@@ -85,7 +82,7 @@ class RepositoryBeanDefinitionReader {
|
||||
}
|
||||
|
||||
static Supplier<org.springframework.data.repository.core.RepositoryMetadata> metadataSupplier(
|
||||
RepositoryMetadata<?> metadata, ConfigurableListableBeanFactory beanFactory) {
|
||||
RepositoryConfiguration<?> metadata, ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
return Lazy.of(() -> new DefaultRepositoryMetadata(forName(metadata.getRepositoryInterface(), beanFactory)));
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -27,7 +26,6 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.aop.SpringProxy;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
@@ -47,12 +45,11 @@ import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.data.projection.EntityProjectionIntrospector;
|
||||
import org.springframework.data.projection.TargetAware;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.config.RepositoryConfiguration;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
import org.springframework.data.repository.config.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -62,29 +59,24 @@ import org.springframework.util.ClassUtils;
|
||||
* {@link BeanRegistrationAotContribution} used to contribute repository registrations.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.aot.generate.GenerationContext
|
||||
* @see org.springframework.beans.factory.aot.BeanRegistrationAotContribution
|
||||
* @see org.springframework.beans.factory.support.RegisteredBean
|
||||
* @see org.springframework.data.aot.RepositoryRegistrationAotProcessor
|
||||
* @since 3.0.0
|
||||
* @since 3.0
|
||||
*/
|
||||
public class RepositoryRegistrationAotContribution implements BeanRegistrationAotContribution {
|
||||
|
||||
private static final String KOTLIN_COROUTINE_REPOSITORY_TYPE_NAME =
|
||||
"org.springframework.data.repository.kotlin.CoroutineCrudRepository";
|
||||
private static final String KOTLIN_COROUTINE_REPOSITORY_TYPE_NAME = "org.springframework.data.repository.kotlin.CoroutineCrudRepository";
|
||||
|
||||
/**
|
||||
* Factory method used to construct a new instance of {@link RepositoryRegistrationAotContribution} initialized with
|
||||
* the given, required {@link RepositoryRegistrationAotProcessor} from which this contribution was created.
|
||||
*
|
||||
* @param repositoryRegistrationAotProcessor reference back to the {@link RepositoryRegistrationAotProcessor} from which this
|
||||
* contribution was created.
|
||||
* @param repositoryRegistrationAotProcessor reference back to the {@link RepositoryRegistrationAotProcessor} from
|
||||
* which this contribution was created.
|
||||
* @return a new instance of {@link RepositoryRegistrationAotContribution}.
|
||||
* @throws IllegalArgumentException if the {@link RepositoryRegistrationAotProcessor} is {@literal null}.
|
||||
* @see org.springframework.data.aot.RepositoryRegistrationAotProcessor
|
||||
*/
|
||||
public static RepositoryRegistrationAotContribution fromProcessor(
|
||||
@NonNull RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor) {
|
||||
RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor) {
|
||||
|
||||
return new RepositoryRegistrationAotContribution(repositoryRegistrationAotProcessor);
|
||||
}
|
||||
@@ -93,28 +85,25 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
|
||||
private BiConsumer<AotRepositoryContext, GenerationContext> moduleContribution;
|
||||
|
||||
@NonNull
|
||||
private final RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link RepositoryRegistrationAotContribution} initialized with the given,
|
||||
* required {@link RepositoryRegistrationAotProcessor} from which this contribution was created.
|
||||
* Constructs a new instance of the {@link RepositoryRegistrationAotContribution} initialized with the given, required
|
||||
* {@link RepositoryRegistrationAotProcessor} from which this contribution was created.
|
||||
*
|
||||
* @param repositoryRegistrationAotProcessor reference back to the {@link RepositoryRegistrationAotProcessor} from which this
|
||||
* contribution was created.
|
||||
* @param repositoryRegistrationAotProcessor reference back to the {@link RepositoryRegistrationAotProcessor} from
|
||||
* which this contribution was created.
|
||||
* @throws IllegalArgumentException if the {@link RepositoryRegistrationAotProcessor} is {@literal null}.
|
||||
* @see org.springframework.data.aot.RepositoryRegistrationAotProcessor
|
||||
*/
|
||||
protected RepositoryRegistrationAotContribution(
|
||||
@NonNull RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor) {
|
||||
RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor) {
|
||||
|
||||
Assert.notNull(repositoryRegistrationAotProcessor,
|
||||
"RepositoryRegistrationAotProcessor must not be null");
|
||||
Assert.notNull(repositoryRegistrationAotProcessor, "RepositoryRegistrationAotProcessor must not be null");
|
||||
|
||||
this.repositoryRegistrationAotProcessor = repositoryRegistrationAotProcessor;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return getRepositoryRegistrationAotProcessor().getBeanFactory();
|
||||
}
|
||||
@@ -123,16 +112,14 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
return Optional.ofNullable(this.moduleContribution);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected AotRepositoryContext getRepositoryContext() {
|
||||
|
||||
Assert.state(this.repositoryContext != null,
|
||||
"The AOT RepositoryContext was not properly initialized; did you call the forBean(:RegisteredBean) method");
|
||||
"The AOT RepositoryContext was not properly initialized; did you call the forBean(:RegisteredBean) method");
|
||||
|
||||
return this.repositoryContext;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected RepositoryRegistrationAotProcessor getRepositoryRegistrationAotProcessor() {
|
||||
return this.repositoryRegistrationAotProcessor;
|
||||
}
|
||||
@@ -146,20 +133,21 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link RepositoryRegistrationAotContribution} for given, required {@link RegisteredBean}
|
||||
* representing the {@link Repository} registered in the bean registry.
|
||||
* Builds a {@link RepositoryRegistrationAotContribution} for given, required {@link RegisteredBean} representing the
|
||||
* {@link Repository} registered in the bean registry.
|
||||
*
|
||||
* @param repositoryBean {@link RegisteredBean} for the {@link Repository}; must not be {@literal null}.
|
||||
* @return a {@link RepositoryRegistrationAotContribution} to contribute AOT metadata and code
|
||||
* for the {@link Repository} {@link RegisteredBean}.
|
||||
* @return a {@link RepositoryRegistrationAotContribution} to contribute AOT metadata and code for the
|
||||
* {@link Repository} {@link RegisteredBean}.
|
||||
* @throws IllegalArgumentException if the {@link RegisteredBean} is {@literal null}.
|
||||
* @see org.springframework.beans.factory.support.RegisteredBean
|
||||
*/
|
||||
public RepositoryRegistrationAotContribution forBean(@NonNull RegisteredBean repositoryBean) {
|
||||
public RepositoryRegistrationAotContribution forBean(RegisteredBean repositoryBean) {
|
||||
|
||||
Assert.notNull(repositoryBean, "The RegisteredBean for the repository must not be null");
|
||||
|
||||
RepositoryMetadata<?> repositoryMetadata = getRepositoryRegistrationAotProcessor().getRepositoryMetadata(repositoryBean);
|
||||
RepositoryConfiguration<?> repositoryMetadata = getRepositoryRegistrationAotProcessor()
|
||||
.getRepositoryMetadata(repositoryBean);
|
||||
|
||||
this.repositoryContext = buildAotRepositoryContext(repositoryBean, repositoryMetadata);
|
||||
|
||||
@@ -168,14 +156,14 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
return this;
|
||||
}
|
||||
|
||||
protected DefaultAotRepositoryContext buildAotRepositoryContext(@NonNull RegisteredBean bean,
|
||||
@NonNull RepositoryMetadata<?> repositoryMetadata) {
|
||||
protected DefaultAotRepositoryContext buildAotRepositoryContext(RegisteredBean bean,
|
||||
RepositoryConfiguration<?> repositoryMetadata) {
|
||||
|
||||
RepositoryInformation repositoryInformation = resolveRepositoryInformation(repositoryMetadata);
|
||||
|
||||
DefaultAotRepositoryContext repositoryContext = new DefaultAotRepositoryContext();
|
||||
DefaultAotRepositoryContext repositoryContext = new DefaultAotRepositoryContext(
|
||||
AotContext.from(this.getBeanFactory()));
|
||||
|
||||
repositoryContext.setAotContext(this::getBeanFactory);
|
||||
repositoryContext.setBeanName(bean.getBeanName());
|
||||
repositoryContext.setBasePackages(repositoryMetadata.getBasePackages().toSet());
|
||||
repositoryContext.setIdentifyingAnnotations(resolveIdentifyingAnnotations());
|
||||
@@ -190,17 +178,17 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
|
||||
try {
|
||||
// TODO: Getting all beans of type RepositoryConfigurationExtensionSupport will have the effect that
|
||||
// if the user is currently operating in multi-store mode, then all identifying annotations from
|
||||
// all stores will be included in the resulting Set.
|
||||
// When using AOT, is multi-store mode allowed? I don't see why not, but does this work correctly
|
||||
// with AOT, ATM?
|
||||
Map<String, RepositoryConfigurationExtensionSupport> repositoryConfigurationExtensionBeans =
|
||||
getBeanFactory().getBeansOfType(RepositoryConfigurationExtensionSupport.class);
|
||||
// if the user is currently operating in multi-store mode, then all identifying annotations from
|
||||
// all stores will be included in the resulting Set.
|
||||
// When using AOT, is multi-store mode allowed? I don't see why not, but does this work correctly
|
||||
// with AOT, ATM?
|
||||
Map<String, RepositoryConfigurationExtensionSupport> repositoryConfigurationExtensionBeans = getBeanFactory()
|
||||
.getBeansOfType(RepositoryConfigurationExtensionSupport.class);
|
||||
|
||||
// repositoryConfigurationExtensionBeans.values().stream()
|
||||
// .map(RepositoryConfigurationExtensionSupport::getIdentifyingAnnotations)
|
||||
// .flatMap(Collection::stream)
|
||||
// .collect(Collectors.toCollection(() -> identifyingAnnotations));
|
||||
// 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,
|
||||
@@ -210,26 +198,25 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
return identifyingAnnotations;
|
||||
}
|
||||
|
||||
private RepositoryInformation resolveRepositoryInformation(RepositoryMetadata<?> repositoryMetadata) {
|
||||
private RepositoryInformation resolveRepositoryInformation(RepositoryConfiguration<?> repositoryMetadata) {
|
||||
return RepositoryBeanDefinitionReader.readRepositoryInformation(repositoryMetadata, getBeanFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helps the AOT processing render the {@link FactoryBean} type correctly that is used to tell the outcome of the {@link FactoryBean}.
|
||||
*
|
||||
* We just need to set the target {@link Repository} {@link Class type} of the {@link RepositoryFactoryBeanSupport}
|
||||
* while keeping the actual ID and DomainType set to {@link Object}. If the generic type signature does not match,
|
||||
* then we do not try to resolve and remap the types, but rather set the {@literal factoryBeanObjectType} attribute
|
||||
* on the {@link RootBeanDefinition}.
|
||||
* Helps the AOT processing render the {@link FactoryBean} type correctly that is used to tell the outcome of the
|
||||
* {@link FactoryBean}. We just need to set the target {@link Repository} {@link Class type} of the
|
||||
* {@link RepositoryFactoryBeanSupport} while keeping the actual ID and DomainType set to {@link Object}. If the
|
||||
* generic type signature does not match, then we do not try to resolve and remap the types, but rather set the
|
||||
* {@literal factoryBeanObjectType} attribute on the {@link RootBeanDefinition}.
|
||||
*/
|
||||
protected void enhanceRepositoryBeanDefinition(@NonNull RegisteredBean repositoryBean,
|
||||
@NonNull RepositoryMetadata<?> repositoryMetadata, @NonNull AotRepositoryContext repositoryContext) {
|
||||
protected void enhanceRepositoryBeanDefinition(RegisteredBean repositoryBean,
|
||||
RepositoryConfiguration<?> repositoryMetadata, AotRepositoryContext repositoryContext) {
|
||||
|
||||
logTrace(String.format("Enhancing repository factory bean definition [%s]", repositoryBean.getBeanName()));
|
||||
|
||||
Class<?> repositoryFactoryBeanType = repositoryContext
|
||||
.resolveType(repositoryMetadata.getRepositoryFactoryBeanClassName())
|
||||
.orElse(RepositoryFactoryBeanSupport.class);
|
||||
.introspectType(repositoryMetadata.getRepositoryFactoryBeanClassName()).resolveType()
|
||||
.orElse(RepositoryFactoryBeanSupport.class);
|
||||
|
||||
ResolvableType resolvedRepositoryFactoryBeanType = ResolvableType.forClass(repositoryFactoryBeanType);
|
||||
|
||||
@@ -237,16 +224,16 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
|
||||
if (isRepositoryWithTypeParameters(resolvedRepositoryFactoryBeanType)) {
|
||||
repositoryBeanDefinition.setTargetType(ResolvableType.forClassWithGenerics(repositoryFactoryBeanType,
|
||||
repositoryContext.getRepositoryInformation().getRepositoryInterface(), Object.class, Object.class));
|
||||
repositoryContext.getRepositoryInformation().getRepositoryInterface(), Object.class, Object.class));
|
||||
} else {
|
||||
repositoryBeanDefinition.setTargetType(resolvedRepositoryFactoryBeanType);
|
||||
repositoryBeanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
|
||||
repositoryContext.getRepositoryInformation().getRepositoryInterface());
|
||||
repositoryContext.getRepositoryInformation().getRepositoryInterface());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRepositoryWithTypeParameters(ResolvableType type) {
|
||||
return type != null && type.getGenerics().length == 3;
|
||||
return type.getGenerics().length == 3;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +242,7 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
* @param moduleContribution {@link BiConsumer} used by data modules to submit contributions; can be {@literal null}.
|
||||
* @return this.
|
||||
*/
|
||||
@NonNull
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public RepositoryRegistrationAotContribution withModuleContribution(
|
||||
@Nullable BiConsumer<AotRepositoryContext, GenerationContext> moduleContribution) {
|
||||
@@ -264,29 +251,27 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(@NonNull GenerationContext generationContext,
|
||||
@NonNull BeanRegistrationCode beanRegistrationCode) {
|
||||
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
|
||||
|
||||
contributeRepositoryInfo(this.repositoryContext, generationContext);
|
||||
getModuleContribution().ifPresent(it -> it.accept(getRepositoryContext(), generationContext));
|
||||
}
|
||||
|
||||
private void contributeRepositoryInfo(@NonNull AotRepositoryContext repositoryContext,
|
||||
@NonNull GenerationContext contribution) {
|
||||
private void contributeRepositoryInfo(AotRepositoryContext repositoryContext, GenerationContext contribution) {
|
||||
|
||||
RepositoryInformation repositoryInformation = getRepositoryInformation();
|
||||
|
||||
logTrace("Contributing repository information for [%s]",
|
||||
repositoryInformation.getRepositoryInterface());
|
||||
logTrace("Contributing repository information for [%s]", repositoryInformation.getRepositoryInterface());
|
||||
|
||||
// TODO: is this the way?
|
||||
contribution.getRuntimeHints().reflection()
|
||||
.registerType(repositoryInformation.getRepositoryInterface(), hint ->
|
||||
hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.registerType(repositoryInformation.getRepositoryBaseClass(), hint ->
|
||||
hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.registerType(repositoryInformation.getDomainType(), hint ->
|
||||
hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS));
|
||||
.registerType(repositoryInformation.getRepositoryInterface(),
|
||||
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.registerType(repositoryInformation.getRepositoryBaseClass(),
|
||||
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.registerType(repositoryInformation.getDomainType(),
|
||||
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS));
|
||||
|
||||
// Repository Fragments
|
||||
for (RepositoryFragment<?> fragment : getRepositoryInformation().getFragments()) {
|
||||
@@ -308,26 +293,26 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
SpringProxy.class, Advised.class, DecoratingProxy.class);
|
||||
|
||||
// Transactional Repository Proxy
|
||||
//repositoryContext.ifTransactionManagerPresent(transactionManagerBeanNames -> {
|
||||
// repositoryContext.ifTransactionManagerPresent(transactionManagerBeanNames -> {
|
||||
|
||||
// TODO: Is the following double JDK Proxy registration above necessary or would a single JDK Proxy
|
||||
// registration suffice?
|
||||
// In other words, simply having a single JDK Proxy registration either with or without
|
||||
// the additional Serializable TypeReference?
|
||||
// NOTE: Using a single JDK Proxy registration causes the
|
||||
// simpleRepositoryWithTxManagerNoKotlinNoReactiveButComponent() test case method to fail.
|
||||
List<TypeReference> transactionalRepositoryProxyTypeReferences =
|
||||
transactionalRepositoryProxyTypeReferences(repositoryInformation);
|
||||
// TODO: Is the following double JDK Proxy registration above necessary or would a single JDK Proxy
|
||||
// registration suffice?
|
||||
// In other words, simply having a single JDK Proxy registration either with or without
|
||||
// the additional Serializable TypeReference?
|
||||
// NOTE: Using a single JDK Proxy registration causes the
|
||||
// simpleRepositoryWithTxManagerNoKotlinNoReactiveButComponent() test case method to fail.
|
||||
List<TypeReference> transactionalRepositoryProxyTypeReferences = transactionalRepositoryProxyTypeReferences(
|
||||
repositoryInformation);
|
||||
|
||||
contribution.getRuntimeHints().proxies()
|
||||
.registerJdkProxy(transactionalRepositoryProxyTypeReferences.toArray(new TypeReference[0]));
|
||||
|
||||
if (isComponentAnnotatedRepository(repositoryInformation)) {
|
||||
transactionalRepositoryProxyTypeReferences.add(TypeReference.of(Serializable.class));
|
||||
contribution.getRuntimeHints().proxies()
|
||||
.registerJdkProxy(transactionalRepositoryProxyTypeReferences.toArray(new TypeReference[0]));
|
||||
|
||||
if (isComponentAnnotatedRepository(repositoryInformation)) {
|
||||
transactionalRepositoryProxyTypeReferences.add(TypeReference.of(Serializable.class));
|
||||
contribution.getRuntimeHints().proxies()
|
||||
.registerJdkProxy(transactionalRepositoryProxyTypeReferences.toArray(new TypeReference[0]));
|
||||
}
|
||||
//});
|
||||
}
|
||||
// });
|
||||
|
||||
// Reactive Repositories
|
||||
if (repositoryInformation.isReactiveRepository()) {
|
||||
@@ -342,12 +327,10 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
}
|
||||
|
||||
// Repository query methods
|
||||
repositoryInformation.getQueryMethods()
|
||||
.map(repositoryInformation::getReturnedDomainClass)
|
||||
.filter(Class::isInterface)
|
||||
.forEach(type -> {
|
||||
if (EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy()
|
||||
.test(type, repositoryInformation.getDomainType())) {
|
||||
repositoryInformation.getQueryMethods().map(repositoryInformation::getReturnedDomainClass)
|
||||
.filter(Class::isInterface).forEach(type -> {
|
||||
if (EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy().test(type,
|
||||
repositoryInformation.getDomainType())) {
|
||||
contributeProjection(type, contribution);
|
||||
}
|
||||
});
|
||||
@@ -360,58 +343,52 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
private boolean isKotlinCoroutineRepository(AotRepositoryContext repositoryContext,
|
||||
RepositoryInformation repositoryInformation) {
|
||||
|
||||
return repositoryContext.resolveType(KOTLIN_COROUTINE_REPOSITORY_TYPE_NAME)
|
||||
.filter(it -> ClassUtils.isAssignable(it, repositoryInformation.getRepositoryInterface()))
|
||||
.isPresent();
|
||||
return repositoryContext.introspectType(KOTLIN_COROUTINE_REPOSITORY_TYPE_NAME).resolveType()
|
||||
.filter(it -> ClassUtils.isAssignable(it, repositoryInformation.getRepositoryInterface())).isPresent();
|
||||
}
|
||||
|
||||
private List<TypeReference> kotlinRepositoryReflectionTypeReferences() {
|
||||
|
||||
return new ArrayList<>(Arrays.asList(
|
||||
TypeReference.of("org.springframework.data.repository.kotlin.CoroutineCrudRepository"),
|
||||
TypeReference.of(Repository.class),
|
||||
TypeReference.of(Iterable.class),
|
||||
TypeReference.of("kotlinx.coroutines.flow.Flow"),
|
||||
TypeReference.of("kotlin.collections.Iterable"),
|
||||
TypeReference.of("kotlin.Unit"),
|
||||
TypeReference.of("kotlin.Long"),
|
||||
TypeReference.of("kotlin.Boolean")
|
||||
));
|
||||
return new ArrayList<>(
|
||||
Arrays.asList(TypeReference.of("org.springframework.data.repository.kotlin.CoroutineCrudRepository"),
|
||||
TypeReference.of(Repository.class), //
|
||||
TypeReference.of(Iterable.class), //
|
||||
TypeReference.of("kotlinx.coroutines.flow.Flow"), //
|
||||
TypeReference.of("kotlin.collections.Iterable"), //
|
||||
TypeReference.of("kotlin.Unit"), //
|
||||
TypeReference.of("kotlin.Long"), //
|
||||
TypeReference.of("kotlin.Boolean")));
|
||||
}
|
||||
|
||||
private List<TypeReference> transactionalRepositoryProxyTypeReferences(RepositoryInformation repositoryInformation) {
|
||||
|
||||
return new ArrayList<>(Arrays.asList(
|
||||
TypeReference.of(repositoryInformation.getRepositoryInterface()),
|
||||
TypeReference.of(Repository.class),
|
||||
TypeReference.of("org.springframework.transaction.interceptor.TransactionalProxy"),
|
||||
TypeReference.of("org.springframework.aop.framework.Advised"),
|
||||
TypeReference.of(DecoratingProxy.class)
|
||||
));
|
||||
return new ArrayList<>(Arrays.asList(TypeReference.of(repositoryInformation.getRepositoryInterface()),
|
||||
TypeReference.of(Repository.class), //
|
||||
TypeReference.of("org.springframework.transaction.interceptor.TransactionalProxy"), //
|
||||
TypeReference.of("org.springframework.aop.framework.Advised"), //
|
||||
TypeReference.of(DecoratingProxy.class)));
|
||||
}
|
||||
|
||||
private void contributeProjection(Class<?> type, GenerationContext generationContext) {
|
||||
|
||||
generationContext.getRuntimeHints().proxies()
|
||||
.registerJdkProxy(type, TargetAware.class, SpringProxy.class, DecoratingProxy.class);
|
||||
generationContext.getRuntimeHints().proxies().registerJdkProxy(type, TargetAware.class, SpringProxy.class,
|
||||
DecoratingProxy.class);
|
||||
}
|
||||
|
||||
static boolean isJavaOrPrimitiveType(Class<?> type) {
|
||||
|
||||
return TypeUtils.type(type).isPartOf("java")
|
||||
|| type.isPrimitive()
|
||||
|| ClassUtils.isPrimitiveArray(type);
|
||||
return TypeUtils.type(type).isPartOf("java") //
|
||||
|| ClassUtils.isPrimitiveOrWrapper(type) //
|
||||
|| ClassUtils.isPrimitiveArray(type); //
|
||||
}
|
||||
|
||||
static boolean isSpringDataManagedAnnotation(MergedAnnotation<?> annotation) {
|
||||
static boolean isSpringDataManagedAnnotation(@Nullable MergedAnnotation<?> annotation) {
|
||||
|
||||
return annotation != null
|
||||
&& (isInSpringDataNamespace(annotation.getType()) || annotation.getMetaTypes().stream()
|
||||
.anyMatch(RepositoryRegistrationAotContribution::isInSpringDataNamespace));
|
||||
return annotation != null && (isInSpringDataNamespace(annotation.getType())
|
||||
|| annotation.getMetaTypes().stream().anyMatch(RepositoryRegistrationAotContribution::isInSpringDataNamespace));
|
||||
}
|
||||
|
||||
static boolean isInSpringDataNamespace(Class<?> type) {
|
||||
return type != null && type.getPackage().getName().startsWith(TypeContributor.DATA_NAMESPACE);
|
||||
return type.getPackage().getName().startsWith(TypeContributor.DATA_NAMESPACE);
|
||||
}
|
||||
|
||||
static void contributeType(Class<?> type, GenerationContext generationContext) {
|
||||
@@ -419,8 +396,8 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo
|
||||
}
|
||||
|
||||
// TODO What was this meant to be used for? Was this type filter maybe meant to be used in
|
||||
// the TypeContributor.contribute(:Class, :Predicate :GenerationContext) method
|
||||
// used in the contributeType(..) method above?
|
||||
// the TypeContributor.contribute(:Class, :Predicate :GenerationContext) method
|
||||
// used in the contributeType(..) method above?
|
||||
public Predicate<Class<?>> typeFilter() { // like only document ones. // TODO: As in MongoDB?
|
||||
return it -> true;
|
||||
}
|
||||
|
||||
@@ -35,10 +35,9 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.data.repository.config.RepositoryConfiguration;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
import org.springframework.data.repository.config.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -46,24 +45,22 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* {@link BeanRegistrationAotProcessor} responsible processing and providing AOT configuration for repositories.
|
||||
* <p>
|
||||
* Processes {@link RepositoryFactoryBeanSupport repository factory beans} to provide generic type information to
|
||||
* the AOT tooling to allow deriving target type from the {@link RootBeanDefinition bean definition}. If generic types
|
||||
* do not match due to customization of the factory bean by the user, at least the target repository type is provided
|
||||
* via the {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE}.
|
||||
* Processes {@link RepositoryFactoryBeanSupport repository factory beans} to provide generic type information to the
|
||||
* AOT tooling to allow deriving target type from the {@link RootBeanDefinition bean definition}. If generic types do
|
||||
* not match due to customization of the factory bean by the user, at least the target repository type is provided via
|
||||
* the {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE}.
|
||||
* </p>
|
||||
* <p>
|
||||
* With {@link RepositoryRegistrationAotProcessor#contribute(AotRepositoryContext, GenerationContext)}, stores
|
||||
* can provide custom logic for contributing additional (eg. reflection) configuration. By default, reflection
|
||||
* configuration will be added for types reachable from the repository declaration and query methods as well as
|
||||
* all used {@link Annotation annotations} from the {@literal org.springframework.data} namespace.
|
||||
* With {@link RepositoryRegistrationAotProcessor#contribute(AotRepositoryContext, GenerationContext)}, stores can
|
||||
* provide custom logic for contributing additional (eg. reflection) configuration. By default, reflection configuration
|
||||
* will be added for types reachable from the repository declaration and query methods as well as all used
|
||||
* {@link Annotation annotations} from the {@literal org.springframework.data} namespace.
|
||||
* </p>
|
||||
* The processor is typically configured via {@link RepositoryConfigurationExtension#getRepositoryAotProcessor()}
|
||||
* and gets added by the {@link org.springframework.data.repository.config.RepositoryConfigurationDelegate}.
|
||||
* The processor is typically configured via {@link RepositoryConfigurationExtension#getRepositoryAotProcessor()} and
|
||||
* gets added by the {@link org.springframework.data.repository.config.RepositoryConfigurationDelegate}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author John Blum
|
||||
* @see org.springframework.beans.factory.BeanFactoryAware
|
||||
* @see org.springframework.beans.factory.aot.BeanRegistrationAotProcessor
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@@ -73,45 +70,41 @@ public class RepositoryRegistrationAotProcessor implements BeanRegistrationAotPr
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Map<String, RepositoryMetadata<?>> configMap;
|
||||
private Map<String, RepositoryConfiguration<?>> configMap;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BeanRegistrationAotContribution processAheadOfTime(@NonNull RegisteredBean bean) {
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean bean) {
|
||||
|
||||
return isRepositoryBean(bean)
|
||||
? newRepositoryRegistrationAotContribution(bean)
|
||||
: null;
|
||||
return isRepositoryBean(bean) ? newRepositoryRegistrationAotContribution(bean) : null;
|
||||
}
|
||||
|
||||
protected void contribute(AotRepositoryContext repositoryContext, GenerationContext generationContext) {
|
||||
|
||||
repositoryContext.getResolvedTypes().stream()
|
||||
.filter(it -> !RepositoryRegistrationAotContribution
|
||||
.isJavaOrPrimitiveType(it))
|
||||
.filter(it -> !RepositoryRegistrationAotContribution.isJavaOrPrimitiveType(it))
|
||||
.forEach(it -> RepositoryRegistrationAotContribution.contributeType(it, generationContext));
|
||||
|
||||
repositoryContext.getResolvedAnnotations().stream()
|
||||
.filter(RepositoryRegistrationAotContribution::isSpringDataManagedAnnotation)
|
||||
.map(MergedAnnotation::getType)
|
||||
.filter(RepositoryRegistrationAotContribution::isSpringDataManagedAnnotation).map(MergedAnnotation::getType)
|
||||
.forEach(it -> RepositoryRegistrationAotContribution.contributeType(it, generationContext));
|
||||
}
|
||||
|
||||
private boolean isRepositoryBean(RegisteredBean bean) {
|
||||
return bean != null && getConfigMap().containsKey(bean.getBeanName());
|
||||
return getConfigMap().containsKey(bean.getBeanName());
|
||||
}
|
||||
|
||||
protected RepositoryRegistrationAotContribution newRepositoryRegistrationAotContribution(
|
||||
@NonNull RegisteredBean repositoryBean) {
|
||||
RegisteredBean repositoryBean) {
|
||||
|
||||
RepositoryRegistrationAotContribution contribution =
|
||||
RepositoryRegistrationAotContribution.fromProcessor(this).forBean(repositoryBean);
|
||||
RepositoryRegistrationAotContribution contribution = RepositoryRegistrationAotContribution.fromProcessor(this)
|
||||
.forBean(repositoryBean);
|
||||
|
||||
return contribution.withModuleContribution(this::contribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
|
||||
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
|
||||
() -> "AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
|
||||
@@ -119,44 +112,40 @@ public class RepositoryRegistrationAotProcessor implements BeanRegistrationAotPr
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
public void setConfigMap(@Nullable Map<String, RepositoryMetadata<?>> configMap) {
|
||||
public void setConfigMap(@Nullable Map<String, RepositoryConfiguration<?>> configMap) {
|
||||
this.configMap = configMap;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Map<String, RepositoryMetadata<?>> getConfigMap() {
|
||||
public Map<String, RepositoryConfiguration<?>> getConfigMap() {
|
||||
return nullSafeMap(this.configMap);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private <K, V> Map<K, V> nullSafeMap(@Nullable Map<K, V> map) {
|
||||
return map != null ? map : Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected RepositoryMetadata<?> getRepositoryMetadata(@NonNull RegisteredBean bean) {
|
||||
protected RepositoryConfiguration<?> getRepositoryMetadata(RegisteredBean bean) {
|
||||
return getConfigMap().get(nullSafeBeanName(bean));
|
||||
}
|
||||
|
||||
private String nullSafeBeanName(@Nullable RegisteredBean bean) {
|
||||
private String nullSafeBeanName(RegisteredBean bean) {
|
||||
|
||||
String beanName = bean != null ? bean.getBeanName() : null;
|
||||
String beanName = bean.getBeanName();
|
||||
|
||||
return StringUtils.hasText(beanName) ? beanName : "";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected Log getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
private void logAt(Predicate<Log> logLevelPredicate, BiConsumer<Log, String> logOperation,
|
||||
String message, Object... arguments) {
|
||||
private void logAt(Predicate<Log> logLevelPredicate, BiConsumer<Log, String> logOperation, String message,
|
||||
Object... arguments) {
|
||||
|
||||
Log logger = getLogger();
|
||||
|
||||
|
||||
@@ -15,12 +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;
|
||||
@@ -29,7 +29,6 @@ import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueH
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
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;
|
||||
@@ -52,37 +51,36 @@ public class SpringDataBeanFactoryInitializationAotProcessor implements BeanFact
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(
|
||||
@NonNull ConfigurableListableBeanFactory beanFactory) {
|
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
processManagedTypes(beanFactory);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void processManagedTypes(@NonNull ConfigurableListableBeanFactory beanFactory) {
|
||||
private void processManagedTypes(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
if (beanFactory instanceof BeanDefinitionRegistry registry) {
|
||||
for (String beanName : beanFactory.getBeanNamesForType(ManagedTypes.class)) {
|
||||
|
||||
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
|
||||
|
||||
if(beanDefinition.getConstructorArgumentValues().isEmpty()) {
|
||||
if (beanDefinition.getConstructorArgumentValues().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ValueHolder argumentValue = beanDefinition.getConstructorArgumentValues().getArgumentValue(0, null, null, null);
|
||||
|
||||
if (argumentValue.getValue() instanceof Supplier supplier) {
|
||||
if (argumentValue.getValue()instanceof Supplier supplier) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.info(String.format("Replacing ManagedType bean definition %s.", beanName));
|
||||
}
|
||||
|
||||
Object value = supplier.get();
|
||||
if(ObjectUtils.isArray(value)) {
|
||||
if (ObjectUtils.isArray(value)) {
|
||||
value = CollectionUtils.arrayToList(value);
|
||||
}
|
||||
if(!(value instanceof Iterable<?>)) {
|
||||
if (!(value instanceof Iterable<?>)) {
|
||||
value = Collections.singleton(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -62,39 +62,39 @@ public class TypeCollector {
|
||||
|
||||
private final Predicate<Method> methodFilter = method -> {
|
||||
|
||||
Predicate<Method> excludedDomainsPredicate = methodToTest ->
|
||||
excludedDomainsFilter.test(methodToTest.getDeclaringClass());
|
||||
// TODO: Eagerly construct predicate objects to avoid object allocations during test(…)
|
||||
Predicate<Method> excludedDomainsPredicate = methodToTest -> excludedDomainsFilter
|
||||
.test(methodToTest.getDeclaringClass());
|
||||
|
||||
Predicate<Method> excludedMethodsPredicate = Predicates.IS_BRIDGE_METHOD
|
||||
.or(Predicates.IS_OBJECT_MEMBER)
|
||||
.or(Predicates.IS_HIBERNATE_MEMBER)
|
||||
.or(Predicates.IS_ENUM_MEMBER)
|
||||
.or(Predicates.IS_NATIVE)
|
||||
.or(Predicates.IS_PRIVATE)
|
||||
.or(Predicates.IS_PROTECTED)
|
||||
.or(Predicates.IS_SYNTHETIC)
|
||||
.or(excludedDomainsPredicate.negate());
|
||||
Predicate<Method> excludedMethodsPredicate = Predicates.IS_BRIDGE_METHOD //
|
||||
.or(Predicates.IS_OBJECT_MEMBER) //
|
||||
.or(Predicates.IS_HIBERNATE_MEMBER) //
|
||||
.or(Predicates.IS_ENUM_MEMBER) //
|
||||
.or(Predicates.IS_NATIVE) //
|
||||
.or(Predicates.IS_PRIVATE) //
|
||||
.or(Predicates.IS_PROTECTED) //
|
||||
.or(Predicates.IS_SYNTHETIC) //
|
||||
.or(excludedDomainsPredicate.negate()); //
|
||||
|
||||
return !excludedMethodsPredicate.test(method);
|
||||
};
|
||||
|
||||
private Predicate<Field> fieldFilter = field -> {
|
||||
|
||||
Predicate<Member> excludedFieldPredicate = Predicates.IS_HIBERNATE_MEMBER
|
||||
.or(Predicates.IS_SYNTHETIC)
|
||||
.or(Predicates.IS_JAVA);
|
||||
// TODO: Eagerly construct predicate objects to avoid object allocations during test(…)
|
||||
Predicate<Member> excludedFieldPredicate = Predicates.IS_HIBERNATE_MEMBER //
|
||||
.or(Predicates.IS_SYNTHETIC) //
|
||||
.or(Predicates.IS_JAVA);
|
||||
|
||||
return !excludedFieldPredicate.test(field);
|
||||
};
|
||||
|
||||
@NonNull
|
||||
public TypeCollector filterFields(@NonNull Predicate<Field> filter) {
|
||||
public TypeCollector filterFields(Predicate<Field> filter) {
|
||||
this.fieldFilter = filter.and(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public TypeCollector filterTypes(@NonNull Predicate<Class<?>> filter) {
|
||||
public TypeCollector filterTypes(Predicate<Class<?>> filter) {
|
||||
this.typeFilter = this.typeFilter.and(filter);
|
||||
return this;
|
||||
}
|
||||
@@ -105,12 +105,10 @@ public class TypeCollector {
|
||||
* @param types the types to inspect
|
||||
* @return a type model collector for the type
|
||||
*/
|
||||
@NonNull
|
||||
public static ReachableTypes inspect(Class<?>... types) {
|
||||
return inspect(Arrays.asList(types));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static ReachableTypes inspect(Collection<Class<?>> types) {
|
||||
return new ReachableTypes(new TypeCollector(), types);
|
||||
}
|
||||
@@ -137,9 +135,11 @@ public class TypeCollector {
|
||||
additionalTypes.addAll(visitConstructorsOfType(type));
|
||||
additionalTypes.addAll(visitMethodsOfType(type));
|
||||
additionalTypes.addAll(visitFieldsOfType(type));
|
||||
|
||||
if (!ObjectUtils.isEmpty(type.toClass().getDeclaredClasses())) {
|
||||
additionalTypes.addAll(Arrays.asList(type.toClass().getDeclaredClasses()));
|
||||
}
|
||||
|
||||
for (Type discoveredType : additionalTypes) {
|
||||
processType(ResolvableType.forType(discoveredType, type), cache, callback);
|
||||
}
|
||||
@@ -204,7 +204,7 @@ public class TypeCollector {
|
||||
private final Lazy<List<Class<?>>> reachableTypes = Lazy.of(this::collect);
|
||||
private final TypeCollector typeCollector;
|
||||
|
||||
public ReachableTypes(@NonNull TypeCollector typeCollector, @NonNull Iterable<Class<?>> roots) {
|
||||
public ReachableTypes(TypeCollector typeCollector, Iterable<Class<?>> roots) {
|
||||
|
||||
this.typeCollector = typeCollector;
|
||||
this.roots = roots;
|
||||
@@ -229,7 +229,7 @@ public class TypeCollector {
|
||||
|
||||
private final Map<String, ResolvableType> mutableCache = new LinkedHashMap<>();
|
||||
|
||||
public void add(@NonNull ResolvableType resolvableType) {
|
||||
public void add(ResolvableType resolvableType) {
|
||||
mutableCache.put(resolvableType.toString(), resolvableType);
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ public class TypeCollector {
|
||||
mutableCache.clear();
|
||||
}
|
||||
|
||||
public boolean contains(@NonNull ResolvableType key) {
|
||||
public boolean contains(ResolvableType key) {
|
||||
return mutableCache.containsKey(key.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -38,9 +38,12 @@ import org.springframework.util.ObjectUtils;
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
// TODO: Consider moving to the org.springframework.data.util package.
|
||||
// TODO: Consider moving to the org.springframework.data.util package or make this package-private if not used somewhere
|
||||
// else.
|
||||
public class TypeUtils {
|
||||
|
||||
public static String TRANSACTION_MANAGER_CLASS_NAME = "org.springframework.transaction.TransactionManager";
|
||||
|
||||
/**
|
||||
* Resolve ALL annotations present for a given type. Will inspect type, constructors, parameters, methods, fields,...
|
||||
*
|
||||
@@ -50,25 +53,25 @@ public class TypeUtils {
|
||||
public static Set<MergedAnnotation<Annotation>> resolveUsedAnnotations(Class<?> type) {
|
||||
|
||||
Set<MergedAnnotation<Annotation>> annotations = new LinkedHashSet<>();
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(type).collect(Collectors.toSet()));
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(type).toList());
|
||||
for (Constructor<?> ctor : type.getDeclaredConstructors()) {
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(ctor).collect(Collectors.toSet()));
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(ctor).toList());
|
||||
for (Parameter parameter : ctor.getParameters()) {
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(parameter).collect(Collectors.toSet()));
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(parameter).toList());
|
||||
}
|
||||
}
|
||||
for (Field field : type.getDeclaredFields()) {
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(field).collect(Collectors.toSet()));
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(field).toList());
|
||||
}
|
||||
try {
|
||||
for (Method method : type.getDeclaredMethods()) {
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(method).collect(Collectors.toSet()));
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(method).toList());
|
||||
for (Parameter parameter : method.getParameters()) {
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(parameter).collect(Collectors.toSet()));
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(parameter).toList());
|
||||
}
|
||||
}
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// ignore an move on
|
||||
// ignore and move on
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
@@ -83,13 +86,14 @@ public class TypeUtils {
|
||||
.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.standardRepeatables(), filter).stream();
|
||||
}
|
||||
|
||||
public static Set<Class<?>> resolveAnnotationTypesFor(AnnotatedElement element, AnnotationFilter filter) {
|
||||
public static Collection<Class<Annotation>> resolveAnnotationTypesFor(AnnotatedElement element,
|
||||
AnnotationFilter filter) {
|
||||
return MergedAnnotations
|
||||
.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.standardRepeatables(), filter).stream()
|
||||
.map(MergedAnnotation::getType).collect(Collectors.toSet());
|
||||
.map(MergedAnnotation::getType).toList();
|
||||
}
|
||||
|
||||
public static Set<Class<?>> resolveAnnotationTypesFor(AnnotatedElement element) {
|
||||
public static Collection<Class<Annotation>> resolveAnnotationTypesFor(AnnotatedElement element) {
|
||||
return resolveAnnotationTypesFor(element, AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
@@ -214,7 +218,7 @@ public class TypeUtils {
|
||||
|
||||
private static class TypeOpsImpl implements TypeOps {
|
||||
|
||||
private Class<?> type;
|
||||
private final Class<?> type;
|
||||
|
||||
TypeOpsImpl(Class<?> type) {
|
||||
this.type = type;
|
||||
|
||||
@@ -26,8 +26,13 @@ import org.springframework.data.domain.ReactiveAuditorAware;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Runtime hint registrars for Spring Data modules that provide auditing functionality.
|
||||
* <p>
|
||||
* Mainly for internal use within the framework.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
* @see RuntimeHintsRegistrar
|
||||
*/
|
||||
public class AuditingHints {
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Predefined Runtime Hints.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.data.aot.hint;
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Support for registering the need for reflection, resources, java serialization and proxies at runtime for Ahead of
|
||||
* Time compilation.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.data.aot;
|
||||
@@ -56,28 +56,41 @@ public abstract class AuditingBeanDefinitionRegistrarSupport implements ImportBe
|
||||
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null");
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
|
||||
|
||||
AbstractBeanDefinition ahbd = registerAuditHandlerBeanDefinition(registry, getConfiguration(annotationMetadata));
|
||||
AbstractBeanDefinition ahbd = registerAuditHandlerBeanDefinition(getConfiguration(annotationMetadata), registry);
|
||||
registerAuditListenerBeanDefinition(ahbd, registry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an appropriate BeanDefinition for an {@link AuditingHandler}.
|
||||
*
|
||||
* @param registry must not be {@literal null}.
|
||||
* @param configuration must not be {@literal null}.
|
||||
* @param registry must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private AbstractBeanDefinition registerAuditHandlerBeanDefinition(BeanDefinitionRegistry registry,
|
||||
AuditingConfiguration configuration) {
|
||||
protected AbstractBeanDefinition registerAuditHandlerBeanDefinition(AuditingConfiguration configuration,
|
||||
BeanDefinitionRegistry registry) {
|
||||
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
|
||||
Assert.notNull(configuration, "AuditingConfiguration must not be null");
|
||||
|
||||
AbstractBeanDefinition ahbd = getAuditHandlerBeanDefinitionBuilder(configuration).getBeanDefinition();
|
||||
BeanDefinitionBuilder builder = getAuditHandlerBeanDefinitionBuilder(configuration);
|
||||
postProcess(builder, configuration, registry);
|
||||
AbstractBeanDefinition ahbd = builder.getBeanDefinition();
|
||||
registry.registerBeanDefinition(getAuditingHandlerBeanName(), ahbd);
|
||||
return ahbd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook to post-process the AuditHandler BeanDefinition.
|
||||
*
|
||||
* @param builder must not be {@literal null}.
|
||||
* @param registry must not be {@literal null}.
|
||||
* @param configuration must not be {@literal null}.
|
||||
* @since 3.0
|
||||
*/
|
||||
protected void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration,
|
||||
BeanDefinitionRegistry registry) {}
|
||||
|
||||
/**
|
||||
* Creates a {@link BeanDefinitionBuilder} to ease the definition of store specific {@link AuditingHandler}
|
||||
* implementations.
|
||||
|
||||
@@ -17,8 +17,8 @@ package org.springframework.data.repository.config;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
@@ -27,6 +27,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
@@ -120,7 +121,8 @@ class RepositoryBeanDefinitionBuilder {
|
||||
extension.getDefaultNamedQueryLocation());
|
||||
configuration.getNamedQueriesLocation().ifPresent(definitionBuilder::setLocations);
|
||||
|
||||
String namedQueriesBeanName = BeanDefinitionReaderUtils.uniqueBeanName(extension.getModuleIdentifier() + ".named-queries", registry);
|
||||
String namedQueriesBeanName = BeanDefinitionReaderUtils
|
||||
.uniqueBeanName(extension.getModuleIdentifier() + ".named-queries", registry);
|
||||
BeanDefinition namedQueries = definitionBuilder.build(configuration.getSource());
|
||||
registry.registerBeanDefinition(namedQueriesBeanName, namedQueries);
|
||||
|
||||
@@ -138,41 +140,43 @@ class RepositoryBeanDefinitionBuilder {
|
||||
}
|
||||
|
||||
// TODO: merge that with the one that creates the BD
|
||||
RepositoryMetadata buildMetadata(RepositoryConfiguration<?> configuration) {
|
||||
RepositoryConfigurationAdapter<?> buildMetadata(RepositoryConfiguration<?> configuration) {
|
||||
|
||||
ImplementationDetectionConfiguration config = configuration
|
||||
.toImplementationDetectionConfiguration(metadataReaderFactory);
|
||||
|
||||
List<RepositoryFragmentConfiguration> repositoryFragmentConfigurationStream = fragmentMetadata.getFragmentInterfaces(configuration.getRepositoryInterface()) //
|
||||
List<RepositoryFragmentConfiguration> repositoryFragmentConfigurationStream = fragmentMetadata
|
||||
.getFragmentInterfaces(configuration.getRepositoryInterface()) //
|
||||
.map(it -> detectRepositoryFragmentConfiguration(it, config)) //
|
||||
.flatMap(Optionals::toStream)
|
||||
.collect(Collectors.toList());//
|
||||
.flatMap(Optionals::toStream).toList();
|
||||
|
||||
if(repositoryFragmentConfigurationStream.isEmpty()) {
|
||||
if (repositoryFragmentConfigurationStream.isEmpty()) {
|
||||
|
||||
ImplementationLookupConfiguration lookup = configuration.toLookupConfiguration(metadataReaderFactory);
|
||||
Optional<AbstractBeanDefinition> beanDefinition = implementationDetector.detectCustomImplementation(lookup);
|
||||
|
||||
if(beanDefinition.isPresent()) {
|
||||
if (beanDefinition.isPresent()) {
|
||||
repositoryFragmentConfigurationStream = new ArrayList<>(1);
|
||||
|
||||
List<String> interfaceNames = fragmentMetadata.getFragmentInterfaces(configuration.getRepositoryInterface()).collect(Collectors.toList());
|
||||
List<String> interfaceNames = fragmentMetadata.getFragmentInterfaces(configuration.getRepositoryInterface())
|
||||
.toList();
|
||||
String implClassName = beanDefinition.get().getBeanClassName();
|
||||
|
||||
try {
|
||||
for (String iName : metadataReaderFactory.getMetadataReader(implClassName).getClassMetadata().getInterfaceNames()) {
|
||||
if(interfaceNames.contains(iName)) {
|
||||
for (String iName : metadataReaderFactory.getMetadataReader(implClassName).getClassMetadata()
|
||||
.getInterfaceNames()) {
|
||||
if (interfaceNames.contains(iName)) {
|
||||
repositoryFragmentConfigurationStream.add(new RepositoryFragmentConfiguration(iName, implClassName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new RepositoryMetadata(configuration, repositoryFragmentConfigurationStream);
|
||||
return new RepositoryConfigurationAdapter<>(configuration, repositoryFragmentConfigurationStream);
|
||||
}
|
||||
|
||||
private Optional<String> registerCustomImplementation(RepositoryConfiguration<?> configuration) {
|
||||
|
||||
@@ -27,12 +27,13 @@ import org.springframework.lang.Nullable;
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
public class RepositoryMetadata<T extends RepositoryConfigurationSource> implements RepositoryConfiguration<T> {
|
||||
class RepositoryConfigurationAdapter<T extends RepositoryConfigurationSource>
|
||||
implements RepositoryConfiguration<T>, RepositoryFragmentConfigurationProvider {
|
||||
|
||||
RepositoryConfiguration<T> repositoryConfiguration;
|
||||
List<RepositoryFragmentConfiguration> fragmentConfiguration;
|
||||
|
||||
public RepositoryMetadata(RepositoryConfiguration<T> repositoryConfiguration,
|
||||
public RepositoryConfigurationAdapter(RepositoryConfiguration<T> repositoryConfiguration,
|
||||
List<RepositoryFragmentConfiguration> fragmentConfiguration) {
|
||||
|
||||
this.repositoryConfiguration = repositoryConfiguration;
|
||||
@@ -116,6 +117,7 @@ public class RepositoryMetadata<T extends RepositoryConfigurationSource> impleme
|
||||
return repositoryConfiguration.getResourceDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RepositoryFragmentConfiguration> getFragmentConfiguration() {
|
||||
return fragmentConfiguration;
|
||||
}
|
||||
@@ -21,13 +21,11 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
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,7 +46,6 @@ 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.util.TypeScanner;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -71,8 +68,7 @@ public class RepositoryConfigurationDelegate {
|
||||
private static final String REPOSITORY_REGISTRATION = "Spring Data %s - Registering repository: %s - Interface: %s - Factory: %s";
|
||||
private static final String MULTIPLE_MODULES = "Multiple Spring Data modules found, entering strict repository configuration mode";
|
||||
private static final String NON_DEFAULT_AUTOWIRE_CANDIDATE_RESOLVER = "Non-default AutowireCandidateResolver (%s) detected. Skipping the registration of LazyRepositoryInjectionPointResolver. Lazy repository injection will not be working";
|
||||
|
||||
static final String FACTORY_BEAN_OBJECT_TYPE = FactoryBean.OBJECT_TYPE_ATTRIBUTE; // "factoryBeanObjectType";
|
||||
private static final String FACTORY_BEAN_OBJECT_TYPE = FactoryBean.OBJECT_TYPE_ATTRIBUTE;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RepositoryConfigurationDelegate.class);
|
||||
|
||||
@@ -169,7 +165,7 @@ public class RepositoryConfigurationDelegate {
|
||||
List<BeanComponentDefinition> definitions = new ArrayList<>();
|
||||
|
||||
Map<String, RepositoryConfiguration<?>> configurationsByRepositoryName = new HashMap<>(configurations.size());
|
||||
Map<String, RepositoryMetadata<?>> metadataByRepositoryBeanName = new HashMap<>(configurations.size());
|
||||
Map<String, RepositoryConfigurationAdapter<?>> metadataByRepositoryBeanName = new HashMap<>(configurations.size());
|
||||
|
||||
for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : configurations) {
|
||||
|
||||
@@ -223,7 +219,7 @@ public class RepositoryConfigurationDelegate {
|
||||
}
|
||||
|
||||
private void registerAotComponents(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension,
|
||||
Map<String, RepositoryMetadata<?>> metadataByRepositoryBeanName) {
|
||||
Map<String, RepositoryConfigurationAdapter<?>> metadataByRepositoryBeanName) {
|
||||
|
||||
// module-specific repository aot processor
|
||||
String repositoryAotProcessorBeanName = String.format("data-%s.repository-aot-processor" /* might be duplicate */,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.repository.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface exposing {@link RepositoryFragmentConfiguration}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface RepositoryFragmentConfigurationProvider {
|
||||
|
||||
/**
|
||||
* Returns the fragment configuration.
|
||||
*
|
||||
* @return the fragment configuration.
|
||||
*/
|
||||
List<RepositoryFragmentConfiguration> getFragmentConfiguration();
|
||||
}
|
||||
@@ -164,7 +164,6 @@ public abstract class RepositoryInformationSupport implements RepositoryInformat
|
||||
* @return
|
||||
*/
|
||||
protected boolean isQueryAnnotationPresentOn(Method method) {
|
||||
|
||||
return AnnotationUtils.findAnnotation(method, QueryAnnotation.class) != null;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,10 +129,8 @@ public interface RepositoryMetadata {
|
||||
boolean isReactiveRepository();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
Set<RepositoryFragment<?>> getFragments();
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryInformationSupport;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -102,13 +100,7 @@ class DefaultRepositoryInformation extends RepositoryInformationSupport implemen
|
||||
return baseComposition.getMethod(method) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public Set<RepositoryFragment<?>> getFragments() {
|
||||
return composition.getFragments().toSet();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user