/* * 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"); } } }