From 1da98b054210b1bcb4e65cde66e378e034b1baa3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 15 Apr 2015 22:49:53 +0200 Subject: [PATCH] @Bean-returned FactoryBean proxy delegates to actual target instance now Issue: SPR-12915 --- .../ConfigurationClassEnhancer.java | 10 +- ...tionWithFactoryBeanAndAutowiringTests.java | 324 ++++++++++-------- 2 files changed, 194 insertions(+), 140 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index fd1854d0de..03145b6d1b 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -289,7 +289,7 @@ class ConfigurationClassEnhancer { } else { // It is a candidate FactoryBean - go ahead with enhancement - return enhanceFactoryBean(factoryBean.getClass(), beanFactory, beanName); + return enhanceFactoryBean(factoryBean, beanFactory, beanName); } } @@ -365,11 +365,11 @@ class ConfigurationClassEnhancer { * instance directly. If a FactoryBean instance is fetched through the container via &-dereferencing, * it will not be proxied. This too is aligned with the way XML configuration works. */ - private Object enhanceFactoryBean(Class fbClass, final ConfigurableBeanFactory beanFactory, + private Object enhanceFactoryBean(final Object factoryBean, final ConfigurableBeanFactory beanFactory, final String beanName) throws InstantiationException, IllegalAccessException { Enhancer enhancer = new Enhancer(); - enhancer.setSuperclass(fbClass); + enhancer.setSuperclass(factoryBean.getClass()); enhancer.setUseFactory(false); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setCallback(new MethodInterceptor() { @@ -378,7 +378,7 @@ class ConfigurationClassEnhancer { if (method.getName().equals("getObject") && args.length == 0) { return beanFactory.getBean(beanName); } - return proxy.invokeSuper(obj, args); + return proxy.invoke(factoryBean, args); } }); return enhancer.create(); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanAndAutowiringTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanAndAutowiringTests.java index 6f4602bd45..9d1ec9b243 100755 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanAndAutowiringTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanAndAutowiringTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,13 +19,17 @@ package org.springframework.context.annotation; import org.junit.Test; import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.Assert; +import static org.junit.Assert.*; + /** * Tests cornering bug SPR-8514. * * @author Chris Beams + * @author Juergen Hoeller * @since 3.1 */ public class ConfigurationWithFactoryBeanAndAutowiringTests { @@ -77,138 +81,188 @@ public class ConfigurationWithFactoryBeanAndAutowiringTests { ctx.register(WildcardParameterizedFactoryBeanInterfaceConfig.class); ctx.refresh(); } -} - - -class DummyBean { -} - - -class MyFactoryBean implements FactoryBean { - @Override - public String getObject() throws Exception { - return "foo"; - } - @Override - public Class getObjectType() { - return String.class; - } - @Override - public boolean isSingleton() { - return true; - } -} - - -class MyParameterizedFactoryBean implements FactoryBean { - - private final T obj; - - public MyParameterizedFactoryBean(T obj) { - this.obj = obj; - } - - @Override - public T getObject() throws Exception { - return obj; - } - - @Override - @SuppressWarnings("unchecked") - public Class getObjectType() { - return (Class)obj.getClass(); - } - - @Override - public boolean isSingleton() { - return true; - } -} - - -@Configuration -class AppConfig { - @Bean - public DummyBean dummyBean() { - return new DummyBean(); - } -} - - -@Configuration -class ConcreteFactoryBeanImplementationConfig { - @Autowired - private DummyBean dummyBean; - - @Bean - public MyFactoryBean factoryBean() { - Assert.notNull(dummyBean, "DummyBean was not injected."); - return new MyFactoryBean(); - } -} - - -@Configuration -class ParameterizedFactoryBeanImplementationConfig { - @Autowired - private DummyBean dummyBean; - - @Bean - public MyParameterizedFactoryBean factoryBean() { - Assert.notNull(dummyBean, "DummyBean was not injected."); - return new MyParameterizedFactoryBean("whatev"); - } -} - - -@Configuration -class ParameterizedFactoryBeanInterfaceConfig { - @Autowired - private DummyBean dummyBean; - - @Bean - public FactoryBean factoryBean() { - Assert.notNull(dummyBean, "DummyBean was not injected."); - return new MyFactoryBean(); - } -} - - -@Configuration -class NonPublicParameterizedFactoryBeanInterfaceConfig { - @Autowired - private DummyBean dummyBean; - - @Bean - FactoryBean factoryBean() { - Assert.notNull(dummyBean, "DummyBean was not injected."); - return new MyFactoryBean(); - } -} - - -@Configuration -class RawFactoryBeanInterfaceConfig { - @Autowired - private DummyBean dummyBean; - - @Bean - @SuppressWarnings("rawtypes") - public FactoryBean factoryBean() { - Assert.notNull(dummyBean, "DummyBean was not injected."); - return new MyFactoryBean(); - } -} - - -@Configuration -class WildcardParameterizedFactoryBeanInterfaceConfig { - @Autowired - private DummyBean dummyBean; - - @Bean - public FactoryBean factoryBean() { - Assert.notNull(dummyBean, "DummyBean was not injected."); - return new MyFactoryBean(); - } + + @Test + public void withFactoryBeanCallingBean() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(AppConfig.class); + ctx.register(FactoryBeanCallingConfig.class); + ctx.refresh(); + assertEquals("true", ctx.getBean("myString")); + } + + + static class DummyBean { + } + + + static class MyFactoryBean implements FactoryBean, InitializingBean { + + private boolean initialized = false; + + @Override + public void afterPropertiesSet() throws Exception { + this.initialized = true; + } + + @Override + public String getObject() throws Exception { + return "foo"; + } + + @Override + public Class getObjectType() { + return String.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + public String getString() { + return Boolean.toString(this.initialized); + } + } + + + static class MyParameterizedFactoryBean implements FactoryBean { + + private final T obj; + + public MyParameterizedFactoryBean(T obj) { + this.obj = obj; + } + + @Override + public T getObject() throws Exception { + return obj; + } + + @Override + @SuppressWarnings("unchecked") + public Class getObjectType() { + return (Class)obj.getClass(); + } + + @Override + public boolean isSingleton() { + return true; + } + } + + + @Configuration + static class AppConfig { + + @Bean + public DummyBean dummyBean() { + return new DummyBean(); + } + } + + + @Configuration + static class ConcreteFactoryBeanImplementationConfig { + + @Autowired + private DummyBean dummyBean; + + @Bean + public MyFactoryBean factoryBean() { + Assert.notNull(dummyBean, "DummyBean was not injected."); + return new MyFactoryBean(); + } + } + + + @Configuration + static class ParameterizedFactoryBeanImplementationConfig { + + @Autowired + private DummyBean dummyBean; + + @Bean + public MyParameterizedFactoryBean factoryBean() { + Assert.notNull(dummyBean, "DummyBean was not injected."); + return new MyParameterizedFactoryBean("whatev"); + } + } + + + @Configuration + static class ParameterizedFactoryBeanInterfaceConfig { + + @Autowired + private DummyBean dummyBean; + + @Bean + public FactoryBean factoryBean() { + Assert.notNull(dummyBean, "DummyBean was not injected."); + return new MyFactoryBean(); + } + } + + + @Configuration + static class NonPublicParameterizedFactoryBeanInterfaceConfig { + + @Autowired + private DummyBean dummyBean; + + @Bean + FactoryBean factoryBean() { + Assert.notNull(dummyBean, "DummyBean was not injected."); + return new MyFactoryBean(); + } + } + + + @Configuration + static class RawFactoryBeanInterfaceConfig { + + @Autowired + private DummyBean dummyBean; + + @Bean + @SuppressWarnings("rawtypes") + public FactoryBean factoryBean() { + Assert.notNull(dummyBean, "DummyBean was not injected."); + return new MyFactoryBean(); + } + } + + + @Configuration + static class WildcardParameterizedFactoryBeanInterfaceConfig { + + @Autowired + private DummyBean dummyBean; + + @Bean + public FactoryBean factoryBean() { + Assert.notNull(dummyBean, "DummyBean was not injected."); + return new MyFactoryBean(); + } + } + + + @Configuration + static class FactoryBeanCallingConfig { + + @Autowired + private DummyBean dummyBean; + + @Bean + public MyFactoryBean factoryBean() { + Assert.notNull(dummyBean, "DummyBean was not injected."); + return new MyFactoryBean(); + } + + @Bean + public String myString() { + return factoryBean().getString(); + } + } + }