/*
* 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.core.reflect;
import io.micronaut.core.beans.BeanIntrospection;
import io.micronaut.core.beans.BeanIntrospector;
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.convert.exceptions.ConversionErrorException;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.reflect.exception.InstantiationException;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.ArgumentUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
Utility methods for instantiating objects.
Author: Graeme Rocher Since: 1.0
/**
* Utility methods for instantiating objects.
*
* @author Graeme Rocher
* @since 1.0
*/
public class InstantiationUtils {
Try to instantiate the given class.
Params: - name – The class name
- classLoader – The class loader to use
Returns: The instantiated instance or Optional.empty()
/**
* Try to instantiate the given class.
*
* @param name The class name
* @param classLoader The class loader to use
* @return The instantiated instance or {@link Optional#empty()}
*/
public static Optional<?> tryInstantiate(String name, ClassLoader classLoader) {
try {
return ClassUtils.forName(name, classLoader)
.flatMap(InstantiationUtils::tryInstantiate);
} catch (Throwable e) {
Logger log = LoggerFactory.getLogger(InstantiationUtils.class);
if (log.isDebugEnabled()) {
log.debug("Tried, but could not instantiate type: " + name, e);
}
return Optional.empty();
}
}
Try to instantiate the given class using BeanIntrospector
. Params: - type – The type
- propertiesMap – The properties values
Map
of the instance - context – The Conversion context
Type parameters: - <T> – The generic type
Throws: - InstantiationException – When an error occurs
Returns: The instantiated instance or Optional.empty()
/**
* Try to instantiate the given class using {@link io.micronaut.core.beans.BeanIntrospector}.
*
* @param type The type
* @param propertiesMap The properties values {@link Map} of the instance
* @param context The Conversion context
* @param <T> The generic type
* @return The instantiated instance or {@link Optional#empty()}
* @throws InstantiationException When an error occurs
*/
public static @NonNull <T> Optional<T> tryInstantiate(@NonNull Class<T> type, Map propertiesMap, ConversionContext context) {
ArgumentUtils.requireNonNull("type", type);
if (propertiesMap.isEmpty()) {
return tryInstantiate(type);
}
final Supplier<T> reflectionFallback = () -> {
Logger log = LoggerFactory.getLogger(InstantiationUtils.class);
if (log.isDebugEnabled()) {
log.debug("Tried, but could not instantiate type: " + type);
}
return null;
};
T result = BeanIntrospector.SHARED.findIntrospection(type).map(introspection -> {
T instance;
Argument[] constructorArguments = introspection.getConstructorArguments();
List<Object> arguments = new ArrayList<>(constructorArguments.length);
try {
if (constructorArguments.length > 0) {
Map bindMap = new LinkedHashMap(propertiesMap.size());
Set<Map.Entry<?, ?>> entries = propertiesMap.entrySet();
for (Map.Entry<?, ?> entry : entries) {
Object key = entry.getKey();
bindMap.put(NameUtils.decapitalize(NameUtils.dehyphenate(key.toString())), entry.getValue());
}
for (Argument<?> argument : constructorArguments) {
if (bindMap.containsKey(argument.getName())) {
Object converted = ConversionService.SHARED.convert(bindMap.get(argument.getName()), argument.getType(), ConversionContext.of(argument)).orElseThrow(() ->
new ConversionErrorException(argument, context.getLastError()
.orElse(() -> new IllegalArgumentException("Value [" + bindMap.get(argument.getName()) + "] cannot be converted to type : " + argument.getType())))
);
arguments.add(converted);
} else if (argument.isDeclaredNullable()) {
arguments.add(null);
} else {
context.reject(new ConversionErrorException(argument, () -> new IllegalArgumentException("No Value found for argument " + argument.getName())));
}
}
instance = introspection.instantiate(arguments.toArray());
} else {
instance = introspection.instantiate();
}
return instance;
} catch (InstantiationException e) {
return reflectionFallback.get();
}
}).orElseGet(reflectionFallback);
return Optional.ofNullable(result);
}
Try to instantiate the given class using the most optimal strategy first trying the BeanIntrospector
and if no bean is present falling back to reflection. Params: - type – The type
Type parameters: - <T> – The generic type
Returns: The instantiated instance or Optional.empty()
/**
* Try to instantiate the given class using the most optimal strategy first trying the {@link io.micronaut.core.beans.BeanIntrospector} and
* if no bean is present falling back to reflection.
*
* @param type The type
* @param <T> The generic type
* @return The instantiated instance or {@link Optional#empty()}
*/
public static @NonNull <T> Optional<T> tryInstantiate(@NonNull Class<T> type) {
ArgumentUtils.requireNonNull("type", type);
final Supplier<T> reflectionFallback = () -> {
final Logger logger = ClassUtils.REFLECTION_LOGGER;
if (logger.isDebugEnabled()) {
logger.debug("Cannot instantiate type [{}] without reflection. Attempting reflective instantiation", type);
}
try {
T bean = type.getDeclaredConstructor().newInstance();
if (type.isInstance(bean)) {
return bean;
}
return null;
} catch (Throwable e) {
try {
Constructor<T> defaultConstructor = type.getDeclaredConstructor();
defaultConstructor.setAccessible(true);
return tryInstantiate(defaultConstructor).orElse(null);
} catch (Throwable e1) {
Logger log = LoggerFactory.getLogger(InstantiationUtils.class);
if (log.isDebugEnabled()) {
log.debug("Tried, but could not instantiate type: " + type, e);
}
return null;
}
}
};
final T result = BeanIntrospector.SHARED.findIntrospection(type).map(introspection -> {
try {
return introspection.instantiate();
} catch (InstantiationException e) {
return reflectionFallback.get();
}
}).orElseGet(reflectionFallback);
return Optional.ofNullable(result);
}
Try to instantiate the given class.
Params: - type – The type
- args – The arguments to the constructor
Type parameters: - <T> – The generic type
Returns: The instantiated instance or Optional.empty()
/**
* Try to instantiate the given class.
*
* @param type The type
* @param args The arguments to the constructor
* @param <T> The generic type
* @return The instantiated instance or {@link Optional#empty()}
*/
public static @NonNull <T> Optional<T> tryInstantiate(@NonNull Constructor<T> type, Object... args) {
try {
return Optional.of(type.newInstance(args));
} catch (Throwable e) {
Logger log = ClassUtils.REFLECTION_LOGGER;
if (log.isDebugEnabled()) {
log.debug("Tried, but could not instantiate type: " + type, e);
}
return Optional.empty();
}
}
Instantiate the given class rethrowing any exceptions as InstantiationException
. Params: - type – The type
Type parameters: - <T> – The generic type
Throws: - InstantiationException – When an error occurs
Returns: The instantiated instance
/**
* Instantiate the given class rethrowing any exceptions as {@link InstantiationException}.
*
* @param type The type
* @param <T> The generic type
* @return The instantiated instance
* @throws InstantiationException When an error occurs
*/
public static <T> T instantiate(Class<T> type) {
try {
return BeanIntrospector.SHARED.findIntrospection(type).map(BeanIntrospection::instantiate).orElseGet(() -> {
try {
Logger log = ClassUtils.REFLECTION_LOGGER;
if (log.isDebugEnabled()) {
log.debug("Reflectively instantiating type: " + type);
}
return type.getDeclaredConstructor().newInstance();
} catch (Throwable e) {
throw new InstantiationException("Could not instantiate type [" + type.getName() + "]: " + e.getMessage(), e);
}
});
} catch (Throwable e) {
throw new InstantiationException("Could not instantiate type [" + type.getName() + "]: " + e.getMessage(), e);
}
}
Instantiate the given class rethrowing any exceptions as InstantiationException
. Params: - type – The type
- classLoader – The classloader
Throws: - InstantiationException – When an error occurs
Returns: The instantiated instance
/**
* Instantiate the given class rethrowing any exceptions as {@link InstantiationException}.
*
* @param type The type
* @param classLoader The classloader
* @return The instantiated instance
* @throws InstantiationException When an error occurs
*/
public static Object instantiate(String type, ClassLoader classLoader) {
try {
return ClassUtils.forName(type, classLoader)
.flatMap(InstantiationUtils::tryInstantiate)
.orElseThrow(() -> new InstantiationException("No class found for name: " + type));
} catch (Throwable e) {
throw new InstantiationException("Could not instantiate type [" + type + "]: " + e.getMessage(), e);
}
}
Instantiate the given class rethrowing any exceptions as InstantiationException
. Params: - type – The type
- requiredType – The required type
Type parameters: - <T> – The type
Throws: - InstantiationException – When an error occurs
Returns: The instantiated instance
/**
* Instantiate the given class rethrowing any exceptions as {@link InstantiationException}.
*
* @param type The type
* @param requiredType The required type
* @param <T> The type
* @return The instantiated instance
* @throws InstantiationException When an error occurs
*/
public static <T> T instantiate(String type, Class<T> requiredType) {
try {
return ClassUtils.forName(type, requiredType.getClassLoader())
.flatMap((Function<Class, Optional<T>>) aClass -> {
if (requiredType == aClass || requiredType.isAssignableFrom(aClass)) {
return tryInstantiate(aClass);
}
return Optional.empty();
})
.orElseThrow(() -> new InstantiationException("No compatible class found for name: " + type));
} catch (Throwable e) {
throw new InstantiationException("Could not instantiate type [" + type + "]: " + e.getMessage(), e);
}
}
}