Skip to content

Commit 809ee0d

Browse files
committed
Annotation post-processors clear old InjectionMetadata registrations on refresh
Issue: SPR-12526
1 parent dfdfc03 commit 809ee0d

File tree

6 files changed

+217
-15
lines changed

6 files changed

+217
-15
lines changed

spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -320,6 +320,16 @@ public void registerProcessedProperty(String propertyName) {
320320
this.processedProperties.add(propertyName);
321321
}
322322

323+
/**
324+
* Clear the "processed" registration of the given property, if any.
325+
* @since 3.2.13
326+
*/
327+
public void clearProcessedProperty(String propertyName) {
328+
if (this.processedProperties != null) {
329+
this.processedProperties.remove(propertyName);
330+
}
331+
}
332+
323333
/**
324334
* Mark this holder as containing converted values only
325335
* (i.e. no runtime resolution needed anymore).

spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
230230
@Override
231231
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
232232
if (beanType != null) {
233-
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType);
233+
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
234234
metadata.checkConfigMembers(beanDefinition);
235235
}
236236
}
@@ -326,7 +326,7 @@ else if (candidates.size() == 1 && logger.isWarnEnabled()) {
326326
public PropertyValues postProcessPropertyValues(
327327
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
328328

329-
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass());
329+
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
330330
try {
331331
metadata.inject(bean, beanName, pvs);
332332
}
@@ -344,7 +344,7 @@ public PropertyValues postProcessPropertyValues(
344344
*/
345345
public void processInjection(Object bean) throws BeansException {
346346
Class<?> clazz = bean.getClass();
347-
InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz);
347+
InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null);
348348
try {
349349
metadata.inject(bean, null, null);
350350
}
@@ -354,7 +354,7 @@ public void processInjection(Object bean) throws BeansException {
354354
}
355355

356356

357-
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz) {
357+
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
358358
// Fall back to class name as cache key, for backwards compatibility with custom callers.
359359
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
360360
// Quick check on the concurrent map first, with minimal locking.
@@ -363,6 +363,9 @@ private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz
363363
synchronized (this.injectionMetadataCache) {
364364
metadata = this.injectionMetadataCache.get(cacheKey);
365365
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
366+
if (metadata != null) {
367+
metadata.clear(pvs);
368+
}
366369
try {
367370
metadata = buildAutowiringMetadata(clazz);
368371
this.injectionMetadataCache.put(cacheKey, metadata);

spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@
4646
*/
4747
public class InjectionMetadata {
4848

49-
private final Log logger = LogFactory.getLog(InjectionMetadata.class);
49+
private static final Log logger = LogFactory.getLog(InjectionMetadata.class);
5050

5151
private final Class<?> targetClass;
5252

@@ -60,6 +60,7 @@ public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> eleme
6060
this.injectedElements = elements;
6161
}
6262

63+
6364
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
6465
Set<InjectedElement> checkedElements = new LinkedHashSet<InjectedElement>(this.injectedElements.size());
6566
for (InjectedElement element : this.injectedElements) {
@@ -82,13 +83,26 @@ public void inject(Object target, String beanName, PropertyValues pvs) throws Th
8283
boolean debug = logger.isDebugEnabled();
8384
for (InjectedElement element : elementsToIterate) {
8485
if (debug) {
85-
logger.debug("Processing injected method of bean '" + beanName + "': " + element);
86+
logger.debug("Processing injected element of bean '" + beanName + "': " + element);
8687
}
8788
element.inject(target, beanName, pvs);
8889
}
8990
}
9091
}
9192

93+
/**
94+
* @since 3.2.13
95+
*/
96+
public void clear(PropertyValues pvs) {
97+
Collection<InjectedElement> elementsToIterate =
98+
(this.checkedElements != null ? this.checkedElements : this.injectedElements);
99+
if (!elementsToIterate.isEmpty()) {
100+
for (InjectedElement element : elementsToIterate) {
101+
element.clearPropertySkipping(pvs);
102+
}
103+
}
104+
}
105+
92106

93107
public static boolean needsRefresh(InjectionMetadata metadata, Class<?> clazz) {
94108
return (metadata == null || !metadata.targetClass.equals(clazz));
@@ -170,7 +184,7 @@ protected void inject(Object target, String requestingBeanName, PropertyValues p
170184
}
171185

172186
/**
173-
* Checks whether this injector's property needs to be skipped due to
187+
* Check whether this injector's property needs to be skipped due to
174188
* an explicit property value having been specified. Also marks the
175189
* affected property as processed for other processors to ignore it.
176190
*/
@@ -201,6 +215,20 @@ else if (pvs instanceof MutablePropertyValues) {
201215
}
202216
}
203217

218+
/**
219+
* @since 3.2.13
220+
*/
221+
protected void clearPropertySkipping(PropertyValues pvs) {
222+
if (pvs == null) {
223+
return;
224+
}
225+
synchronized (pvs) {
226+
if (Boolean.FALSE.equals(this.skip) && this.pd != null && pvs instanceof MutablePropertyValues) {
227+
((MutablePropertyValues) pvs).clearProcessedProperty(this.pd.getName());
228+
}
229+
}
230+
}
231+
204232
/**
205233
* Either this or {@link #inject} needs to be overridden.
206234
*/

spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
281281
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
282282
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
283283
if (beanType != null) {
284-
InjectionMetadata metadata = findResourceMetadata(beanName, beanType);
284+
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
285285
metadata.checkConfigMembers(beanDefinition);
286286
}
287287
}
@@ -300,7 +300,7 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) throw
300300
public PropertyValues postProcessPropertyValues(
301301
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
302302

303-
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass());
303+
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
304304
try {
305305
metadata.inject(bean, beanName, pvs);
306306
}
@@ -311,7 +311,7 @@ public PropertyValues postProcessPropertyValues(
311311
}
312312

313313

314-
private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz) {
314+
private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, PropertyValues pvs) {
315315
// Fall back to class name as cache key, for backwards compatibility with custom callers.
316316
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
317317
// Quick check on the concurrent map first, with minimal locking.
@@ -320,6 +320,9 @@ private InjectionMetadata findResourceMetadata(String beanName, final Class<?> c
320320
synchronized (this.injectionMetadataCache) {
321321
metadata = this.injectionMetadataCache.get(cacheKey);
322322
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
323+
if (metadata != null) {
324+
metadata.clear(pvs);
325+
}
323326
try {
324327
metadata = buildResourceMetadata(clazz);
325328
this.injectionMetadataCache.put(cacheKey, metadata);
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright 2002-2014 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.context.annotation.configuration;
18+
19+
import javax.annotation.Resource;
20+
21+
import org.junit.Test;
22+
23+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.context.annotation.Scope;
27+
28+
import static org.junit.Assert.*;
29+
import static org.springframework.beans.factory.config.BeanDefinition.*;
30+
31+
/**
32+
* @author Marcin Piela
33+
* @author Juergen Hoeller
34+
*/
35+
public class Spr12526Tests {
36+
37+
@Test
38+
public void testInjection() {
39+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(TestContext.class);
40+
CustomCondition condition = ctx.getBean(CustomCondition.class);
41+
42+
condition.setCondition(true);
43+
FirstService firstService = (FirstService) ctx.getBean(Service.class);
44+
assertNotNull("FirstService.dependency is null", firstService.getDependency());
45+
46+
condition.setCondition(false);
47+
SecondService secondService = (SecondService) ctx.getBean(Service.class);
48+
assertNotNull("SecondService.dependency is null", secondService.getDependency());
49+
}
50+
51+
52+
@Configuration
53+
public static class TestContext {
54+
55+
@Bean
56+
@Scope(SCOPE_SINGLETON)
57+
public CustomCondition condition() {
58+
return new CustomCondition();
59+
}
60+
61+
62+
@Bean
63+
@Scope(SCOPE_PROTOTYPE)
64+
public Service service(CustomCondition condition) {
65+
return (condition.check() ? new FirstService() : new SecondService());
66+
}
67+
68+
@Bean
69+
public DependencyOne dependencyOne() {
70+
return new DependencyOne();
71+
}
72+
73+
74+
@Bean
75+
public DependencyTwo dependencyTwo() {
76+
return new DependencyTwo();
77+
}
78+
}
79+
80+
81+
public static class CustomCondition {
82+
83+
private boolean condition;
84+
85+
public boolean check() {
86+
return condition;
87+
}
88+
89+
public void setCondition(boolean value) {
90+
this.condition = value;
91+
}
92+
}
93+
94+
95+
public interface Service {
96+
97+
void doStuff();
98+
}
99+
100+
101+
public static class FirstService implements Service {
102+
103+
private DependencyOne dependency;
104+
105+
106+
@Override
107+
public void doStuff() {
108+
if (dependency == null) {
109+
throw new IllegalStateException("FirstService: dependency is null");
110+
}
111+
}
112+
113+
@Resource(name = "dependencyOne")
114+
public void setDependency(DependencyOne dependency) {
115+
this.dependency = dependency;
116+
}
117+
118+
119+
public DependencyOne getDependency() {
120+
return dependency;
121+
}
122+
}
123+
124+
125+
public static class SecondService implements Service {
126+
127+
private DependencyTwo dependency;
128+
129+
@Override
130+
public void doStuff() {
131+
if (dependency == null) {
132+
throw new IllegalStateException("SecondService: dependency is null");
133+
}
134+
}
135+
136+
@Resource(name = "dependencyTwo")
137+
public void setDependency(DependencyTwo dependency) {
138+
this.dependency = dependency;
139+
}
140+
141+
142+
public DependencyTwo getDependency() {
143+
return dependency;
144+
}
145+
}
146+
147+
148+
public static class DependencyOne {
149+
}
150+
151+
152+
public static class DependencyTwo {
153+
}
154+
155+
}

spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ public void setBeanFactory(BeanFactory beanFactory) {
330330
@Override
331331
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
332332
if (beanType != null) {
333-
InjectionMetadata metadata = findPersistenceMetadata(beanName, beanType);
333+
InjectionMetadata metadata = findPersistenceMetadata(beanName, beanType, null);
334334
metadata.checkConfigMembers(beanDefinition);
335335
}
336336
}
@@ -349,7 +349,7 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) throw
349349
public PropertyValues postProcessPropertyValues(
350350
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
351351

352-
InjectionMetadata metadata = findPersistenceMetadata(beanName, bean.getClass());
352+
InjectionMetadata metadata = findPersistenceMetadata(beanName, bean.getClass(), pvs);
353353
try {
354354
metadata.inject(bean, beanName, pvs);
355355
}
@@ -376,7 +376,7 @@ public void postProcessBeforeDestruction(Object bean, String beanName) throws Be
376376
}
377377

378378

379-
private InjectionMetadata findPersistenceMetadata(String beanName, final Class<?> clazz) {
379+
private InjectionMetadata findPersistenceMetadata(String beanName, final Class<?> clazz, PropertyValues pvs) {
380380
// Fall back to class name as cache key, for backwards compatibility with custom callers.
381381
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
382382
// Quick check on the concurrent map first, with minimal locking.
@@ -385,6 +385,9 @@ private InjectionMetadata findPersistenceMetadata(String beanName, final Class<?
385385
synchronized (this.injectionMetadataCache) {
386386
metadata = this.injectionMetadataCache.get(cacheKey);
387387
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
388+
if (metadata != null) {
389+
metadata.clear(pvs);
390+
}
388391
try {
389392
metadata = buildPersistenceMetadata(clazz);
390393
this.injectionMetadataCache.put(cacheKey, metadata);

0 commit comments

Comments
 (0)