Add support for @ScopedProxy for factory beans using the @FactoryBean annotation within a @Component
Add missing unit tests
This commit is contained in:
parent
bf31766ff8
commit
13dfa11def
|
|
@ -65,8 +65,7 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
|||
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
|
||||
|
||||
private boolean includeAnnotationConfig = true;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ClassPathBeanDefinitionScanner for the given bean factory.
|
||||
* @param registry the BeanFactory to load bean definitions into,
|
||||
|
|
@ -221,14 +220,23 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
|||
|
||||
protected void postProcessComponentBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
|
||||
//TODO refactor increment index count as part of naming strategy.
|
||||
int count = 0;
|
||||
Set<BeanDefinitionHolder> factoryBeanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
|
||||
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitions) {
|
||||
Set<BeanDefinition> candidates = findCandidateFactoryMethods(beanDefinitionHolder);
|
||||
for (BeanDefinition candidate : candidates ) {
|
||||
//TODO refactor to introduce naming strategy and some sanity checks.
|
||||
String beanName = beanDefinitionHolder.getBeanName() + "$" + candidate.getFactoryMethodName() + "#" + count++;
|
||||
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
|
||||
for (BeanDefinition candidate : candidates ) {
|
||||
|
||||
|
||||
BeanDefinitionHolder definitionHolder;
|
||||
if (candidate.getBeanClassName().equals("org.springframework.aop.scope.ScopedProxyFactoryBean")){
|
||||
String scopedFactoryBeanName = "scopedTarget." + candidate.getPropertyValues().getPropertyValue("targetBeanName").getValue();
|
||||
definitionHolder = new BeanDefinitionHolder(candidate, scopedFactoryBeanName);
|
||||
} else {
|
||||
String configurationComponentBeanName = beanDefinitionHolder.getBeanName();
|
||||
String factoryMethodName = candidate.getFactoryMethodName();
|
||||
String beanName = createFactoryBeanName(configurationComponentBeanName, factoryMethodName);
|
||||
definitionHolder = new BeanDefinitionHolder(candidate, beanName);
|
||||
}
|
||||
|
||||
factoryBeanDefinitions.add(definitionHolder);
|
||||
registerBeanDefinition(definitionHolder, this.registry);
|
||||
}
|
||||
|
|
@ -236,8 +244,6 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
|||
beanDefinitions.addAll(factoryBeanDefinitions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Apply further settings to the given bean definition,
|
||||
* beyond the contents retrieved from scanning the component class.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import java.util.Set;
|
|||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.scope.ScopedProxyFactoryBean;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
|
@ -34,6 +35,8 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
|
@ -77,6 +80,8 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
|
|||
protected static final String QUALIFIER_CLASS_NAME = "org.springframework.beans.factory.annotation.Qualifier";
|
||||
|
||||
protected static final String SCOPE_CLASS_NAME = "org.springframework.context.annotation.Scope";
|
||||
|
||||
protected static final String SCOPEDPROXY_CLASS_NAME = "org.springframework.beans.factory.annotation.ScopedProxy";
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
|
@ -90,6 +95,8 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
|
|||
|
||||
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
|
||||
|
||||
private int factoryBeanCount = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Create a ClassPathScanningCandidateComponentProvider.
|
||||
|
|
@ -259,10 +266,33 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
|
|||
|
||||
factoryBeanDef.setResource(containingBeanDef.getResource());
|
||||
factoryBeanDef.setSource(containingBeanDef.getSource());
|
||||
|
||||
if (debugEnabled) {
|
||||
logger.debug("Identified candidate factory method in class: " + resource);
|
||||
}
|
||||
candidates.add(factoryBeanDef);
|
||||
|
||||
RootBeanDefinition scopedFactoryBeanDef = null;
|
||||
if (methodMetadata.hasAnnotation(SCOPEDPROXY_CLASS_NAME)) {
|
||||
//TODO validate that @ScopedProxy isn't applied to singleton/prototype beans.
|
||||
Map<String, Object> attributes = methodMetadata.getAnnotationAttributes(SCOPEDPROXY_CLASS_NAME);
|
||||
scopedFactoryBeanDef = new RootBeanDefinition(ScopedProxyFactoryBean.class);
|
||||
String t= scopedFactoryBeanDef.getBeanClassName();
|
||||
String targetBeanName = createFactoryBeanName(beanDefinitionHolder.getBeanName(), factoryBeanDef.getFactoryMethodName());
|
||||
scopedFactoryBeanDef.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
|
||||
|
||||
//TODO handle cglib options
|
||||
// scopedFactoryBeanDef.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE);
|
||||
scopedFactoryBeanDef.setAutowireCandidate(false);
|
||||
scopedFactoryBeanDef.setResource(containingBeanDef.getResource());
|
||||
scopedFactoryBeanDef.setSource(containingBeanDef.getSource());
|
||||
|
||||
candidates.add(scopedFactoryBeanDef);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
if (traceEnabled) {
|
||||
|
|
@ -365,4 +395,11 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
|
|||
return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
|
||||
}
|
||||
|
||||
|
||||
protected String createFactoryBeanName(String configurationComponentBeanName, String factoryMethodName) {
|
||||
//TODO consider adding hex string and passing in definition object.
|
||||
String beanName = configurationComponentBeanName + "$" + factoryMethodName;
|
||||
return beanName;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
|
||||
@Target({ElementType.METHOD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Qualifier
|
||||
public @interface BeanAge {
|
||||
int value();
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright 2002-2009 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.
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.aop.scope.ScopedObject;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.SimpleMapScope;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||
import org.springframework.context.annotation4.FactoryMethodComponent;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
|
||||
public class ClassPathFactoryBeanDefinitionScannerTests extends TestCase {
|
||||
|
||||
private static final String BASE_PACKAGE = FactoryMethodComponent.class.getPackage().getName();
|
||||
|
||||
private static final int NUM_DEFAULT_BEAN_DEFS = 4;
|
||||
|
||||
private static final int NUM_FACTORY_METHODS = 5; // @ScopedProxy creates another
|
||||
|
||||
private static final int NUM_COMPONENT_DEFS = 1;
|
||||
|
||||
|
||||
public void testSingletonScopedFactoryMethod()
|
||||
{
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
|
||||
SimpleMapScope scope = new SimpleMapScope();
|
||||
context.getBeanFactory().registerScope("request", scope);
|
||||
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
|
||||
assertEquals(NUM_FACTORY_METHODS + NUM_COMPONENT_DEFS + NUM_DEFAULT_BEAN_DEFS, beanCount);
|
||||
assertTrue(context.containsBean("factoryMethodComponent"));
|
||||
assertTrue(context.containsBean("factoryMethodComponent$staticInstance"));
|
||||
assertTrue(context.containsBean("factoryMethodComponent$getPublicInstance"));
|
||||
|
||||
|
||||
|
||||
|
||||
TestBean staticTestBean = (TestBean)context.getBean("factoryMethodComponent$staticInstance");//1
|
||||
assertEquals("staticInstance", staticTestBean.getName());
|
||||
TestBean staticTestBean2 = (TestBean)context.getBean("factoryMethodComponent$staticInstance");//1
|
||||
assertSame(staticTestBean, staticTestBean2);
|
||||
|
||||
TestBean tb = (TestBean)context.getBean("factoryMethodComponent$getPublicInstance"); //2
|
||||
assertEquals("publicInstance", tb.getName());
|
||||
TestBean tb2 = (TestBean)context.getBean("factoryMethodComponent$getPublicInstance"); //2
|
||||
assertEquals("publicInstance", tb2.getName());
|
||||
assertSame(tb2, tb);
|
||||
|
||||
//Were qualifiers applied to bean definition
|
||||
ConfigurableListableBeanFactory cbf = (ConfigurableListableBeanFactory)context.getAutowireCapableBeanFactory();
|
||||
AbstractBeanDefinition abd = (AbstractBeanDefinition)cbf.getBeanDefinition("factoryMethodComponent$getPublicInstance"); //2
|
||||
Set<AutowireCandidateQualifier> qualifierSet = abd.getQualifiers();
|
||||
assertEquals(1, qualifierSet.size());
|
||||
|
||||
|
||||
tb = (TestBean)context.getBean("factoryMethodComponent$getProtectedInstance"); //3
|
||||
assertEquals("protectedInstance", tb.getName());
|
||||
tb2 = (TestBean)context.getBean("factoryMethodComponent$getProtectedInstance"); //3
|
||||
assertEquals("protectedInstance", tb2.getName());
|
||||
assertSame(tb2, tb);
|
||||
|
||||
tb = (TestBean)context.getBean("factoryMethodComponent$getPrivateInstance"); //4
|
||||
assertEquals("privateInstance", tb.getName());
|
||||
assertEquals(0, tb.getAge());
|
||||
tb2 = (TestBean)context.getBean("factoryMethodComponent$getPrivateInstance"); //4
|
||||
assertEquals(1, tb2.getAge());
|
||||
assertNotSame(tb2, tb);
|
||||
|
||||
Object bean = context.getBean("scopedTarget.factoryMethodComponent$requestScopedInstance"); //5
|
||||
assertNotNull(bean);
|
||||
assertTrue(bean instanceof ScopedObject);
|
||||
|
||||
//Scope assertions
|
||||
assertTrue(AopUtils.isCglibProxy(bean));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2002-2009 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.
|
||||
* 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.context.annotation4;
|
||||
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.FactoryMethod;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.ScopedProxy;
|
||||
import org.springframework.context.annotation.BeanAge;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Class used to test the functionality of @FactoryMethod bean definitions declared inside
|
||||
* a Spring @Component class.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
@Component
|
||||
public class FactoryMethodComponent {
|
||||
|
||||
private static TestBean staticTestBean = new TestBean("staticInstance",1);
|
||||
|
||||
@Autowired @Qualifier("public")
|
||||
public TestBean autowiredTestBean;
|
||||
|
||||
private static int i;
|
||||
|
||||
@FactoryMethod @Qualifier("static")
|
||||
public static TestBean staticInstance()
|
||||
{
|
||||
return staticTestBean;
|
||||
}
|
||||
|
||||
public static TestBean nullInstance()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@FactoryMethod @Qualifier("public")
|
||||
public TestBean getPublicInstance() {
|
||||
return new TestBean("publicInstance");
|
||||
}
|
||||
|
||||
@FactoryMethod @BeanAge(1)
|
||||
protected TestBean getProtectedInstance() {
|
||||
return new TestBean("protectedInstance", 1);
|
||||
}
|
||||
|
||||
@FactoryMethod @Scope("prototype")
|
||||
private TestBean getPrivateInstance() {
|
||||
return new TestBean("privateInstance", i++);
|
||||
}
|
||||
|
||||
@FactoryMethod @Scope("request") @ScopedProxy
|
||||
public TestBean requestScopedInstance()
|
||||
{
|
||||
TestBean testBean = new TestBean("requestScopedInstance", 3);
|
||||
return testBean;
|
||||
}
|
||||
|
||||
//TODO method for test that fails if use @ScopedProxy with singleton scope.
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2002-2009 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.
|
||||
* 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.context.annotation4;
|
||||
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.beans.factory.annotation.FactoryMethod;
|
||||
|
||||
/**
|
||||
* Class to test that @FactoryMethods are detected only when inside a class with an @Component
|
||||
* class annotation.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class SimpleBean {
|
||||
|
||||
|
||||
// This should *not* recognized as a @FactoryMethod since it does not reside inside an @Component
|
||||
@FactoryMethod
|
||||
public TestBean getPublicInstance() {
|
||||
return new TestBean("publicInstance");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue