All Downloads are FREE. Search and download functionalities are using the official Maven repository.
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.context.DefaultRuntimeBeanDefinition Maven / Gradle / Ivy
/*
* Copyright 2017-2022 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.context;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.context.exceptions.BeanInstantiationException;
import io.micronaut.core.annotation.AnnotationClassValue;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.Experimental;
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.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.annotation.MutableAnnotationMetadata;
import io.micronaut.inject.qualifiers.ClosestTypeArgumentQualifier;
import io.micronaut.inject.qualifiers.PrimaryQualifier;
import io.micronaut.inject.qualifiers.TypeArgumentQualifier;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/**
* Default implementation of {@link RuntimeBeanDefinition}.
* @param The bean type
* @author graemerocher
* @since 3.6.0
*/
@Experimental
final class DefaultRuntimeBeanDefinition extends AbstractBeanContextConditional implements RuntimeBeanDefinition {
private static final AtomicInteger REF_COUNT = new AtomicInteger(0);
private static final String MSG_BEAN_TYPE_CANNOT_BE_NULL = "Bean type cannot be null";
private final Argument beanType;
private final Supplier supplier;
private final AnnotationMetadata annotationMetadata;
private final String beanName;
private final Qualifier qualifier;
private final boolean isSingleton;
private final Class extends Annotation> scope;
private final Class>[] exposedTypes;
private Map, List>> typeArguments;
DefaultRuntimeBeanDefinition(
@NonNull Argument beanType,
@NonNull Supplier supplier,
@Nullable Qualifier qualifier,
@Nullable AnnotationMetadata annotationMetadata,
boolean isSingleton,
@Nullable Class extends Annotation> scope,
Class>[] exposedTypes, Map,
List>> typeArguments) {
Objects.requireNonNull(beanType, MSG_BEAN_TYPE_CANNOT_BE_NULL);
Objects.requireNonNull(supplier, "Bean supplier cannot be null");
this.beanType = beanType;
this.supplier = supplier;
this.beanName = generateBeanName(beanType.getType());
this.qualifier = qualifier;
this.annotationMetadata = annotationMetadata == null ? AnnotationMetadata.EMPTY_METADATA : annotationMetadata;
this.isSingleton = isSingleton;
this.scope = scope;
this.exposedTypes = exposedTypes;
this.typeArguments = typeArguments;
}
@Override
public List> getTypeArguments(Class> type) {
Class bt = getBeanType();
if (type == bt) {
return getTypeArguments();
}
if (type != null && type.isAssignableFrom(bt)) {
if (typeArguments != null) {
List> args = typeArguments.get(type);
if (args != null) {
return args;
}
}
List> list = RuntimeBeanDefinition.super.getTypeArguments(type);
if (CollectionUtils.isNotEmpty(list)) {
if (typeArguments == null) {
synchronized (this.beanType) {
typeArguments = new LinkedHashMap<>(3);
}
}
typeArguments.put(type, list);
}
return list;
} else {
return Collections.emptyList();
}
}
@Override
public boolean isAbstract() {
return false;
}
@Override
@NonNull
public Set> getExposedTypes() {
return ArrayUtils.isNotEmpty(exposedTypes) ?
CollectionUtils.setOf(exposedTypes) :
RuntimeBeanDefinition.super.getExposedTypes();
}
@Override
public Optional> getScope() {
return Optional.ofNullable(scope);
}
@Override
public Optional getScopeName() {
return getScope().map(Class::getName);
}
@Override
@NonNull
public Argument asArgument() {
return beanType;
}
@Override
public boolean isPrimary() {
return qualifier == PrimaryQualifier.INSTANCE || RuntimeBeanDefinition.super.isPrimary();
}
@Override
public Qualifier getDeclaredQualifier() {
return this.qualifier != null ? this.qualifier :
RuntimeBeanDefinition.super.getDeclaredQualifier();
}
@Override
public Qualifier resolveDynamicQualifier() {
return qualifier;
}
/**
* Generates the bean name for the give ntype.
* @param beanType The bean type
* @return The bean name
*/
static String generateBeanName(@NonNull Class> beanType) {
Objects.requireNonNull(beanType, MSG_BEAN_TYPE_CANNOT_BE_NULL);
return beanType.getName() + "$DynamicDefinition" + REF_COUNT.incrementAndGet();
}
@Override
public String getBeanDefinitionName() {
return beanName;
}
@Override
@NonNull
public AnnotationMetadata getAnnotationMetadata() {
return annotationMetadata;
}
@Override
public BeanDefinition load() {
return this;
}
@Override
public boolean isPresent() {
return true;
}
@Override
public Class getBeanType() {
return beanType.getType();
}
@SuppressWarnings("unchecked")
@Override
@NonNull
public List> getTypeArguments() {
return Arrays.asList(beanType.getTypeParameters());
}
@Override
@NonNull
public Class>[] getTypeParameters() {
return getTypeArguments()
.stream()
.map(Argument::getType)
.toArray(Class[]::new);
}
@Override
public boolean isSingleton() {
return isSingleton;
}
@Override
public T instantiate(BeanResolutionContext resolutionContext, BeanContext context) throws BeanInstantiationException {
return supplier.get();
}
/**
* Implementation of {@link RuntimeBeanDefinition.Builder}.
* @param The bean
*/
static final class RuntimeBeanBuilder implements RuntimeBeanDefinition.Builder {
private Argument beanType;
private final Supplier supplier;
private Qualifier qualifier;
private boolean singleton;
private AnnotationMetadata annotationMetadata;
private Class extends Annotation> scope;
private Class>[] exposedTypes = ReflectionUtils.EMPTY_CLASS_ARRAY;
private Map, List>> typeArguments;
private Class extends B> replacesType;
RuntimeBeanBuilder(Argument beanType, Supplier supplier) {
this.beanType = Objects.requireNonNull(beanType, MSG_BEAN_TYPE_CANNOT_BE_NULL);
this.supplier = Objects.requireNonNull(supplier, "Bean supplier cannot be null");
}
@Override
public Builder qualifier(Qualifier qualifier) {
this.qualifier = qualifier;
if (qualifier instanceof TypeArgumentQualifier typeArgumentQualifier) {
Argument>[] arguments = Arrays.stream(typeArgumentQualifier.getTypeArguments())
.map(Argument::of)
.toArray(Argument[]::new);
typeArguments(arguments);
} else if (qualifier instanceof ClosestTypeArgumentQualifier typeArgumentQualifier) {
Argument>[] arguments = Arrays.stream(typeArgumentQualifier.getTypeArguments())
.map(Argument::of)
.toArray(Argument[]::new);
typeArguments(arguments);
}
return this;
}
@Override
public Builder replaces(Class extends B> otherType) {
this.replacesType = otherType;
return this;
}
@Override
@SuppressWarnings("java:S1872")
public Builder scope(Class extends Annotation> scope) {
this.scope = scope;
if (scope != null && scope.getSimpleName().equals("Singleton")) {
this.singleton = true;
}
return this;
}
@Override
public Builder singleton(boolean isSingleton) {
this.singleton = true;
return this;
}
@Override
public Builder exposedTypes(Class>... types) {
for (Class> type : types) {
if (!type.isAssignableFrom(beanType.getType())) {
throw new IllegalArgumentException("Bean type doesn't implement: " + type.getName());
}
}
this.exposedTypes = types;
return this;
}
@Override
public Builder typeArguments(Argument>... arguments) {
this.beanType = Argument.of(beanType.getType(), arguments);
return this;
}
@Override
public Builder typeArguments(Class> implementedType, Argument>... arguments) {
if (typeArguments == null) {
typeArguments = new LinkedHashMap<>(5);
}
typeArguments.put(implementedType, List.of(arguments));
return this;
}
@Override
public Builder annotationMetadata(AnnotationMetadata annotationMetadata) {
this.annotationMetadata = annotationMetadata;
return this;
}
@Override
@NonNull
public RuntimeBeanDefinition build() {
if (replacesType != null) {
MutableAnnotationMetadata mutableAnnotationMetadata;
if (this.annotationMetadata instanceof MutableAnnotationMetadata mm) {
mutableAnnotationMetadata = mm;
} else if (this.annotationMetadata == null || this.annotationMetadata == EMPTY_METADATA) {
mutableAnnotationMetadata = new MutableAnnotationMetadata();
this.annotationMetadata = mutableAnnotationMetadata;
} else {
throw new IllegalStateException("Previous non-mutable annotation metadata set");
}
Map values = new HashMap<>(3);
values.put(AnnotationMetadata.VALUE_MEMBER, new AnnotationClassValue<>(replacesType));
if (qualifier instanceof Named named) {
values.put("named", named.getName());
}
mutableAnnotationMetadata.addAnnotation(Replaces.class.getName(), values);
}
return new DefaultRuntimeBeanDefinition<>(
beanType,
supplier,
qualifier,
annotationMetadata,
singleton,
scope,
exposedTypes,
typeArguments
);
}
}
}