org.rosenvold.spring.convention.ConventionBeanFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.rosenvold.spring.convention.ConventionBeanFactory.java

Source

package org.rosenvold.spring.convention;

/*
 * Copyright 2011 the original author or 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
 *
 *      http://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.
 */

import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.*;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ConventionBeanFactory extends DefaultListableBeanFactory {

    private final NameToClassResolver nameToClassResolver;
    private final CandidateEvaluator candidateEvaluator;

    private final Map<String, AnnotatedBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, AnnotatedBeanDefinition>();
    private final Map<Class, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<Class, RootBeanDefinition>();
    private final Map<String, Class> cachedConventionResolutions = new ConcurrentHashMap<String, Class>();
    private final Map<Class, Object> resolvableDependenciesLocalCache = new HashMap<Class, Object>();
    /* Maps by type to bean names */
    private final Map<Class, String[]> byTypeMappingSingletonsEager = new ConcurrentHashMap<Class, String[]>();
    private final Map<Class, String[]> byTypeMappingNonSingletonsEager = new ConcurrentHashMap<Class, String[]>();

    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(
            this.resourcePatternResolver);
    private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();
    private final QualifierAnnotationAutowireCandidateResolver qualifierAnnotationAutowireCandidateResolver = new QualifierAnnotationAutowireCandidateResolver();
    private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
    private final String[] nothing = new String[] {};

    public ConventionBeanFactory(NameToClassResolver beanClassResolver, CandidateEvaluator candidateEvaluator) {
        this.nameToClassResolver = beanClassResolver;
        this.candidateEvaluator = candidateEvaluator;
    }

    private void clearTypeBasedCaches() {
        byTypeMappingSingletonsEager.clear();
        byTypeMappingNonSingletonsEager.clear();
    }

    @Override
    public String[] getBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit) {
        if (type == null || !allowEagerInit) {
            return getBeanNamesForTypeImpl(type, includeNonSingletons, allowEagerInit);
        }
        Map<Class, String[]> cache = includeNonSingletons ? byTypeMappingNonSingletonsEager
                : byTypeMappingSingletonsEager;
        String[] resolvedBeanNames = cache.get(type);
        if (resolvedBeanNames != null) {
            return resolvedBeanNames;
        }
        resolvedBeanNames = getBeanNamesForTypeImpl(type, includeNonSingletons, allowEagerInit);
        cache.put(type, resolvedBeanNames);
        return resolvedBeanNames;
    }

    public synchronized String[] getBeanNamesForTypeImpl(Class type, boolean includeNonSingletons,
            boolean allowEagerInit) { // LBF, local only.
        final Class cacheEntry = getCacheEntry(type);
        if (cacheEntry == null) {
            final String[] beanNamesForType = super.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
            if (beanNamesForType.length > 0)
                return beanNamesForType;

            final Class aClass = resolveImplClass(type.getName());
            if (aClass != null) {
                return new String[] { aClass.getName() };
            }
            return nothing;
        } else {
            if (isCacheMiss(cacheEntry)) {
                return nothing;
            } else {
                return new String[] { cacheEntry.getName() };
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public synchronized <T> T getBean(Class<T> requiredType) throws BeansException {
        final Class cacheEntry = getCacheEntry(requiredType);
        if (cacheEntry == null) {
            if (super.getBeanNamesForType(requiredType).length > 0) {
                return super.getBean(requiredType);
            }
            final Class aClass = resolveClass(requiredType);
            return (T) instantiate(aClass);
        } else {
            return isCacheMiss(cacheEntry) ? null : (T) instantiate(cacheEntry);
        }
    }

    @Override
    public synchronized Object getBean(String name) throws BeansException {
        setupConventionBeanIfMissing(name);
        return super.getBean(name);
    }

    @Override
    public synchronized <T> T getBean(String name, Class<T> tClass) throws BeansException {
        setupConventionBeanIfMissing(name);
        return super.getBean(name, tClass);
    }

    private synchronized void registerByDirectNameToClassMapping(String name) {
        final Class<?> type = getResolvedType(name);
        registerBeanByResolvedType(name, type);
    }

    @Override
    public synchronized Object getBean(String name, Object... objects) throws BeansException {
        setupConventionBeanIfMissing(name);
        return super.getBean(name, objects);
    }

    @Override
    public synchronized boolean containsBean(String name) {
        setupConventionBeanIfMissing(name);
        return super.containsBean(name);
    }

    @Override
    public synchronized boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        setupConventionBeanIfMissing(name);
        return super.isSingleton(name);
    }

    @Override
    public synchronized boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
        setupConventionBeanIfMissing(name);
        return super.isPrototype(name);
    }

    private synchronized String getAnnotatedScope(Class<?> type) {
        if (type != null) {
            final Scope annotation = type.getAnnotation(Scope.class);
            if (annotation != null) {
                return annotation.value();
            }
        }
        return AbstractBeanDefinition.SCOPE_DEFAULT;
    }

    @Override
    public synchronized boolean isTypeMatch(String name, Class aClass) throws NoSuchBeanDefinitionException {
        setupConventionBeanIfMissing(name);
        return super.isTypeMatch(name, aClass);
    }

    @Override
    public synchronized Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        return super.getType(name);
    }

    @Override
    public synchronized String[] getAliases(String name) {
        setupConventionBeanIfMissing(name);
        return super.getAliases(name);
    }

    @Override
    public synchronized boolean containsBeanDefinition(String beanName) { // LBF; local only
        //setupConventionBeanIfMissing(beanName);
        return super.containsBeanDefinition(beanName);
    }

    @Override
    protected synchronized RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
        if (super.containsBeanDefinition(beanName)) {
            return super.getMergedLocalBeanDefinition(beanName);
        }
        final Class<?> type = getResolvedType(beanName);
        if (type != null) {
            RootBeanDefinition rootBeanDefinition = mergedBeanDefinitions.get(type);
            if (rootBeanDefinition == null) {
                rootBeanDefinition = new RootBeanDefinition(type);
                rootBeanDefinition.overrideFrom(getBeanDefinition(beanName));
                // Are these next two really necessary ???
                rootBeanDefinition.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);
                rootBeanDefinition.setScope(getAnnotatedScope(type));
                mergedBeanDefinitions.put(type, rootBeanDefinition);
            }
            return rootBeanDefinition;
        }
        return null;
    }

    @Override
    public synchronized BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
        if (super.containsBeanDefinition(beanName)) {
            return super.getBeanDefinition(beanName);
        }
        final Class<?> resolvedType = getResolvedType(beanName);
        return getOrCreateBeanDefinition(beanName, resolvedType);
    }

    protected Class predictBeanType(String beanName, RootBeanDefinition mbd, Class... typesToMatch) {
        final Class<?> resolvedType = getResolvedType(beanName);
        if (resolvedType != null)
            return resolvedType;

        return super.predictBeanType(beanName, mbd, typesToMatch);
    }

    private void setupConventionBeanIfMissing(String name) {
        if (!super.containsBeanDefinition(name)) {
            registerByDirectNameToClassMapping(name);
        }
    }

    private Class<?> getResolvedType(String s) throws NoSuchBeanDefinitionException {
        final Class aClass = resolveImplClass(s);
        return aClass != null && candidateEvaluator.isBean(aClass) ? aClass : null;
    }

    private static class CacheMiss {
    }

    private Class resolveImplClass(final String beanName) {
        Class aClass = cachedConventionResolutions.get(beanName);
        if (aClass != null && !CacheMiss.class.equals(aClass))
            return aClass;
        aClass = nameToClassResolver.resolveBean(beanName, candidateEvaluator);
        cachedConventionResolutions.put(beanName, aClass != null ? aClass : CacheMiss.class);
        return aClass;
    }

    private Class getCacheEntry(Class key) {
        return cachedConventionResolutions.get(beanNameFromClass(key));
    }

    private boolean isCacheMiss(Class cacheResult) {
        return CacheMiss.class.equals(cacheResult);
    }

    private Class resolveClass(final Class beanClass) {
        return resolveImplClass(beanNameFromClass(beanClass));
    }

    private String beanNameFromClass(final Class beanClass) {
        return beanClass.getName();
    }

    private Object instantiate(Class aClass) throws BeansException {
        return doGetBean(aClass.getName(), null, null, false);
    }

    private AnnotatedBeanDefinition getOrCreateBeanDefinition(String beanName, Class<?> resolvedType) {
        AnnotatedBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            beanDefinition = createScannedBeanDefinition(resolvedType);
            beanDefinitionMap.put(beanName, beanDefinition);
        }
        return beanDefinition;
    }

    private ScannedGenericBeanDefinition createScannedBeanDefinition(Class<?> resolvedType) {
        final ScannedGenericBeanDefinition rootBeanDefinition = getScannedBeanDefinition(resolvedType);
        rootBeanDefinition.applyDefaults(this.beanDefinitionDefaults);
        processCommonDefinitionAnnotations(rootBeanDefinition);
        rootBeanDefinition.setScope(getAnnotatedScope(resolvedType));
        return rootBeanDefinition;
    }

    /*on beanDefinition = beanDefinitionMap.get(beanName);
       if (beanDefinition != null) return beanDefinition;
        
       final ScannedGenericBeanDefinition rootBeanDefinition = getScannedBeanDefinition(type);
       rootBeanDefinition.applyDefaults(this.beanDefinitionDefaults);
       processCommonDefinitionAnnotations(rootBeanDefinition);
       rootBeanDefinition.setScope(getAnnotatedScope(type));
       beanDefinitionMap.put(type, rootBeanDefinition);
       return rootBeanDefinition;
    } */

    private ScannedGenericBeanDefinition getScannedBeanDefinition(Class clazz) {
        try {
            MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(clazz.getName());
            return new ScannedGenericBeanDefinition(metadataReader);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void registerBeanByResolvedType(String beanName, Class<?> resolvedType) {
        if (resolvedType == null)
            return;
        final BeanDefinition beanDefinition = getOrCreateBeanDefinition(beanName, resolvedType);
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(beanDefinition);

        ScopedProxyMode scopedProxyMode = scopeMetadata.getScopedProxyMode();
        if (!scopedProxyMode.equals(ScopedProxyMode.NO)) {
            definitionHolder = ScopedProxyUtils.createScopedProxy(definitionHolder, this,
                    scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS));
        }
        registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    }

    // Ripped from ActionConfigUtils
    static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
        if (abd.getMetadata().isAnnotated(Primary.class.getName())) {
            abd.setPrimary(true);
        }
        if (abd.getMetadata().isAnnotated(Lazy.class.getName())) {
            Boolean value = (Boolean) abd.getMetadata().getAnnotationAttributes(Lazy.class.getName()).get("value");
            abd.setLazyInit(value);
        }
        if (abd.getMetadata().isAnnotated(DependsOn.class.getName())) {
            String[] value = (String[]) abd.getMetadata().getAnnotationAttributes(DependsOn.class.getName())
                    .get("value");
            abd.setDependsOn(value);
        }
    }

    public AutowireCandidateResolver getAutowireCandidateResolver() {
        return this.qualifierAnnotationAutowireCandidateResolver;
    }

    protected Map<String, Object> findAutowireCandidates(String beanName, Class requiredType,
            DependencyDescriptor descriptor) {

        //        System.out.println("beanName = " + beanName + ", requiredType" + requiredType);
        String[] candidateNames = getCandidateNames(requiredType, descriptor);

        Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
        for (Class autowiringType : this.resolvableDependenciesLocalCache.keySet()) {
            if (autowiringType.isAssignableFrom(requiredType)) {
                Object autowiringValue = this.resolvableDependenciesLocalCache.get(autowiringType);
                autowiringValue = resolveAutowiringValue(autowiringValue, requiredType);
                if (requiredType.isInstance(autowiringValue)) {
                    result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                    break;
                }
            }
        }
        for (String candidateName : candidateNames) {
            if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, descriptor)) {
                result.put(candidateName, getBean(candidateName));
            }
        }
        return result;
    }

    public void registerResolvableDependency(Class dependencyType, Object autowiredValue) {
        Assert.notNull(dependencyType, "Type must not be null");
        this.resolvableDependenciesLocalCache.put(dependencyType, autowiredValue);
        super.registerResolvableDependency(dependencyType, autowiredValue);
    }

    private final ConcurrentHashMap<Class, String[]> typeCache = new ConcurrentHashMap<Class, String[]>();

    private String[] getCandidateNames(Class requiredType, DependencyDescriptor descriptor) {
        String[] strings = typeCache.get(requiredType);
        if (strings != null) {
            return strings;
        }
        strings = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true,
                descriptor.isEager());
        typeCache.put(requiredType, strings);
        return strings;
    }

    /**
     * Resolve the given autowiring value against the given required type,
     * e.g. an {@link org.springframework.beans.factory.ObjectFactory} value to its actual object result.
     *
     * @param autowiringValue the value to resolve
     * @param requiredType    the type to assign the result to
     * @return the resolved value
     */
    public static Object resolveAutowiringValue(Object autowiringValue, Class requiredType) {
        if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
            ObjectFactory factory = (ObjectFactory) autowiringValue;
            if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
                autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
                        new Class[] { requiredType }, new ObjectFactoryDelegatingInvocationHandler(factory));
            } else {
                return factory.getObject();
            }
        }
        return autowiringValue;
    }

    protected void resetBeanDefinition(String beanName) {
        clearTypeBasedCaches();
        super.resetBeanDefinition(beanName);
    }

    /**
     * Reflective InvocationHandler for lazy access to the current target object.
     */
    private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {

        private final ObjectFactory objectFactory;

        public ObjectFactoryDelegatingInvocationHandler(ObjectFactory objectFactory) {
            this.objectFactory = objectFactory;
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if (methodName.equals("equals")) {
                // Only consider equal when proxies are identical.
                return (proxy == args[0]);
            } else if (methodName.equals("hashCode")) {
                // Use hashCode of proxy.
                return System.identityHashCode(proxy);
            } else if (methodName.equals("toString")) {
                return this.objectFactory.toString();
            }
            try {
                return method.invoke(this.objectFactory.getObject(), args);
            } catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }
    }

}