001/* 002 * Copyright 2002-2018 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.annotation; 018 019import java.beans.PropertyDescriptor; 020import java.lang.reflect.Field; 021import java.lang.reflect.InvocationTargetException; 022import java.lang.reflect.Member; 023import java.lang.reflect.Method; 024import java.util.Collection; 025import java.util.LinkedHashSet; 026import java.util.Set; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030 031import org.springframework.beans.MutablePropertyValues; 032import org.springframework.beans.PropertyValues; 033import org.springframework.beans.factory.support.RootBeanDefinition; 034import org.springframework.util.ReflectionUtils; 035 036/** 037 * Internal class for managing injection metadata. 038 * Not intended for direct use in applications. 039 * 040 * <p>Used by {@link AutowiredAnnotationBeanPostProcessor}, 041 * {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor} and 042 * {@link org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor}. 043 * 044 * @author Juergen Hoeller 045 * @since 2.5 046 */ 047public class InjectionMetadata { 048 049 private static final Log logger = LogFactory.getLog(InjectionMetadata.class); 050 051 private final Class<?> targetClass; 052 053 private final Collection<InjectedElement> injectedElements; 054 055 private volatile Set<InjectedElement> checkedElements; 056 057 058 public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) { 059 this.targetClass = targetClass; 060 this.injectedElements = elements; 061 } 062 063 064 public void checkConfigMembers(RootBeanDefinition beanDefinition) { 065 Set<InjectedElement> checkedElements = new LinkedHashSet<InjectedElement>(this.injectedElements.size()); 066 for (InjectedElement element : this.injectedElements) { 067 Member member = element.getMember(); 068 if (!beanDefinition.isExternallyManagedConfigMember(member)) { 069 beanDefinition.registerExternallyManagedConfigMember(member); 070 checkedElements.add(element); 071 if (logger.isDebugEnabled()) { 072 logger.debug("Registered injected element on class [" + this.targetClass.getName() + "]: " + element); 073 } 074 } 075 } 076 this.checkedElements = checkedElements; 077 } 078 079 public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable { 080 Collection<InjectedElement> elementsToIterate = 081 (this.checkedElements != null ? this.checkedElements : this.injectedElements); 082 if (!elementsToIterate.isEmpty()) { 083 for (InjectedElement element : elementsToIterate) { 084 if (logger.isDebugEnabled()) { 085 logger.debug("Processing injected element of bean '" + beanName + "': " + element); 086 } 087 element.inject(target, beanName, pvs); 088 } 089 } 090 } 091 092 /** 093 * @since 3.2.13 094 */ 095 public void clear(PropertyValues pvs) { 096 Collection<InjectedElement> elementsToIterate = 097 (this.checkedElements != null ? this.checkedElements : this.injectedElements); 098 if (!elementsToIterate.isEmpty()) { 099 for (InjectedElement element : elementsToIterate) { 100 element.clearPropertySkipping(pvs); 101 } 102 } 103 } 104 105 106 public static boolean needsRefresh(InjectionMetadata metadata, Class<?> clazz) { 107 return (metadata == null || metadata.targetClass != clazz); 108 } 109 110 111 /** 112 * A single injected element. 113 */ 114 public abstract static class InjectedElement { 115 116 protected final Member member; 117 118 protected final boolean isField; 119 120 protected final PropertyDescriptor pd; 121 122 protected volatile Boolean skip; 123 124 protected InjectedElement(Member member, PropertyDescriptor pd) { 125 this.member = member; 126 this.isField = (member instanceof Field); 127 this.pd = pd; 128 } 129 130 public final Member getMember() { 131 return this.member; 132 } 133 134 protected final Class<?> getResourceType() { 135 if (this.isField) { 136 return ((Field) this.member).getType(); 137 } 138 else if (this.pd != null) { 139 return this.pd.getPropertyType(); 140 } 141 else { 142 return ((Method) this.member).getParameterTypes()[0]; 143 } 144 } 145 146 protected final void checkResourceType(Class<?> resourceType) { 147 if (this.isField) { 148 Class<?> fieldType = ((Field) this.member).getType(); 149 if (!(resourceType.isAssignableFrom(fieldType) || fieldType.isAssignableFrom(resourceType))) { 150 throw new IllegalStateException("Specified field type [" + fieldType + 151 "] is incompatible with resource type [" + resourceType.getName() + "]"); 152 } 153 } 154 else { 155 Class<?> paramType = 156 (this.pd != null ? this.pd.getPropertyType() : ((Method) this.member).getParameterTypes()[0]); 157 if (!(resourceType.isAssignableFrom(paramType) || paramType.isAssignableFrom(resourceType))) { 158 throw new IllegalStateException("Specified parameter type [" + paramType + 159 "] is incompatible with resource type [" + resourceType.getName() + "]"); 160 } 161 } 162 } 163 164 /** 165 * Either this or {@link #getResourceToInject} needs to be overridden. 166 */ 167 protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable { 168 if (this.isField) { 169 Field field = (Field) this.member; 170 ReflectionUtils.makeAccessible(field); 171 field.set(target, getResourceToInject(target, requestingBeanName)); 172 } 173 else { 174 if (checkPropertySkipping(pvs)) { 175 return; 176 } 177 try { 178 Method method = (Method) this.member; 179 ReflectionUtils.makeAccessible(method); 180 method.invoke(target, getResourceToInject(target, requestingBeanName)); 181 } 182 catch (InvocationTargetException ex) { 183 throw ex.getTargetException(); 184 } 185 } 186 } 187 188 /** 189 * Check whether this injector's property needs to be skipped due to 190 * an explicit property value having been specified. Also marks the 191 * affected property as processed for other processors to ignore it. 192 */ 193 protected boolean checkPropertySkipping(PropertyValues pvs) { 194 if (this.skip != null) { 195 return this.skip; 196 } 197 if (pvs == null) { 198 this.skip = false; 199 return false; 200 } 201 synchronized (pvs) { 202 if (this.skip != null) { 203 return this.skip; 204 } 205 if (this.pd != null) { 206 if (pvs.contains(this.pd.getName())) { 207 // Explicit value provided as part of the bean definition. 208 this.skip = true; 209 return true; 210 } 211 else if (pvs instanceof MutablePropertyValues) { 212 ((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName()); 213 } 214 } 215 this.skip = false; 216 return false; 217 } 218 } 219 220 /** 221 * Clear property skipping for this element. 222 * @since 3.2.13 223 */ 224 protected void clearPropertySkipping(PropertyValues pvs) { 225 if (pvs == null) { 226 return; 227 } 228 synchronized (pvs) { 229 if (Boolean.FALSE.equals(this.skip) && this.pd != null && pvs instanceof MutablePropertyValues) { 230 ((MutablePropertyValues) pvs).clearProcessedProperty(this.pd.getName()); 231 } 232 } 233 } 234 235 /** 236 * Either this or {@link #inject} needs to be overridden. 237 */ 238 protected Object getResourceToInject(Object target, String requestingBeanName) { 239 return null; 240 } 241 242 @Override 243 public boolean equals(Object other) { 244 if (this == other) { 245 return true; 246 } 247 if (!(other instanceof InjectedElement)) { 248 return false; 249 } 250 InjectedElement otherElement = (InjectedElement) other; 251 return this.member.equals(otherElement.member); 252 } 253 254 @Override 255 public int hashCode() { 256 return this.member.getClass().hashCode() * 29 + this.member.getName().hashCode(); 257 } 258 259 @Override 260 public String toString() { 261 return getClass().getSimpleName() + " for " + this.member; 262 } 263 } 264 265}