All Downloads are FREE. Search and download functionalities are using the official Maven repository.

infra.beans.support.BeanInstantiator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 - 2024 the original author or authors.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see [https://www.gnu.org/licenses/]
 */

package infra.beans.support;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;

import infra.beans.BeanInstantiationException;
import infra.beans.BeanUtils;
import infra.core.ConstructorNotFoundException;
import infra.lang.Assert;
import infra.lang.Nullable;
import infra.reflect.MethodAccessor;
import infra.reflect.MethodInvoker;
import infra.reflect.ReflectionException;
import infra.util.ReflectionUtils;

/**
 * bean-instantiator: bean instantiate strategy
 *
 * @author Harry Yang
 * @see java.lang.reflect.Constructor
 * @since 2020-08-13 19:31
 */
public abstract class BeanInstantiator {

  /**
   * get internal Constructor
   *
   * @see Constructor
   * @since 4.0
   */
  @Nullable
  public Constructor getConstructor() {
    return null;
  }

  /**
   * Invoke default {@link java.lang.reflect.Constructor}
   *
   * @return returns T
   * @throws BeanInstantiationException cannot instantiate a bean
   */
  public Object instantiate() {
    return instantiate(null);
  }

  /**
   * Invoke {@link java.lang.reflect.Constructor} with given args
   *
   * @return returns T
   * @throws BeanInstantiationException cannot instantiate a bean
   */
  public final Object instantiate(@Nullable Object[] args) {
    try {
      return doInstantiate(args);
    }
    catch (BeanInstantiationException e) {
      throw e;
    }
    catch (Throwable e) {
      throw new BeanInstantiationException(this + " cannot instantiate a bean", e);
    }
  }

  // internal new-instance impl @since 4.0
  protected abstract Object doInstantiate(@Nullable Object[] args)
          throws Throwable;

  //---------------------------------------------------------------------
  // Static Factory Methods
  //---------------------------------------------------------------------

  /**
   * use BeanInstantiatorGenerator to create bytecode Constructor access
   * or fallback to java reflect Constructor cause private access
   *
   * @param constructor java reflect Constructor
   * @return BeanInstantiator to construct target T
   * @see BeanInstantiatorGenerator#create()
   */
  public static BeanInstantiator forConstructor(Constructor constructor) {
    return new BeanInstantiatorGenerator(constructor).create();
  }

  /**
   * use factory-method accessor
   *
   * @param accessor factory-method accessor
   * @param obj accessor object
   * @return MethodAccessorBeanInstantiator to construct target T
   */
  public static BeanInstantiator forMethod(MethodAccessor accessor, Object obj) {
    return new MethodAccessorBeanInstantiator(accessor, obj);
  }

  /**
   * use java reflect factory-method
   *
   * @param method java reflect method
   * @param obj accessor object
   * @return MethodAccessorBeanInstantiator to construct target T
   */
  public static BeanInstantiator forMethod(Method method, Object obj) {
    return forMethod(MethodInvoker.forMethod(method), obj);
  }

  /**
   * use factory-method accessor
   *
   * @param accessor factory-method accessor
   * @param obj accessor object Supplier
   * @return MethodAccessorBeanInstantiator to construct target T
   */
  public static BeanInstantiator forMethod(MethodAccessor accessor, Supplier obj) {
    return new MethodAccessorBeanInstantiator(accessor, obj);
  }

  /**
   * use java reflect method
   *
   * @param method factory-method
   * @return MethodAccessorBeanInstantiator to construct target T
   */
  public static BeanInstantiator forMethod(Method method, Supplier obj) {
    return forMethod(MethodInvoker.forMethod(method), obj);
  }

  /**
   * use static factory-method accessor
   *
   * @param accessor static factory-method accessor
   * @return StaticMethodAccessorBeanInstantiator to construct target T
   */
  public static BeanInstantiator forStaticMethod(MethodAccessor accessor) {
    Assert.notNull(accessor, "MethodAccessor is required");
    return new StaticMethodAccessorBeanInstantiator(accessor);
  }

  /**
   * use static java reflect method
   *
   * @param method static factory-method
   * @return StaticMethodAccessorBeanInstantiator to construct target T
   */
  public static BeanInstantiator forStaticMethod(Method method) {
    return forStaticMethod(MethodInvoker.forMethod(method));
  }

  /**
   * Get target class's {@link BeanInstantiator}
   *
   * 

* just invoke Constructor, fast invoke or use java reflect * * @param targetClass Target class * @return {@link BeanInstantiator} * @throws ConstructorNotFoundException No suitable constructor * @see BeanUtils#obtainConstructor(Class) */ public static BeanInstantiator forClass(final Class targetClass) { Constructor suitableConstructor = BeanUtils.obtainConstructor(targetClass); return forConstructor(suitableConstructor); } /** * @param function function */ public static BeanInstantiator forFunction(Function function) { Assert.notNull(function, "instance function is required"); return new FunctionInstantiator(function); } /** * @param supplier bean instance supplier */ public static BeanInstantiator forSupplier(Supplier supplier) { Assert.notNull(supplier, "instance supplier is required"); return new SupplierInstantiator(supplier); } /** * use default constructor * * @param target target class */ public static BeanInstantiator forConstructor(final Class target) { Assert.notNull(target, "target class is required"); if (target.isArray()) { Class componentType = target.getComponentType(); return new ArrayInstantiator(componentType); } else if (Collection.class.isAssignableFrom(target)) { return new CollectionInstantiator(target); } else if (Map.class.isAssignableFrom(target)) { return new MapInstantiator(target); } try { final Constructor constructor = target.getDeclaredConstructor(); return forConstructor(constructor); } catch (NoSuchMethodException e) { throw new ReflectionException( "Target class: '%s' has no default constructor".formatted(target), e); } } /** * @param constructor java reflect Constructor * @return ReflectiveConstructor * @see ReflectiveInstantiator */ public static ConstructorAccessor forReflective(Constructor constructor) { Assert.notNull(constructor, "Constructor is required"); ReflectionUtils.makeAccessible(constructor); return new ReflectiveInstantiator(constructor); } /** * Instantiates an object, WITHOUT calling it's constructor, using internal * sun.reflect.ReflectionFactory - a class only available on JDK's that use Sun's 1.4 (or later) * Java implementation. This is the best way to instantiate an object without any side effects * caused by the constructor - however it is not available on every platform. * * @param target target class * @return SunReflectionFactoryInstantiator * @see SunReflectionFactoryInstantiator * @since 4.0 */ public static BeanInstantiator forSerialization(final Class target) { return new SunReflectionFactoryInstantiator(target); } }