001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.beans.factory.config; 018 019import java.lang.reflect.Field; 020 021import org.springframework.beans.factory.BeanClassLoaderAware; 022import org.springframework.beans.factory.BeanFactoryUtils; 023import org.springframework.beans.factory.BeanNameAware; 024import org.springframework.beans.factory.FactoryBean; 025import org.springframework.beans.factory.FactoryBeanNotInitializedException; 026import org.springframework.beans.factory.InitializingBean; 027import org.springframework.util.ClassUtils; 028import org.springframework.util.ReflectionUtils; 029import org.springframework.util.StringUtils; 030 031/** 032 * {@link FactoryBean} which retrieves a static or non-static field value. 033 * 034 * <p>Typically used for retrieving public static final constants. Usage example: 035 * 036 * <pre class="code">// standard definition for exposing a static field, specifying the "staticField" property 037 * <bean id="myField" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"> 038 * <property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/> 039 * </bean> 040 * 041 * // convenience version that specifies a static field pattern as bean name 042 * <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE" 043 * class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/></pre> 044 * </pre> 045 * 046 * <p>If you are using Spring 2.0, you can also use the following style of configuration for 047 * public static fields. 048 * 049 * <pre class="code"><util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/></pre> 050 * 051 * @author Juergen Hoeller 052 * @since 1.1 053 * @see #setStaticField 054 */ 055public class FieldRetrievingFactoryBean 056 implements FactoryBean<Object>, BeanNameAware, BeanClassLoaderAware, InitializingBean { 057 058 private Class<?> targetClass; 059 060 private Object targetObject; 061 062 private String targetField; 063 064 private String staticField; 065 066 private String beanName; 067 068 private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); 069 070 // the field we will retrieve 071 private Field fieldObject; 072 073 074 /** 075 * Set the target class on which the field is defined. 076 * Only necessary when the target field is static; else, 077 * a target object needs to be specified anyway. 078 * @see #setTargetObject 079 * @see #setTargetField 080 */ 081 public void setTargetClass(Class<?> targetClass) { 082 this.targetClass = targetClass; 083 } 084 085 /** 086 * Return the target class on which the field is defined. 087 */ 088 public Class<?> getTargetClass() { 089 return targetClass; 090 } 091 092 /** 093 * Set the target object on which the field is defined. 094 * Only necessary when the target field is not static; 095 * else, a target class is sufficient. 096 * @see #setTargetClass 097 * @see #setTargetField 098 */ 099 public void setTargetObject(Object targetObject) { 100 this.targetObject = targetObject; 101 } 102 103 /** 104 * Return the target object on which the field is defined. 105 */ 106 public Object getTargetObject() { 107 return this.targetObject; 108 } 109 110 /** 111 * Set the name of the field to be retrieved. 112 * Refers to either a static field or a non-static field, 113 * depending on a target object being set. 114 * @see #setTargetClass 115 * @see #setTargetObject 116 */ 117 public void setTargetField(String targetField) { 118 this.targetField = StringUtils.trimAllWhitespace(targetField); 119 } 120 121 /** 122 * Return the name of the field to be retrieved. 123 */ 124 public String getTargetField() { 125 return this.targetField; 126 } 127 128 /** 129 * Set a fully qualified static field name to retrieve, 130 * e.g. "example.MyExampleClass.MY_EXAMPLE_FIELD". 131 * Convenient alternative to specifying targetClass and targetField. 132 * @see #setTargetClass 133 * @see #setTargetField 134 */ 135 public void setStaticField(String staticField) { 136 this.staticField = StringUtils.trimAllWhitespace(staticField); 137 } 138 139 /** 140 * The bean name of this FieldRetrievingFactoryBean will be interpreted 141 * as "staticField" pattern, if neither "targetClass" nor "targetObject" 142 * nor "targetField" have been specified. 143 * This allows for concise bean definitions with just an id/name. 144 */ 145 @Override 146 public void setBeanName(String beanName) { 147 this.beanName = StringUtils.trimAllWhitespace(BeanFactoryUtils.originalBeanName(beanName)); 148 } 149 150 @Override 151 public void setBeanClassLoader(ClassLoader classLoader) { 152 this.beanClassLoader = classLoader; 153 } 154 155 156 @Override 157 public void afterPropertiesSet() throws ClassNotFoundException, NoSuchFieldException { 158 if (this.targetClass != null && this.targetObject != null) { 159 throw new IllegalArgumentException("Specify either targetClass or targetObject, not both"); 160 } 161 162 if (this.targetClass == null && this.targetObject == null) { 163 if (this.targetField != null) { 164 throw new IllegalArgumentException( 165 "Specify targetClass or targetObject in combination with targetField"); 166 } 167 168 // If no other property specified, consider bean name as static field expression. 169 if (this.staticField == null) { 170 this.staticField = this.beanName; 171 } 172 173 // Try to parse static field into class and field. 174 int lastDotIndex = this.staticField.lastIndexOf('.'); 175 if (lastDotIndex == -1 || lastDotIndex == this.staticField.length()) { 176 throw new IllegalArgumentException( 177 "staticField must be a fully qualified class plus static field name: " + 178 "e.g. 'example.MyExampleClass.MY_EXAMPLE_FIELD'"); 179 } 180 String className = this.staticField.substring(0, lastDotIndex); 181 String fieldName = this.staticField.substring(lastDotIndex + 1); 182 this.targetClass = ClassUtils.forName(className, this.beanClassLoader); 183 this.targetField = fieldName; 184 } 185 186 else if (this.targetField == null) { 187 // Either targetClass or targetObject specified. 188 throw new IllegalArgumentException("targetField is required"); 189 } 190 191 // Try to get the exact method first. 192 Class<?> targetClass = (this.targetObject != null) ? this.targetObject.getClass() : this.targetClass; 193 this.fieldObject = targetClass.getField(this.targetField); 194 } 195 196 197 @Override 198 public Object getObject() throws IllegalAccessException { 199 if (this.fieldObject == null) { 200 throw new FactoryBeanNotInitializedException(); 201 } 202 ReflectionUtils.makeAccessible(this.fieldObject); 203 if (this.targetObject != null) { 204 // instance field 205 return this.fieldObject.get(this.targetObject); 206 } 207 else { 208 // class field 209 return this.fieldObject.get(null); 210 } 211 } 212 213 @Override 214 public Class<?> getObjectType() { 215 return (this.fieldObject != null ? this.fieldObject.getType() : null); 216 } 217 218 @Override 219 public boolean isSingleton() { 220 return false; 221 } 222 223}