org.springframework.beans.factory.support.DependencyInjectionAspectSupport.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.beans.factory.support.DependencyInjectionAspectSupport.java

Source

/*
 * Copyright 2002-2004 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.
 */

package org.springframework.beans.factory.support;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.propertyeditors.ClassEditor;

/**
 * Convenient superclass for aspects/persistence API
 * configuration classes that should be able to autowire
 * objects into a factory.
 * <p>
 * There are two ways of doing this: by mapping managed classes to prototype bean definitions
 * in the factory; and by identifying classes of which instances should be autowired into the factory
 * using the autowiring capables of AutowireCapableBeanFactory. If your managed class implements
 * Spring lifecycle interfaces such as BeanFactoryAware or ApplicationContextAware, you must use the
 * former method. With the latter method, only properties will be set, based on automatic satisfaction
 * of dependencies from other beans (singleton or non-singleton) defined in the factory. 
 * 
 * <p>Could also use attributes on persistent classes to identify those eligible for autowiring, or
 * even the prototype bean name.
 * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory
 * 
 * @since 1.2
 * @author Rod Johnson
 */
public abstract class DependencyInjectionAspectSupport implements InitializingBean, BeanFactoryAware {

    protected final Log log = LogFactory.getLog(getClass());

    private BeanFactory beanFactory;

    private AutowireCapableBeanFactory aabf;

    /**
     * Map of Class to prototype name
     */
    private Map managedClassToPrototypeMap = new HashMap();

    private int defaultAutowireMode = 0;

    /**
     * List of Class
     */
    protected List autowireByTypeClasses = new LinkedList();

    /**
     * List of Class
     */
    private List autowireByNameClasses = new LinkedList();

    /**
     * @return Returns the autowireAll.
     */
    public int getDefaultAutowireMode() {
        return defaultAutowireMode;
    }

    /**
     * Convenient property enabling autowiring of all instances. We might want this in an
     * AspectJ aspect subclass, for example, relying on the AspectJ aspect to target the
     * advice.
     * @param autowireAll The autowireAll to set.
     */
    public void setDefaultAutowireMode(int mode) {
        if (mode != 0 && mode != AutowireCapableBeanFactory.AUTOWIRE_BY_NAME
                && mode != AbstractAutowireCapableBeanFactory.AUTOWIRE_BY_TYPE) {
            throw new IllegalArgumentException(
                    "defaultAutowireMode must be a constant on AutowireCapableBeanFactory: AUTOWIRE_BY_TYPE or AUTOWIRE_BY_NAME");
        }
        defaultAutowireMode = mode;
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        if (beanFactory instanceof AutowireCapableBeanFactory) {
            this.aabf = (AutowireCapableBeanFactory) beanFactory;
        }
    }

    /**
     * Expose the owning bean factory to subclasses. 
     * Call only after initialization is complete.
     * @return the owning bean factory. Cannot be null in valid use of this class.
     */
    protected BeanFactory getBeanFactory() {
        return this.beanFactory;
    }

    /**
     * Set the Classes or class names that will be autowired by type
     * @param autowireByTypeClasses list of Class or String classname
     */
    public void setAutowireByTypeClasses(List autowireByTypeClasses) {
        this.autowireByTypeClasses = convertListFromStringsToClassesIfNecessary(autowireByTypeClasses);
    }

    /**
     * Return classes autowired by type
     * @return list of Class
     */
    public List getAutowireByTypeClasses() {
        return autowireByTypeClasses;
    }

    public void addAutowireByTypeClass(Class clazz) {
        this.autowireByTypeClasses.add(clazz);
    }

    /**
     * Set the Classes or class names that will be autowired by name
     * @param autowireByNameClasses list of Class or String classname
     */
    public void setAutowireByNameClasses(List autowireByNameClasses) {
        this.autowireByNameClasses = convertListFromStringsToClassesIfNecessary(autowireByNameClasses);
    }

    /**
     * Return classes autowired by name
     * @return list of Class
     */
    public List getAutowireByNameClasses() {
        return autowireByNameClasses;
    }

    public void addAutowireByNameClass(Class clazz) {
        this.autowireByNameClasses.add(clazz);
    }

    /**
     * Property key is class FQN, value is prototype name to use to obtain a new instance
     * @param persistentClassBeanNames
     */
    public void setManagedClassNamesToPrototypeNames(Properties persistentClassBeanNames) {
        for (Iterator i = persistentClassBeanNames.keySet().iterator(); i.hasNext();) {
            String className = (String) i.next();
            String beanName = persistentClassBeanNames.getProperty(className);
            addManagedClassToPrototypeMapping(classNameStringToClass(className), beanName);
        }
    }

    /**
     * Utility method to convert a collection from a list of String class name to a list of Classes
     * @param l list which may contain Class or String
     * @return list of resolved Class instances
     */
    private List convertListFromStringsToClassesIfNecessary(List l) {
        List classes = new ArrayList(l.size());
        for (Iterator itr = l.iterator(); itr.hasNext();) {
            Object next = itr.next();
            if (next instanceof String) {
                next = classNameStringToClass((String) next);
            }
            classes.add(next);
        }
        return classes;
    }

    /**
     * Resolve this FQN
     * @param className name of the class to resolve
     * @return the Class
     */
    private Class classNameStringToClass(String className) {
        ClassEditor ce = new ClassEditor();
        ce.setAsText(className);
        return (Class) ce.getValue();
    }

    /**
     * Return a Map of managed classes to prototype names
     * @return Map with key being FQN and value prototype bean name to use for that class
     */
    public Map getManagedClassToPrototypeNames() {
        return this.managedClassToPrototypeMap;
    }

    public void addManagedClassToPrototypeMapping(Class clazz, String beanName) {
        this.managedClassToPrototypeMap.put(clazz, beanName);
    }

    /**
     * Check that mandatory properties were set
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public final void afterPropertiesSet() {
        if (beanFactory == null) {
            throw new IllegalArgumentException("beanFactory is required");
        }

        validateConfiguration();
        validateProperties();
    }

    /**
     * Subclasses should implement this to validate their configuration    
     */
    protected abstract void validateProperties();

    protected void validateConfiguration() {
        if (managedClassToPrototypeMap.isEmpty() && autowireByTypeClasses.isEmpty()
                && autowireByNameClasses.isEmpty() && defaultAutowireMode == 0) {
            throw new IllegalArgumentException(
                    "Must set persistent class information: no managed classes configured and no autowiring configuration or defaults");
        }

        if ((defaultAutowireMode != 0 || !autowireByTypeClasses.isEmpty() || !autowireByNameClasses.isEmpty())
                && aabf == null) {
            throw new IllegalArgumentException(
                    "Autowiring supported only when running in an AutowireCapableBeanFactory");
        }

        // Check that all persistent classes map to prototype definitions
        for (Iterator itr = managedClassToPrototypeMap.keySet().iterator(); itr.hasNext();) {
            String beanName = (String) managedClassToPrototypeMap.get(itr.next());
            if (!beanFactory.containsBean(beanName)) {
                throw new IllegalArgumentException("No bean with name '" + beanName + "' defined in factory");
            }
            if (beanFactory.isSingleton(beanName)) {
                throw new IllegalArgumentException(
                        "Bean name '" + beanName + "' must be a prototype, with singleton=\"false\"");
            }
        }
        log.info("Validated " + managedClassToPrototypeMap.size() + " persistent class to prototype mappings");
    }

    /**
     * Subclasses can call this to autowire properties on an existing object
     * @param o
     * @param autowireMode
     * @throws BeansException
     */
    protected void autowireProperties(Object o) throws NoAutowiringConfigurationForClassException, BeansException {
        if (autowireByTypeClasses.contains(o.getClass())) {
            aabf.autowireBeanProperties(o, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
        } else if (autowireByNameClasses.contains(o.getClass())) {
            aabf.autowireBeanProperties(o, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false);
        } else if (defaultAutowireMode == AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE) {
            aabf.autowireBeanProperties(o, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
        } else if (defaultAutowireMode == AutowireCapableBeanFactory.AUTOWIRE_BY_NAME) {
            aabf.autowireBeanProperties(o, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false);
        } else {
            throw new NoAutowiringConfigurationForClassException(o.getClass());
        }
        log.info("Autowired properties of persistent object with class=" + o.getClass().getName());
    }

    /**
     * Subclasses will call this to create an object of the requisite class
     * @param clazz
     * @return
     * @throws NoAutowiringConfigurationForClassException
     */
    protected Object createAndConfigure(Class clazz) throws NoAutowiringConfigurationForClassException {
        Object o = null;
        String name = (String) managedClassToPrototypeMap.get(clazz);
        if (name != null) {
            o = beanFactory.getBean(name);
        } else {
            // Fall back to trying autowiring         
            o = BeanUtils.instantiateClass(clazz);
            autowireProperties(o);
        }

        if (o == null) {
            throw new NoAutowiringConfigurationForClassException(clazz);
        } else {
            return o;
        }
    }

    protected class NoAutowiringConfigurationForClassException extends Exception {
        public NoAutowiringConfigurationForClassException(Class clazz) {
            super(clazz + " cannot be autowired");
        }
    }

}