/*************************************************************************** * Copyright 2009-2012 by Christian Ihle * * kontakt@usikkert.net * * * * This file is part of KouInject. * * * * KouInject 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 3 of * * the License, or (at your option) any later version. * * * * KouInject 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with KouInject. * * If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ package net.usikkert.kouinject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import net.usikkert.kouinject.beandata.BeanData; import net.usikkert.kouinject.beandata.BeanKey; import net.usikkert.kouinject.beandata.ConstructorData; import net.usikkert.kouinject.beandata.FieldData; import net.usikkert.kouinject.beandata.InjectionPoint; import net.usikkert.kouinject.beandata.MethodData; import net.usikkert.kouinject.generics.GenericsHelper; import net.usikkert.kouinject.generics.TypeMap; import net.usikkert.kouinject.util.BeanHelper; import net.usikkert.kouinject.util.ReflectionUtils; import org.apache.commons.lang.Validate; /** * Implementation of {@link BeanDataHandler} that uses annotations and reflection * to extract meta-data from beans to find dependencies. * * <p>Scans beans for the {@link Inject} annotation to detect constructor, fields and methods * for dependency injection. They are then scanned for the {@link javax.inject.Qualifier} annotation to * find the required qualifier for the dependency.</p> * * @author Christian Ihle */ public class AnnotationBasedBeanDataHandler implements BeanDataHandler { private static final Class<Inject> INJECTION_ANNOTATION = Inject.class; private final AnnotationBasedScopeHandler scopeHandler = new AnnotationBasedScopeHandler(); private final ReflectionUtils reflectionUtils = new ReflectionUtils(); private final BeanHelper beanHelper = new BeanHelper(); /** * {@inheritDoc} */ @Override public BeanData getBeanData(final BeanKey beanKey, final boolean skipConstructor) { Validate.notNull(beanKey, "Bean key can not be null"); final Class<?> beanClass = beanKey.getBeanClass(); Validate.notNull(beanClass, "Bean class can not be null"); final TypeMap typeMap = GenericsHelper.mapTypeVariablesToActualTypes(beanClass); final List<Method> allMethods = reflectionUtils.findAllMethods(beanClass); final List<Member> allMembers = reflectionUtils.findAllMembers(beanClass); final List<InjectionPoint> injectionPoints = findInjectionPoints(allMembers, allMethods, typeMap); final ConstructorData constructorData = createConstructorDataIfNeeded(beanClass, skipConstructor, typeMap); final boolean singleton = scopeHandler.isSingleton(beanClass); return new BeanData(beanKey, constructorData, injectionPoints, singleton); } private ConstructorData createConstructorDataIfNeeded(final Class<?> beanClass, final boolean skipConstructor, final TypeMap typeMap) { if (skipConstructor) { return null; } final Constructor<?> constructor = findConstructor(beanClass); return createConstructorData(constructor, typeMap); } private List<InjectionPoint> findInjectionPoints(final List<Member> allMembers, final List<Method> allMethods, final TypeMap typeMap) { final List<InjectionPoint> injectionPoints = new ArrayList<InjectionPoint>(); for (final Member member : allMembers) { if (member instanceof Field) { final Field field = (Field) member; if (fieldNeedsInjection(field)) { final FieldData fieldData = createFieldData(field, typeMap); injectionPoints.add(fieldData); } } else if (member instanceof Method) { final Method method = (Method) member; if (methodNeedsInjection(method) && !reflectionUtils.isOverridden(method, allMethods)) { final MethodData methodData = createMethodData(method, typeMap); injectionPoints.add(methodData); } } else { throw new UnsupportedOperationException("Unsupported member: " + member); } } return injectionPoints; } private boolean fieldNeedsInjection(final Field field) { return !reflectionUtils.isStatic(field) && !reflectionUtils.isFinal(field) && field.isAnnotationPresent(INJECTION_ANNOTATION); } private FieldData createFieldData(final Field field, final TypeMap typeMap) { final BeanKey dependency = beanHelper.findFieldKey(field, typeMap); return new FieldData(field, dependency); } private boolean methodNeedsInjection(final Method method) { return !reflectionUtils.isStatic(method) && method.isAnnotationPresent(INJECTION_ANNOTATION); } private MethodData createMethodData(final Method method, final TypeMap typeMap) { final List<BeanKey> dependencies = beanHelper.findParameterKeys(method, typeMap); return new MethodData(method, dependencies); } private Constructor<?> findConstructor(final Class<?> beanClass) { final Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors(); final List<Constructor<?>> matches = new ArrayList<Constructor<?>>(); for (final Constructor<?> constructor : declaredConstructors) { if (constructorNeedsInjection(constructor)) { matches.add(constructor); } } if (matches.size() == 0) { try { return beanClass.getDeclaredConstructor(); } catch (final NoSuchMethodException e) { throw new RuntimeException(e); } } else if (matches.size() > 1) { throw new UnsupportedOperationException( "Wrong number of constructors found for autowiring " + beanClass + " " + matches); } return matches.get(0); } private boolean constructorNeedsInjection(final Constructor<?> constructor) { return constructor.isAnnotationPresent(INJECTION_ANNOTATION); } private ConstructorData createConstructorData(final Constructor<?> constructor, final TypeMap typeMap) { final List<BeanKey> dependencies = beanHelper.findParameterKeys(constructor, typeMap); return new ConstructorData(constructor, dependencies); } }