io.micronaut.context.RuntimeBeanDefinition 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.Context;
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.reflect.GenericTypeUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.inject.BeanContextConditional;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.BeanDefinitionReference;
import io.micronaut.inject.InstantiatableBeanDefinition;
import io.micronaut.inject.qualifiers.Qualifiers;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Allow the construction for bean definitions programmatically that can be registered
* via {@link BeanDefinitionRegistry} at runtime.
*
* This differs from {@link BeanDefinitionRegistry#registerSingleton(Object)} in that
* beans registered this way can be created lazily or not at all and participate
* more completely in the life cycle of the {@link BeanContext} (for examples event listeners like {@link io.micronaut.context.event.BeanCreatedEventListener} will be fired).
*
* Note that it is generally not recommended to use this approach and build time bean computation is preferred. This type is
* designed to support a few limited use cases where runtime bean registration is required.
*
* @param The bean type
* @since 3.6.0
* @author graemerocher
* @see BeanDefinitionRegistry#registerBeanDefinition(RuntimeBeanDefinition)
*/
@Experimental
public interface RuntimeBeanDefinition extends BeanDefinitionReference, InstantiatableBeanDefinition, BeanContextConditional {
@Override
@NonNull
default AnnotationMetadata getAnnotationMetadata() {
return AnnotationMetadata.EMPTY_METADATA;
}
@Override
default boolean isEnabled(@NonNull BeanContext context, BeanResolutionContext resolutionContext) {
return true;
}
@Override
default List> getTypeArguments(Class> type) {
Class beanType = getBeanType();
if (type != null && type.isAssignableFrom(beanType)) {
if (type.isInterface()) {
return Arrays.stream(GenericTypeUtils.resolveInterfaceTypeArguments(beanType, type))
.map(Argument::of)
.collect(Collectors.toList());
} else {
return Arrays.stream(GenericTypeUtils.resolveSuperTypeGenericArguments(beanType, type))
.map(Argument::of)
.collect(Collectors.toList());
}
} else {
return Collections.emptyList();
}
}
@Override
default boolean isContextScope() {
return getAnnotationMetadata().hasDeclaredAnnotation(Context.class);
}
@Override
default boolean isConfigurationProperties() {
return BeanDefinitionReference.super.isConfigurationProperties();
}
@Override
default BeanDefinition load() {
return this;
}
@Override
default String getBeanDefinitionName() {
return DefaultRuntimeBeanDefinition.generateBeanName(getBeanType());
}
@Override
default BeanDefinition load(BeanContext context) {
return this;
}
@Override
default boolean isPresent() {
return true;
}
@Override
default boolean isSingleton() {
return BeanDefinitionReference.super.isSingleton();
}
/**
* Creates a new effectively singleton bean definition that references the given bean.
*
* @param bean The bean
* @return The {@link BeanDefinitionReference}
* @param The bean type
* @since 3.6.0
*/
@NonNull
static RuntimeBeanDefinition of(@NonNull B bean) {
Objects.requireNonNull(bean, "Bean cannot be null");
@SuppressWarnings("unchecked") Class t = (Class) bean.getClass();
return builder(t, () -> bean).singleton(true).build();
}
/**
* Creates a new bean definition that will resolve the bean from the given supplier.
*
* The bean is by default not singleton and the supplier will be invoked for each injection point.
* @param beanType The bean type
* @param beanSupplier The bean supplier
* @return The {@link BeanDefinitionReference}
* @param The bean type
* @since 3.6.0
*/
@NonNull
static RuntimeBeanDefinition of(
@NonNull Class beanType,
@NonNull Supplier beanSupplier) {
return builder(beanType, beanSupplier).build();
}
/**
* A new builder for constructing and configuring runtime created beans.
* @param bean The bean to use
* @return The builder
* @param The bean type
*/
@NonNull
static Builder builder(@NonNull B bean) {
Objects.requireNonNull(bean, "Bean cannot be null");
@SuppressWarnings("unchecked")
Argument beanType = (Argument) Argument.of(bean.getClass());
return new DefaultRuntimeBeanDefinition.RuntimeBeanBuilder<>(
beanType,
() -> bean
).singleton(true);
}
/**
* A new builder for constructing and configuring runtime created beans.
* @param beanType The bean type
* @param beanSupplier The bean supplier
* @return The builder
* @param The bean type
*/
@NonNull
static Builder builder(@NonNull Class beanType, @NonNull Supplier beanSupplier) {
return new DefaultRuntimeBeanDefinition.RuntimeBeanBuilder<>(
Argument.of(beanType),
beanSupplier
);
}
/**
* A new builder for constructing and configuring runtime created beans.
* @param beanType The bean type
* @param beanSupplier The bean supplier
* @return The builder
* @param The bean type
*/
@NonNull
static Builder builder(@NonNull Argument beanType, @NonNull Supplier beanSupplier) {
return new DefaultRuntimeBeanDefinition.RuntimeBeanBuilder<>(
beanType,
beanSupplier
);
}
/**
* A builder for constructing {@link RuntimeBeanDefinition} instances.
* @param The bean type
*/
interface Builder {
/**
* The qualifier to use.
* @param qualifier The qualifier
* @return This builder
*/
@NonNull
Builder qualifier(@Nullable Qualifier qualifier);
/**
* Adds this type as a bean replacement of the given type.
* @param otherType The other type
* @return This bean builder
* @since 4.0.0
*/
@NonNull
Builder replaces(@Nullable Class extends B> otherType);
/**
* The qualifier to use.
* @param name The named qualifier to use.
* @return This builder
* @since 3.7.0
*/
@NonNull
default Builder named(@Nullable String name) {
if (name == null) {
qualifier(null);
} else {
qualifier(Qualifiers.byName(name));
}
return this;
}
/**
* The scope to use.
* @param scope The scope
* @return This builder
*/
@NonNull
Builder scope(@Nullable Class extends Annotation> scope);
/**
* Is the bean singleton.
* @param isSingleton True if it is singleton
* @return This builder
*/
@NonNull
Builder singleton(boolean isSingleton);
/**
* Limit the exposed types of this bean.
* @param types The exposed types
* @return This builder
*/
@NonNull
Builder exposedTypes(Class>...types);
/**
* The type arguments for the type.
* @param arguments The arguments
* @return This builder
*/
@NonNull
Builder typeArguments(Argument>... arguments);
/**
* The type arguments for an implemented type of this type.
* @param implementedType The implemented type
* @param arguments The arguments
* @return This builder
*/
@NonNull
Builder typeArguments(Class> implementedType, Argument>... arguments);
/**
* The annotation metadata for the bean.
* @param annotationMetadata The annotation metadata
* @return This builder
*/
@NonNull
Builder annotationMetadata(@Nullable AnnotationMetadata annotationMetadata);
/**
* Builds the runtime bean.
* @return The runtime bean
*/
@NonNull
RuntimeBeanDefinition build();
}
}