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

org.jvault.vault.InstanceVault Maven / Gradle / Ivy

package org.jvault.vault;

import org.jvault.annotation.Inject;
import org.jvault.exceptions.DisallowedAccessException;
import org.jvault.exceptions.NoDefinedInternalBeanException;
import org.jvault.factory.extensible.Vault;
import org.jvault.metadata.API;
import org.jvault.metadata.ThreadSafe;
import org.jvault.util.Reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.List;

/**
 * Receive instance of class as a parameter and inject bean to parameter. 
*
* InstanceVault injects beans into parameters, using the @Inject annotation mapped to a field or constructor of the parameter's class.
* Examples. *
* 1. Field Inject
*
 *    {@code
 *        public class Foo { @Inject private SomeBean bean; }
 *    }
 * 
* If a Foo instance is passed to a method of InstanceVault,
* InstanceVault injects a bean into the field mapped to @Inject of the created Foo instance.
*
* 2. Constructor Inject
*
 *     {@code
 *      public class Foo{
 *
 *          private SomeBean bean;
 *
 *          public Foo(){}
 *
 *·         @Inject
 *          private Foo(@Inject("bean") SomeBean someBean){ }
 *      }
 *     }
 * 
* InstanceVault injects a value into the field based on the constructor parameter information mapped with @Inject of the passed parameter.
* Note that the constructor is not actually executed, and the bean is injected based on the parameter information of the constructor.
* and, the injected field is not mapped to the parameter name of the constructor,
* but is mapped to the value of the @Inject annotation marked on the parameter of the constructor.
*
* Also, constructor parameters must be marked with @Inject annotation. Otherwise, {@link org.jvault.exceptions.DuplicateInjectConstructorException} is thrown.
*
* InstanceVault can only be instantiated in the org.jvault.* package,
* and actually you can't force instantiation of Vault without using Reflection.
* To obtain InstanceVault, see the {@link org.jvault.factory.TypeVaultFactory} class. * * @author devxb * @since 0.1 */ @API @ThreadSafe public final class InstanceVault extends AbstractVault{ private final Reflection REFLECTION; InstanceVault(Vault.Builder builder) { super(builder); REFLECTION = Accessors.UtilAccessor.getAccessor().getReflection(); } /** * Same method as inject(injectTarget, returnType).
* The inject(injectTarget, returnType) method receives two parameters to prevent unchecked cast,
* but this method enables the above function to be performed with one parameter.
* Therefore, it is more convenient and recommended to use this method. * * @param injectTarget The instance of class to be injected beans, Vault will inject beans into this parameter. * @return Returns an instance of the type received param. * @param the type of return instance. */ @SuppressWarnings("unchecked") public R inject(R injectTarget){ return inject(injectTarget, (Class)injectTarget.getClass()); } /** * Method of inject beans into parameter and return same identified instance of parameter (that may be injected bean).
*
* Unlike ClassVault, InstanceVault does not cache parameters that marked @InternalBean(type = Type.SINGLETON).
* This means that the instance returned for each request has the same address value as the instance passed by the user each time.
* * @param the type of return instance. * @param injectTarget The instance of target class to be injected beans, Vault will inject beans into this parameter. * @param returnType The class-type to be returned. This must be a type that matches the param parameter. * @return Returns a same identified instance (that may be injected bean) of the injectTarget received param. * @throws DisallowedAccessException Occurs when the package in param is a package that does not have access to Vault, * * or the Beans to be injected into param cannot be injected into the package in Param. */ @Override public R inject(Object injectTarget, Class returnType) { R typeOfInjectTarget = returnType.cast(injectTarget); throwIfParamDoesNotAccessible(typeOfInjectTarget.getClass()); Constructor constructor = REFLECTION.findConstructor(returnType); if(isConstructorInjectable(constructor)) return injectBeanToConstructor(typeOfInjectTarget, constructor); return injectBeanToTargetField(typeOfInjectTarget); } private boolean isConstructorInjectable(Constructor constructor){ return constructor != null; } private R injectBeanToConstructor(R injectTarget, Constructor constructor){ Class injectTargetClass = injectTarget.getClass(); List parameters = REFLECTION.getAnnotatedConstructorParameters(constructor); for(Parameter parameter : parameters){ Inject inject = parameter.getDeclaredAnnotation(Inject.class); String beanName = inject.value(); throwIfCanNotFindDefinedBean(beanName); Field field = getInjectTargetField(injectTargetClass, beanName); field.setAccessible(true); injectBeanToField(injectTarget, field, beanName); } return injectTarget; } private Field getInjectTargetField(Class injectTargetClass, String name){ try { return injectTargetClass.getDeclaredField(name); } catch (NoSuchFieldException e){ throw new IllegalStateException("Failed to find a field that matches the constructor's parameter bean name \"" + name + "\""); } } private R injectBeanToTargetField(R injectTarget){ List fields = REFLECTION.findFields(injectTarget.getClass()); for(Field field : fields){ field.setAccessible(true); String beanName = getBeanNameByField(field); throwIfCanNotFindDefinedBean(beanName); injectBeanToField(injectTarget, field, beanName); } return injectTarget; } private String getBeanNameByField(Field field) { String beanName = field.getName(); if (!field.getAnnotation(Inject.class).value().equals("")) beanName = field.getAnnotation(Inject.class).value(); return beanName; } private void throwIfCanNotFindDefinedBean(String beanName) { if (!BEANS.containsKey(beanName)) throw new NoDefinedInternalBeanException(beanName); } private void injectBeanToField(Object injectTarget, Field field, String beanName) { try { field.set(injectTarget, BEANS.get(beanName).loadIfInjectable(injectTarget.getClass())); } catch (IllegalAccessException IAE) { throw new IllegalStateException("Can not access field value \"" + beanName + "\""); } } }