Save This Page
Home » spring-framework-2.5.5-with-dependencies » org.springframework » beans » factory » config » [javadoc | source]
    1   /*
    2    * Copyright 2002-2008 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.beans.factory.config;
   18   
   19   import org.springframework.beans.BeanWrapper;
   20   import org.springframework.beans.BeansException;
   21   import org.springframework.beans.PropertyAccessorFactory;
   22   import org.springframework.beans.factory.BeanFactory;
   23   import org.springframework.beans.factory.BeanFactoryAware;
   24   import org.springframework.beans.factory.BeanFactoryUtils;
   25   import org.springframework.beans.factory.BeanNameAware;
   26   import org.springframework.beans.factory.FactoryBean;
   27   import org.springframework.util.StringUtils;
   28   
   29   /**
   30    * {@link FactoryBean} that evaluates a property path on a given target object.
   31    * 
   32    * <p>The target object can be specified directly or via a bean name.
   33    *
   34    * <p>Usage examples:
   35    *
   36    * <pre class="code">&lt;!-- target bean to be referenced by name --&gt;
   37    * &lt;bean id="tb" class="org.springframework.beans.TestBean" singleton="false"&gt;
   38    *   &lt;property name="age" value="10"/&gt;
   39    *   &lt;property name="spouse"&gt;
   40    *     &lt;bean class="org.springframework.beans.TestBean"&gt;
   41    *       &lt;property name="age" value="11"/&gt;
   42    *     &lt;/bean&gt;
   43    *   &lt;/property&gt;
   44    * &lt;/bean&gt;
   45    *
   46    * &lt;!-- will result in 12, which is the value of property 'age' of the inner bean --&gt;
   47    * &lt;bean id="propertyPath1" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"&gt;
   48    *   &lt;property name="targetObject"&gt;
   49    *     &lt;bean class="org.springframework.beans.TestBean"&gt;
   50    *       &lt;property name="age" value="12"/&gt;
   51    *     &lt;/bean&gt;
   52    *   &lt;/property&gt;
   53    *   &lt;property name="propertyPath" value="age"/&gt;
   54    * &lt;/bean&gt;
   55    *
   56    * &lt;!-- will result in 11, which is the value of property 'spouse.age' of bean 'tb' --&gt;
   57    * &lt;bean id="propertyPath2" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"&gt;
   58    *   &lt;property name="targetBeanName" value="tb"/&gt;
   59    *   &lt;property name="propertyPath" value="spouse.age"/&gt;
   60    * &lt;/bean&gt;
   61    *
   62    * &lt;!-- will result in 10, which is the value of property 'age' of bean 'tb' --&gt;
   63    * &lt;bean id="tb.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/&gt;</pre>
   64    * 
   65    * <p>If you are using Spring 2.0 and XML Schema support in your configuration file(s),
   66    * you can also use the following style of configuration for property path access.
   67    * (See also the appendix entitled 'XML Schema-based configuration' in the Spring
   68    * reference manual for more examples.)
   69    * 
   70    * <pre class="code"> &lt;!-- will result in 10, which is the value of property 'age' of bean 'tb' --&gt;
   71    * &lt;util:property-path id="name" path="testBean.age"/&gt;</pre>
   72    *
   73    * Thanks to Matthias Ernst for the suggestion and initial prototype!
   74    *
   75    * @author Juergen Hoeller
   76    * @since 1.1.2
   77    * @see #setTargetObject
   78    * @see #setTargetBeanName
   79    * @see #setPropertyPath
   80    */
   81   public class PropertyPathFactoryBean implements FactoryBean, BeanNameAware, BeanFactoryAware {
   82   
   83   	private BeanWrapper targetBeanWrapper;
   84   
   85   	private String targetBeanName;
   86   
   87   	private String propertyPath;
   88   
   89   	private Class resultType;
   90   
   91   	private String beanName;
   92   
   93   	private BeanFactory beanFactory;
   94   
   95   
   96   	/**
   97   	 * Specify a target object to apply the property path to.
   98   	 * Alternatively, specify a target bean name.
   99   	 * @param targetObject a target object, for example a bean reference
  100   	 * or an inner bean
  101   	 * @see #setTargetBeanName
  102   	 */
  103   	public void setTargetObject(Object targetObject) {
  104   		this.targetBeanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(targetObject);
  105   	}
  106   
  107   	/**
  108   	 * Specify the name of a target bean to apply the property path to.
  109   	 * Alternatively, specify a target object directly.
  110   	 * @param targetBeanName the bean name to be looked up in the
  111   	 * containing bean factory (e.g. "testBean")
  112   	 * @see #setTargetObject
  113   	 */
  114   	public void setTargetBeanName(String targetBeanName) {
  115   		this.targetBeanName = StringUtils.trimAllWhitespace(targetBeanName);
  116   	}
  117   
  118   	/**
  119   	 * Specify the property path to apply to the target.
  120   	 * @param propertyPath the property path, potentially nested
  121   	 * (e.g. "age" or "spouse.age")
  122   	 */
  123   	public void setPropertyPath(String propertyPath) {
  124   		this.propertyPath = StringUtils.trimAllWhitespace(propertyPath);
  125   	}
  126   
  127   	/**
  128   	 * Specify the type of the result from evaluating the property path.
  129   	 * <p>Note: This is not necessary for directly specified target objects
  130   	 * or singleton target beans, where the type can be determined through
  131   	 * introspection. Just specify this in case of a prototype target,
  132   	 * provided that you need matching by type (for example, for autowiring).
  133   	 * @param resultType the result type, for example "java.lang.Integer"
  134   	 */
  135   	public void setResultType(Class resultType) {
  136   		this.resultType = resultType;
  137   	}
  138   
  139   	/**
  140   	 * The bean name of this PropertyPathFactoryBean will be interpreted
  141   	 * as "beanName.property" pattern, if neither "targetObject" nor
  142   	 * "targetBeanName" nor "propertyPath" have been specified.
  143   	 * This allows for concise bean definitions with just an id/name.
  144   	 */
  145   	public void setBeanName(String beanName) {
  146   		this.beanName = StringUtils.trimAllWhitespace(BeanFactoryUtils.originalBeanName(beanName));
  147   	}
  148   
  149   
  150   	public void setBeanFactory(BeanFactory beanFactory) {
  151   		this.beanFactory = beanFactory;
  152   
  153   		if (this.targetBeanWrapper != null && this.targetBeanName != null) {
  154   			throw new IllegalArgumentException("Specify either 'targetObject' or 'targetBeanName', not both");
  155   		}
  156   
  157   		if (this.targetBeanWrapper == null && this.targetBeanName == null) {
  158   			if (this.propertyPath != null) {
  159   				throw new IllegalArgumentException(
  160   				    "Specify 'targetObject' or 'targetBeanName' in combination with 'propertyPath'");
  161   			}
  162   
  163   			// No other properties specified: check bean name.
  164   			int dotIndex = this.beanName.indexOf('.');
  165   			if (dotIndex == -1) {
  166   				throw new IllegalArgumentException(
  167   				    "Neither 'targetObject' nor 'targetBeanName' specified, and PropertyPathFactoryBean " +
  168   				    "bean name '" + this.beanName + "' does not follow 'beanName.property' syntax");
  169   			}
  170   			this.targetBeanName = this.beanName.substring(0, dotIndex);
  171   			this.propertyPath = this.beanName.substring(dotIndex + 1);
  172   		}
  173   
  174   		else if (this.propertyPath == null) {
  175   			// either targetObject or targetBeanName specified
  176   			throw new IllegalArgumentException("'propertyPath' is required");
  177   		}
  178   
  179   		if (this.targetBeanWrapper == null && this.beanFactory.isSingleton(this.targetBeanName)) {
  180   			// Eagerly fetch singleton target bean, and determine result type.
  181   			Object bean = this.beanFactory.getBean(this.targetBeanName);
  182   			this.targetBeanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean);
  183   			this.resultType = this.targetBeanWrapper.getPropertyType(this.propertyPath);
  184   		}
  185   	}
  186   
  187   
  188   	public Object getObject() throws BeansException {
  189   		BeanWrapper target = this.targetBeanWrapper;
  190   		if (target == null) {
  191   			// Fetch prototype target bean...
  192   			Object bean = this.beanFactory.getBean(this.targetBeanName);
  193   			target = PropertyAccessorFactory.forBeanPropertyAccess(bean);
  194   		}
  195   		return target.getPropertyValue(this.propertyPath);
  196   	}
  197   
  198   	public Class getObjectType() {
  199   		return this.resultType;
  200   	}
  201   
  202   	/**
  203   	 * While this FactoryBean will often be used for singleton targets,
  204   	 * the invoked getters for the property path might return a new object
  205   	 * for each call, so we have to assume that we're not returning the
  206   	 * same object for each {@link #getObject()} call.
  207   	 */
  208   	public boolean isSingleton() {
  209   		return false;
  210   	}
  211   
  212   }

Save This Page
Home » spring-framework-2.5.5-with-dependencies » org.springframework » beans » factory » config » [javadoc | source]