Please wait. This can take some minutes ...
                    
                 
             
         
        
            
                
                    
                    
                        Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. 
                        
                        Project price only 1 $ 
                        
                            You can buy this project and download/modify it how often you want.
                        
                        
                        
                     
                 
             
         
             
    
    
    io.micronaut.inject.BeanDefinition Maven / Gradle / Ivy 
    
/*
 * Copyright 2017-2020 original 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 io.micronaut.inject;
import static io.micronaut.core.annotation.AnnotationUtil.ANN_ADAPTER;
import static io.micronaut.core.type.TypeInformation.TypeFormat.getBeanTypeString;
import io.micronaut.context.Qualifier;
import io.micronaut.context.annotation.ConfigurationReader;
import io.micronaut.context.annotation.DefaultScope;
import io.micronaut.context.annotation.EachBean;
import io.micronaut.context.annotation.EachProperty;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.naming.Named;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.type.ArgumentCoercible;
import io.micronaut.core.type.TypeInformation;
import io.micronaut.core.type.TypeInformation.TypeFormat;
import io.micronaut.core.util.AnsiColour;
import io.micronaut.inject.proxy.InterceptedBean;
import jakarta.inject.Singleton;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
/**
 * Defines a bean definition and its requirements. A bean definition must have a singled injectable constructor or a
 * no-args constructor.
 *
 * @param  The bean type
 * @author Graeme Rocher
 * @since 1.0
 */
public interface BeanDefinition extends QualifiedBeanType, Named, BeanType, ArgumentCoercible {
    /**
     * @return The type information for the bean.
     * @since 4.8.0
     */
    default @NonNull TypeInformation getTypeInformation() {
        return new TypeInformation<>() {
            @Override
            public String getBeanTypeString(TypeFormat format) {
                Class beanType = getType();
                boolean synthetic = beanType.isSynthetic();
                if (synthetic) {
                    AnnotationMetadata annotationMetadata = getAnnotationMetadata();
                    // synthetic bean so produce better formatting.
                    if (annotationMetadata.hasDeclaredStereotype(ANN_ADAPTER)) {
                        @SuppressWarnings("unchecked") ExecutableMethod method =
                            (ExecutableMethod) BeanDefinition.this.getExecutableMethods().iterator().next();
                        // Not great, but to produce accurate debug output we have to reach into AOP internals
                        Class> resolvedBeanType = method.classValue(ANN_ADAPTER, "adaptedBean")
                            .orElse(beanType);
                        return TypeFormat.getBeanTypeString(
                            format,
                            resolvedBeanType,
                            getGenericBeanType().getTypeVariables(),
                            annotationMetadata
                        );
                    } else if (InterceptedBean.class.isAssignableFrom(beanType)) {
                        if (beanType.isInterface()) {
                            return TypeFormat.getBeanTypeString(
                                format,
                                beanType.getInterfaces()[0],
                                getGenericBeanType().getTypeVariables(),
                                annotationMetadata
                            );
                        } else {
                            return TypeFormat.getBeanTypeString(
                                format,
                                beanType.getSuperclass(),
                                getGenericBeanType().getTypeVariables(),
                                annotationMetadata
                            );
                        }
                    } else {
                        return TypeInformation.super.getBeanTypeString(format);
                    }
                } else {
                    return TypeInformation.super.getBeanTypeString(format);
                }
            }
            @Override
            public Class getType() {
                return getBeanType();
            }
            @Override
            public Map> getTypeVariables() {
                return BeanDefinition.this.getGenericBeanType().getTypeVariables();
            }
            @Override
            public AnnotationMetadata getAnnotationMetadata() {
                return BeanDefinition.this.getAnnotationMetadata();
            }
        };
    }
    /**
     * @return The scope of the bean
     */
    default Optional> getScope() {
        return Optional.empty();
    }
    /**
     * @return The name of the scope
     */
    default Optional getScopeName() {
        return Optional.empty();
    }
    /**
     * @return Whether the scope is singleton
     */
    default boolean isSingleton() {
        final String scopeName = getScopeName().orElse(null);
        if (scopeName != null && scopeName.equals(AnnotationUtil.SINGLETON)) {
            return true;
        } else {
            return getAnnotationMetadata().stringValue(DefaultScope.class)
                    .map(t -> t.equals(Singleton.class.getName()) || t.equals(AnnotationUtil.SINGLETON))
                    .orElse(false);
        }
    }
    /**
     * If {@link #isContainerType()} returns true this will return the container element.
     * @return The container element.
     */
    default Optional> getContainerElement() {
        return Optional.empty();
    }
    @Override
    @SuppressWarnings("java:S3776")
    default boolean isCandidateBean(@Nullable Argument> beanType) {
        if (beanType == null) {
            return false;
        }
        if (QualifiedBeanType.super.isCandidateBean(beanType)) {
            final Argument>[] typeArguments = beanType.getTypeParameters();
            final int len = typeArguments.length;
            Class> beanClass = beanType.getType();
            if (len == 0) {
                if (isContainerType()) {
                    if (getBeanType().isAssignableFrom(beanClass)) {
                        return true;
                    }
                    final Optional> containerElement = getContainerElement();
                    if (containerElement.isPresent()) {
                        final Class> t = containerElement.get().getType();
                        return beanType.isAssignableFrom(t) || beanClass == t;
                    } else {
                        return false;
                    }
                } else {
                    return true;
                }
            } else {
                final Argument>[] beanTypeParameters;
                if (!Iterable.class.isAssignableFrom(beanClass)) {
                    final Optional> containerElement = getContainerElement();
                    //noinspection OptionalIsPresent
                    if (containerElement.isPresent()) {
                        beanTypeParameters = containerElement.get().getTypeParameters();
                    } else {
                        beanTypeParameters = getTypeArguments(beanClass).toArray(Argument.ZERO_ARGUMENTS);
                    }
                } else {
                    beanTypeParameters = getTypeArguments(beanClass).toArray(Argument.ZERO_ARGUMENTS);
                }
                if (len != beanTypeParameters.length) {
                    return false;
                }
                for (int i = 0; i < beanTypeParameters.length; i++) {
                    Argument> candidateParameter = beanTypeParameters[i];
                    final Argument> requestedParameter = typeArguments[i];
                    if (!requestedParameter.isAssignableFrom(candidateParameter.getType())) {
                        if (!(candidateParameter.isTypeVariable() && candidateParameter.isAssignableFrom(requestedParameter.getType()))) {
                            return false;
                        }
                    }
                }
                return true;
            }
        }
        return false;
    }
    /**
     * @return Whether the bean declared with {@link io.micronaut.context.annotation.EachProperty} or
     * {@link io.micronaut.context.annotation.EachBean}
     */
    default boolean isIterable() {
        return hasDeclaredStereotype(EachProperty.class) || hasDeclaredStereotype(EachBean.class);
    }
    /**
     * @return Is the type configuration properties.
     */
    default boolean isConfigurationProperties() {
        return isIterable() || hasDeclaredStereotype(ConfigurationReader.class);
    }
    /**
     * @return The produced bean type
     */
    @Override
    Class getBeanType();
    /**
     * @return The type that declares this definition, null if not applicable.
     */
    default Optional> getDeclaringType() {
        return Optional.empty();
    }
    /**
     * The single concrete constructor that is an injection point for creating the bean.
     *
     * @return The constructor injection point
     */
    default ConstructorInjectionPoint getConstructor() {
        return new ConstructorInjectionPoint<>() {
            @Override
            public Argument>[] getArguments() {
                return Argument.ZERO_ARGUMENTS;
            }
            @Override
            public BeanDefinition getDeclaringBean() {
                return BeanDefinition.this;
            }
            @Override
            public String toString() {
                return getDeclaringBeanType().getName() + "(" + Argument.toString(getArguments()) + ")";
            }
        };
    }
    /**
     * @return All required components for this entity definition
     */
    default Collection> getRequiredComponents() {
        return Collections.emptyList();
    }
    /**
     * All methods that require injection. This is a subset of all the methods in the class.
     *
     * @return The required properties
     */
    default Collection> getInjectedMethods() {
        return Collections.emptyList();
    }
    /**
     * All the fields that require injection.
     *
     * @return The required fields
     */
    default Collection> getInjectedFields() {
        return Collections.emptyList();
    }
    /**
     * All the methods that should be called once the bean has been fully initialized and constructed.
     *
     * @return Methods to call post construct
     */
    default Collection> getPostConstructMethods() {
        return Collections.emptyList();
    }
    /**
     * All the methods that should be called when the object is to be destroyed.
     *
     * @return Methods to call pre-destroy
     */
    default Collection> getPreDestroyMethods() {
        return Collections.emptyList();
    }
    /**
     * @return The class name
     */
    @Override
    @NonNull
    default String getName() {
        return getBeanType().getName();
    }
    /**
     * Finds a single {@link ExecutableMethod} for the given name and argument types.
     *
     * @param name          The method name
     * @param argumentTypes The argument types
     * @param            The return type
     * @return An optional {@link ExecutableMethod}
     */
    default  Optional> findMethod(String name, Class>... argumentTypes) {
        return Optional.empty();
    }
    /**
     * Whether an {@link ExecutableMethod} exists which is annotated with the supplied annotation
     *
     * @param methodName          The method name
     * @param argumentTypes The argument types
     * @return Whether an {@link ExecutableMethod} exists which is annotated with the supplied annotation
     * @since 4.3.0
     */
    default boolean hasAnnotatedMethod(@NonNull Class extends Annotation> annotationClass,
                                       @NonNull String methodName,
                                       @NonNull Class>... argumentTypes) {
        return findMethod(methodName, argumentTypes).map(method -> method.hasAnnotation(annotationClass)).orElse(false);
    }
    /**
     * Finds possible methods for the given method name.
     *
     * @param name The method name
     * @param   The return type
     * @return The possible methods
     */
    default  Stream> findPossibleMethods(String name) {
        return Stream.empty();
    }
    /**
     * @return The {@link ExecutableMethod} instances for this definition
     */
    default Collection> getExecutableMethods() {
        return Collections.emptyList();
    }
    @Override
    @NonNull
    default Argument asArgument() {
        return Argument.of(
                getBeanType(),
                getAnnotationMetadata(),
                getTypeParameters()
        );
    }
    /**
     * Whether this bean definition represents a proxy.
     *
     * @return True if it represents a proxy
     */
    default boolean isProxy() {
        return false;
    }
    /**
     * If the bean itself declares any type arguments this method will return the classes that represent those types.
     *
     * @return The type arguments
     */
    default @NonNull List> getTypeArguments() {
        return getTypeArguments(getBeanType());
    }
    /**
     * Return the type arguments for the given interface or super type for this bean.
     *
     * @param type The super class or interface type
     * @return The type arguments
     */
    default @NonNull List> getTypeArguments(Class> type) {
        if (type == null) {
            return Collections.emptyList();
        }
        return getTypeArguments(type.getName());
    }
    /**
     * Returns the type parameters as a class array for the given type.
     * @param type The type
     * @return The type parameters
     */
    default @NonNull Class>[] getTypeParameters(@Nullable Class> type) {
        if (type == null) {
            return ReflectionUtils.EMPTY_CLASS_ARRAY;
        } else {
            final List> typeArguments = getTypeArguments(type);
            if (typeArguments.isEmpty()) {
                return ReflectionUtils.EMPTY_CLASS_ARRAY;
            }
            Class>[] params = new Class>[typeArguments.size()];
            int i = 0;
            for (Argument> argument : typeArguments) {
                params[i++] = argument.getType();
            }
            return params;
        }
    }
    /**
     *
     * Returns the type parameters as a class array for the bean type.
     *
     * @return The type parameters for the bean type as a class array.
     */
    default @NonNull Class>[] getTypeParameters() {
        return getTypeParameters(getBeanType());
    }
    /**
     * Return the type arguments for the given interface or super type for this bean.
     *
     * @param type The super class or interface type
     * @return The type arguments
     */
    default @NonNull List> getTypeArguments(String type) {
        return Collections.emptyList();
    }
    /**
     * Finds a single {@link ExecutableMethod} for the given name and argument types.
     *
     * @param name          The method name
     * @param argumentTypes The argument types
     * @param            The return type
     * @return An optional {@link ExecutableMethod}
     * @throws IllegalStateException If the method cannot be found
     */
    @SuppressWarnings("unchecked")
    default  ExecutableMethod getRequiredMethod(String name, Class>... argumentTypes) {
        return (ExecutableMethod) findMethod(name, argumentTypes)
            .orElseThrow(() -> ReflectionUtils.newNoSuchMethodError(getBeanType(), name, argumentTypes));
    }
    /**
     * @return Whether the bean definition is abstract
     */
    default boolean isAbstract() {
        return Modifier.isAbstract(getBeanType().getModifiers());
    }
    @Override
    default Argument getGenericBeanType() {
        return asArgument();
    }
    /**
     * Resolve the declared qualifier for this bean.
     * @return The qualifier or null if this isn't one
     */
    @Override
    default @Nullable Qualifier getDeclaredQualifier() {
        return QualifiedBeanType.super.getDeclaredQualifier();
    }
    /**
     * @return Method that can be overridden to resolve a dynamic qualifier
     */
    @Override
    default @Nullable Qualifier resolveDynamicQualifier() {
        return QualifiedBeanType.super.resolveDynamicQualifier();
    }
    /**
     * Gets a description of the bean as close as possible to source representation.
     * @param typeFormat The type format to use.
     * @param includeArguments Whether to include arguments.
     * @return The bean description.
     * @since 4.8.0
     */
    default @NonNull String getBeanDescription(@NonNull TypeFormat typeFormat, boolean includeArguments) {
        ConstructorInjectionPoint constructor = getConstructor();
        StringBuilder beanDescription = new StringBuilder();
        Argument>[] arguments = constructor.getArguments();
        if (constructor instanceof MethodInjectionPoint,?> methodInjectionPoint) {
            // factory bean with method
            Class> declaringType = methodInjectionPoint.getDeclaringType();
            Class declaringBeanType = constructor.getDeclaringBeanType();
            String factoryType = TypeFormat.getTypeString(
                typeFormat,
                declaringBeanType,
                Map.of()
            );
            String beanTypeName = getBeanTypeString(
                typeFormat,
                declaringType,
                asArgument().getTypeVariables(),
                methodInjectionPoint.getAnnotationMetadata()
            );
            beanDescription.append(beanTypeName).append(" ");
            beanDescription.append(factoryType)
                .append(".")
                .append(methodInjectionPoint.getName());
        } else if (constructor instanceof FieldInjectionPoint,?> fieldInjectionPoint) {
            // factory bean with method
            Class declaringBeanType = constructor.getDeclaringBeanType();
            String factoryType = TypeFormat.getTypeString(
                typeFormat,
                declaringBeanType,
                Map.of()
            );
            Class> declaringType = fieldInjectionPoint.getDeclaringBean().getBeanType();
            String beanTypeName = getBeanTypeString(
                typeFormat,
                declaringType,
                asArgument().getTypeVariables(),
                fieldInjectionPoint.getAnnotationMetadata()
            );
            beanDescription.append(beanTypeName).append(" ");
            beanDescription.append(factoryType)
                .append(".")
                .append(fieldInjectionPoint.getName());
            return beanDescription.toString();
        } else {
            boolean synthetic = getBeanType().isSynthetic();
            if (synthetic) {
                // AOP proxy or generated event listener
                AnnotationMetadata annotationMetadata = getAnnotationMetadata();
                if (annotationMetadata.hasDeclaredStereotype(ANN_ADAPTER)) {
                    @SuppressWarnings("unchecked") ExecutableMethod method =
                        (ExecutableMethod) getExecutableMethods().iterator().next();
                    // Not great, but to produce accurate debug output we have to reach into AOP internals
                    Class> adaptedType = method.classValue(ANN_ADAPTER).orElse(getBeanType());
                    Class> beanType = method.classValue(ANN_ADAPTER, "adaptedBean").orElse(getBeanType());
                    String beanMethod = method.stringValue(ANN_ADAPTER, "adaptedMethod").orElse("unknown");
                    String beanTypeString = getBeanTypeString(
                        typeFormat,
                        beanType,
                        asArgument().getTypeVariables(),
                        annotationMetadata
                    );
                    beanDescription.append(beanTypeString)
                        .append(".")
                        .append(beanMethod);
                    @NonNull Argument>[] methodArguments = method.getArguments();
                    List> typeArguments = getTypeArguments(adaptedType);
                    if (typeArguments.size() == methodArguments.length) {
                        arguments = new Argument[methodArguments.length];
                        for (int i = 0; i < methodArguments.length; i++) {
                            @NonNull Argument> methodArgument = methodArguments[i];
                            Argument> t = typeArguments.get(i);
                            arguments[i] = Argument.of(
                                t.getType(),
                                methodArgument.getName(),
                                methodArgument.getAnnotationMetadata(),
                                t.getTypeParameters()
                            );
                        }
                    }
                } else {
                    Class beanType = getBeanType();
                    String beanTypeString;
                    if (beanType.isInterface()) {
                        beanTypeString = getBeanTypeString(
                            typeFormat,
                            beanType.getInterfaces()[0],
                            asArgument().getTypeVariables(),
                            annotationMetadata
                        );
                    } else {
                        beanTypeString = getBeanTypeString(
                            typeFormat,
                            beanType.getSuperclass(),
                            asArgument().getTypeVariables(),
                            annotationMetadata
                        );
                    }
                    beanDescription.append(beanTypeString);
                }
            } else {
                beanDescription.append(
                    getTypeInformation().getBeanTypeString(typeFormat)
                );
            }
        }
        if (includeArguments) {
            beanDescription.append(typeFormat.isAnsi() ? AnsiColour.brightCyan("(") : "(");
            for (int i = 0; i < arguments.length; i++) {
                Argument> argument = arguments[i];
                if (argument.getName().startsWith("$")) {
                    // skip internal
                    continue;
                }
                String argType = getBeanTypeString(
                    typeFormat,
                    argument
                );
                String argumentName = argument.getName();
                beanDescription.append(argType)
                    .append(" ")
                    .append(typeFormat.isAnsi() ? AnsiColour.brightBlue(argumentName) : argumentName);
                if (i != arguments.length - 1) {
                    Argument> next = arguments[i + 1];
                    if (getBeanType().isSynthetic() &&
                        next.getName().startsWith("$")) {
                        // skip synthetic arguments
                        break;
                    }
                    beanDescription.append(", ");
                }
            }
            beanDescription.append(typeFormat.isAnsi() ? AnsiColour.brightCyan(")") : ")");
        }
        return beanDescription.toString();
    }
    /**
     * Gets a description of the bean as close as possible to source representation.
     * @param typeFormat The type format to use.
     * @return The bean description.
     * @since 4.8.0
     */
    default @NonNull String getBeanDescription(@NonNull TypeFormat typeFormat) {
        return getBeanDescription(typeFormat, true);
    }
}