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.annotation;
18
19 import java.beans.PropertyDescriptor;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Member;
23 import java.lang.reflect.Method;
24 import java.util.Arrays;
25 import java.util.Iterator;
26 import java.util.LinkedHashSet;
27 import java.util.Set;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31
32 import org.springframework.beans.MutablePropertyValues;
33 import org.springframework.beans.PropertyValues;
34 import org.springframework.beans.factory.support.RootBeanDefinition;
35 import org.springframework.util.ReflectionUtils;
36
37 /**
38 * Internal class for managing injection metadata.
39 * Not intended for direct use in applications.
40 *
41 * <p>Used by {@link AutowiredAnnotationBeanPostProcessor},
42 * {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor} and
43 * {@link org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor}.
44 *
45 * @author Juergen Hoeller
46 * @since 2.5
47 */
48 public class InjectionMetadata {
49
50 private final Log logger = LogFactory.getLog(InjectionMetadata.class);
51
52 private String targetClassName;
53
54 private final Set<InjectedElement> injectedFields = new LinkedHashSet<InjectedElement>();
55
56 private final Set<InjectedElement> injectedMethods = new LinkedHashSet<InjectedElement>();
57
58
59 public InjectionMetadata() {
60 }
61
62 public InjectionMetadata(Class targetClass) {
63 this.targetClassName = targetClass.getName();
64 }
65
66
67 public void addInjectedField(InjectedElement element) {
68 if (logger.isDebugEnabled()) {
69 logger.debug("Found injected field on class [" + this.targetClassName + "]: " + element);
70 }
71 this.injectedFields.add(element);
72 }
73
74 public void addInjectedMethod(InjectedElement element) {
75 if (logger.isDebugEnabled()) {
76 logger.debug("Found injected method on class [" + this.targetClassName + "]: " + element);
77 }
78 this.injectedMethods.add(element);
79 }
80
81 public void checkConfigMembers(RootBeanDefinition beanDefinition) {
82 doRegisterConfigMembers(beanDefinition, this.injectedFields);
83 doRegisterConfigMembers(beanDefinition, this.injectedMethods);
84 }
85
86 private void doRegisterConfigMembers(RootBeanDefinition beanDefinition, Set<InjectedElement> members) {
87 for (Iterator<InjectedElement> it = members.iterator(); it.hasNext();) {
88 Member member = it.next().getMember();
89 if (!beanDefinition.isExternallyManagedConfigMember(member)) {
90 beanDefinition.registerExternallyManagedConfigMember(member);
91 }
92 else {
93 it.remove();
94 }
95 }
96 }
97
98 public void injectFields(Object target, String beanName) throws Throwable {
99 if (!this.injectedFields.isEmpty()) {
100 boolean debug = logger.isDebugEnabled();
101 for (InjectedElement element : this.injectedFields) {
102 if (debug) {
103 logger.debug("Processing injected field of bean '" + beanName + "': " + element);
104 }
105 element.inject(target, beanName, null);
106 }
107 }
108 }
109
110 public void injectMethods(Object target, String beanName, PropertyValues pvs) throws Throwable {
111 if (!this.injectedMethods.isEmpty()) {
112 boolean debug = logger.isDebugEnabled();
113 for (InjectedElement element : this.injectedMethods) {
114 if (debug) {
115 logger.debug("Processing injected method of bean '" + beanName + "': " + element);
116 }
117 element.inject(target, beanName, pvs);
118 }
119 }
120 }
121
122
123 public static abstract class InjectedElement {
124
125 protected final Member member;
126
127 protected final boolean isField;
128
129 protected final PropertyDescriptor pd;
130
131 protected volatile Boolean skip;
132
133 protected InjectedElement(Member member, PropertyDescriptor pd) {
134 this.member = member;
135 this.isField = (member instanceof Field);
136 this.pd = pd;
137 }
138
139 public final Member getMember() {
140 return this.member;
141 }
142
143 protected final Class getResourceType() {
144 if (this.isField) {
145 return ((Field) this.member).getType();
146 }
147 else if (this.pd != null) {
148 return this.pd.getPropertyType();
149 }
150 else {
151 return ((Method) this.member).getParameterTypes()[0];
152 }
153 }
154
155 protected final void checkResourceType(Class resourceType) {
156 if (this.isField) {
157 Class fieldType = ((Field) this.member).getType();
158 if (!(resourceType.isAssignableFrom(fieldType) || fieldType.isAssignableFrom(resourceType))) {
159 throw new IllegalStateException("Specified field type [" + fieldType +
160 "] is incompatible with resource type [" + resourceType.getName() + "]");
161 }
162 }
163 else {
164 Class paramType =
165 (this.pd != null ? this.pd.getPropertyType() : ((Method) this.member).getParameterTypes()[0]);
166 if (!(resourceType.isAssignableFrom(paramType) || paramType.isAssignableFrom(resourceType))) {
167 throw new IllegalStateException("Specified parameter type [" + paramType +
168 "] is incompatible with resource type [" + resourceType.getName() + "]");
169 }
170 }
171 }
172
173 /**
174 * Either this or {@link #getResourceToInject} needs to be overridden.
175 */
176 protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable {
177 if (this.isField) {
178 Field field = (Field) this.member;
179 ReflectionUtils.makeAccessible(field);
180 field.set(target, getResourceToInject(target, requestingBeanName));
181 }
182 else {
183 if (this.skip == null) {
184 this.skip = Boolean.valueOf(checkPropertySkipping(pvs));
185 }
186 if (this.skip.booleanValue()) {
187 return;
188 }
189 try {
190 Method method = (Method) this.member;
191 ReflectionUtils.makeAccessible(method);
192 method.invoke(target, getResourceToInject(target, requestingBeanName));
193 }
194 catch (InvocationTargetException ex) {
195 throw ex.getTargetException();
196 }
197 }
198 }
199
200 /**
201 * Checks whether this injector's property needs to be skipped due to
202 * an explicit property value having been specified. Also marks the
203 * affected property as processed for other processors to ignore it.
204 */
205 protected boolean checkPropertySkipping(PropertyValues pvs) {
206 if (this.pd != null && pvs != null) {
207 if (pvs.contains(this.pd.getName())) {
208 // Explicit value provided as part of the bean definition.
209 return true;
210 }
211 else if (pvs instanceof MutablePropertyValues) {
212 ((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName());
213 }
214 }
215 return false;
216 }
217
218 /**
219 * Either this or {@link #inject} needs to be overridden.
220 */
221 protected Object getResourceToInject(Object target, String requestingBeanName) {
222 return null;
223 }
224
225 public boolean equals(Object other) {
226 if (this == other) {
227 return true;
228 }
229 if (!(other instanceof InjectedElement)) {
230 return false;
231 }
232 InjectedElement otherElement = (InjectedElement) other;
233 if (this.isField) {
234 return this.member.equals(otherElement.member);
235 }
236 else {
237 return (otherElement.member instanceof Method &&
238 this.member.getName().equals(otherElement.member.getName()) &&
239 Arrays.equals(((Method) this.member).getParameterTypes(),
240 ((Method) otherElement.member).getParameterTypes()));
241 }
242 }
243
244 public int hashCode() {
245 return this.member.getClass().hashCode() * 29 + this.member.getName().hashCode();
246 }
247
248 public String toString() {
249 return getClass().getSimpleName() + " for " + this.member;
250 }
251 }
252
253 }