InjectionMetadata.java 6.7 KB
Newer Older
1
/*
2
 * Copyright 2002-2011 the original author or authors.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * 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.annotation;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
24
import java.util.Collection;
25
import java.util.Collections;
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.util.ReflectionUtils;

/**
 * Internal class for managing injection metadata.
 * Not intended for direct use in applications.
 *
 * <p>Used by {@link AutowiredAnnotationBeanPostProcessor},
 * {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor} and
 * {@link org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor}.
 *
 * @author Juergen Hoeller
 * @since 2.5
 */
public class InjectionMetadata {

	private final Log logger = LogFactory.getLog(InjectionMetadata.class);

53
	private final Set<InjectedElement> injectedElements;
54 55


56
	public InjectionMetadata(Class targetClass, Collection<InjectedElement> elements) {
57
		this.injectedElements = Collections.synchronizedSet(new LinkedHashSet<InjectedElement>());
58 59 60 61 62
		for (InjectedElement element : elements) {
			if (logger.isDebugEnabled()) {
				logger.debug("Found injected element on class [" + targetClass.getName() + "]: " + element);
			}
			this.injectedElements.add(element);
63 64 65 66
		}
	}

	public void checkConfigMembers(RootBeanDefinition beanDefinition) {
67 68 69 70 71 72 73 74 75
		synchronized(this.injectedElements) {
			for (Iterator<InjectedElement> it = this.injectedElements.iterator(); it.hasNext();) {
				Member member = it.next().getMember();
				if (!beanDefinition.isExternallyManagedConfigMember(member)) {
					beanDefinition.registerExternallyManagedConfigMember(member);
				}
				else {
					it.remove();
				}
76 77 78 79
			}
		}
	}

80 81
	public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
		if (!this.injectedElements.isEmpty()) {
82
			boolean debug = logger.isDebugEnabled();
83
			for (InjectedElement element : this.injectedElements) {
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
				if (debug) {
					logger.debug("Processing injected method of bean '" + beanName + "': " + element);
				}
				element.inject(target, beanName, pvs);
			}
		}
	}


	public static abstract class InjectedElement {

		protected final Member member;

		protected final boolean isField;

		protected final PropertyDescriptor pd;

		protected volatile Boolean skip;

		protected InjectedElement(Member member, PropertyDescriptor pd) {
			this.member = member;
			this.isField = (member instanceof Field);
			this.pd = pd;
		}

		public final Member getMember() {
			return this.member;
		}

		protected final Class getResourceType() {
			if (this.isField) {
				return ((Field) this.member).getType();
			}
			else if (this.pd != null) {
				return this.pd.getPropertyType();
			}
			else {
				return ((Method) this.member).getParameterTypes()[0];
			}
		}

		protected final void checkResourceType(Class resourceType) {
			if (this.isField) {
				Class fieldType = ((Field) this.member).getType();
				if (!(resourceType.isAssignableFrom(fieldType) || fieldType.isAssignableFrom(resourceType))) {
					throw new IllegalStateException("Specified field type [" + fieldType +
							"] is incompatible with resource type [" + resourceType.getName() + "]");
				}
			}
			else {
				Class paramType =
						(this.pd != null ? this.pd.getPropertyType() : ((Method) this.member).getParameterTypes()[0]);
				if (!(resourceType.isAssignableFrom(paramType) || paramType.isAssignableFrom(resourceType))) {
					throw new IllegalStateException("Specified parameter type [" + paramType +
							"] is incompatible with resource type [" + resourceType.getName() + "]");
				}
			}
		}

		/**
		 * Either this or {@link #getResourceToInject} needs to be overridden.
		 */
		protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable {
			if (this.isField) {
				Field field = (Field) this.member;
				ReflectionUtils.makeAccessible(field);
				field.set(target, getResourceToInject(target, requestingBeanName));
			}
			else {
153
				if (checkPropertySkipping(pvs)) {
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
					return;
				}
				try {
					Method method = (Method) this.member;
					ReflectionUtils.makeAccessible(method);
					method.invoke(target, getResourceToInject(target, requestingBeanName));
				}
				catch (InvocationTargetException ex) {
					throw ex.getTargetException();
				}
			}
		}

		/**
		 * Checks whether this injector's property needs to be skipped due to
		 * an explicit property value having been specified. Also marks the
		 * affected property as processed for other processors to ignore it.
		 */
		protected boolean checkPropertySkipping(PropertyValues pvs) {
173
			if (this.skip == null) {
174 175 176 177 178 179 180 181 182 183 184 185
				if (pvs != null) {
					synchronized (pvs) {
						if (this.skip == null) {
							if (this.pd != null) {
								if (pvs.contains(this.pd.getName())) {
									// Explicit value provided as part of the bean definition.
									this.skip = true;
									return true;
								}
								else if (pvs instanceof MutablePropertyValues) {
									((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName());
								}
186 187 188
							}
						}
					}
189
				}
190
				this.skip = false;
191
			}
192
			return this.skip;
193 194 195 196 197 198 199 200 201
		}

		/**
		 * Either this or {@link #inject} needs to be overridden.
		 */
		protected Object getResourceToInject(Object target, String requestingBeanName) {
			return null;
		}

202
		@Override
203 204 205 206 207 208 209 210
		public boolean equals(Object other) {
			if (this == other) {
				return true;
			}
			if (!(other instanceof InjectedElement)) {
				return false;
			}
			InjectedElement otherElement = (InjectedElement) other;
211
			return this.member.equals(otherElement.member);
212 213
		}

214
		@Override
215 216 217 218
		public int hashCode() {
			return this.member.getClass().hashCode() * 29 + this.member.getName().hashCode();
		}

219
		@Override
220 221 222 223 224 225
		public String toString() {
			return getClass().getSimpleName() + " for " + this.member;
		}
	}

}