Java tutorial
/* * Grapht, an open source dependency injector. * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt) * Copyright 2010-2014 Regents of the University of Minnesota * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 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, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.grouplens.grapht.reflect.internal; import org.apache.commons.lang3.reflect.MethodUtils; import org.grouplens.grapht.ConstructionException; import org.grouplens.grapht.Instantiator; import org.grouplens.grapht.LifecycleManager; import org.grouplens.grapht.NullDependencyException; import org.grouplens.grapht.reflect.Desire; import org.grouplens.grapht.reflect.InjectionPoint; import org.grouplens.grapht.util.LogContext; import org.grouplens.grapht.util.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.PostConstruct; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Instantiates class instances. * * @author <a href="http://grouplens.org">GroupLens Research</a> */ public class ClassInstantiator implements Instantiator { private static final Logger logger = LoggerFactory.getLogger(ClassInstantiator.class); private final Class<?> type; private final List<Desire> desires; private final Map<Desire, Instantiator> providers; private final LifecycleManager manager; /** * Create an ClassInstantiator that will provide instances of the given * type, with given the list of desires and a function mapping that * satisfies those providers. * * @param type The type of instance created * @param desires The dependency desires for the instance * @param providers The providers that satisfy the desires of the type */ public ClassInstantiator(Class<?> type, List<Desire> desires, Map<Desire, Instantiator> providers, LifecycleManager manager) { Preconditions.notNull("type", type); Preconditions.notNull("desires", desires); Preconditions.notNull("providers", providers); this.type = type; this.desires = desires; this.providers = providers; this.manager = manager; } @Override public Class getType() { return type; } @Override public Object instantiate() throws ConstructionException { // find constructor and build up necessary constructor arguments Constructor<?> ctor = getConstructor(); LogContext globalLogContext = LogContext.create(); Object instance = null; Method[] methods; try { // create the instance that we are injecting try { globalLogContext.put("org.grouplens.grapht.class", ctor.getClass().toString()); Object[] ctorArgs = new Object[ctor.getParameterTypes().length]; for (Desire d : desires) { LogContext ipContext = LogContext.create(); if (d.getInjectionPoint() instanceof ConstructorParameterInjectionPoint) { // this desire is a constructor argument so create it now Instantiator provider = providers.get(d); ConstructorParameterInjectionPoint cd = (ConstructorParameterInjectionPoint) d .getInjectionPoint(); logger.trace("Injection point satisfactions in progress {}", cd); try { ipContext.put("org.grouplens.grapht.injectionPoint", cd.toString()); } finally { ipContext.finish(); } ctorArgs[cd.getParameterIndex()] = checkNull(cd, provider.instantiate()); } } logger.trace("Invoking constructor {} with arguments {}", ctor, ctorArgs); ctor.setAccessible(true); instance = ctor.newInstance(ctorArgs); } catch (InvocationTargetException e) { throw new ConstructionException(ctor, "Constructor " + ctor + " failed", e); } catch (InstantiationException e) { throw new ConstructionException(ctor, "Could not instantiate " + type, e); } catch (IllegalAccessException e) { throw new ConstructionException(ctor, "Access violation on " + ctor, e); } // satisfy dependencies in the order of the list, which was // prepared to comply with JSR 330 Map<Method, InjectionArgs> settersAndArguments = new HashMap<Method, InjectionArgs>(); for (Desire d : desires) { LogContext ipContext = LogContext.create(); try { final InjectionStrategy injectionStrategy = InjectionStrategy .forInjectionPoint(d.getInjectionPoint()); ipContext.put("org.grouplens.grapht.injectionPoint", d.getInjectionPoint().toString()); injectionStrategy.inject(d.getInjectionPoint(), instance, providers.get(d), settersAndArguments); } finally { ipContext.finish(); } } } finally { globalLogContext.finish(); } if (manager != null) { manager.registerComponent(instance); } methods = MethodUtils.getMethodsWithAnnotation(type, PostConstruct.class); for (Method method : methods) { method.setAccessible(true); try { method.invoke(instance); } catch (InvocationTargetException e) { throw new ConstructionException("Exception throw by " + method, e); } catch (IllegalAccessException e) { throw new ConstructionException("Access violation invoking " + method, e); } } // the instance has been fully configured return instance; } @SuppressWarnings("unchecked") private Constructor<?> getConstructor() { for (Desire d : desires) { if (d.getInjectionPoint() instanceof ConstructorParameterInjectionPoint) { // since we only allow one injectable constructor, any ConstructorParameterInjectionPoint // will have the same constructor as all other constructor parameter injection points Constructor<?> ctor = ((ConstructorParameterInjectionPoint) d.getInjectionPoint()).getMember(); logger.debug("Using constructor annotated with @Inject: {}", ctor); return ctor; } } try { logger.debug("Using default constructor for {}", type); return type.getDeclaredConstructor(); } catch (NoSuchMethodException e) { // this constructor is being invoked for a ClassSatisfaction or a // ProviderClassSatisfaction, both of which assert that the type is // instantiable, so this should never happen throw new RuntimeException("Unexpected exception", e); } } static Object checkNull(InjectionPoint injectPoint, Object value) throws NullDependencyException { if (value == null && !injectPoint.isNullable()) { throw new NullDependencyException(injectPoint); } else { return value; } } static class InjectionArgs { public final Object[] arguments; private final boolean[] injected; public InjectionArgs(int num) { arguments = new Object[num]; injected = new boolean[num]; } public void set(int i, Object o) { arguments[i] = o; injected[i] = true; } public boolean isCompleted() { for (int i = 0; i < injected.length; i++) { if (!injected[i]) { return false; } } return true; } } }