Introduce FeatureSpecification support
Introduce FeatureSpecification interface and implementations FeatureSpecification objects decouple the configuration of spring container features from the concern of parsing XML namespaces, allowing for reuse in code-based configuration (see @Feature* annotations below). * ComponentScanSpec * TxAnnotationDriven * MvcAnnotationDriven * MvcDefaultServletHandler * MvcResources * MvcViewControllers Refactor associated BeanDefinitionParsers to delegate to new impls above The following BeanDefinitionParser implementations now deal only with the concern of XML parsing. Validation is handled by their corresponding FeatureSpecification object. Bean definition creation and registration is handled by their corresponding FeatureSpecificationExecutor type. * ComponentScanBeanDefinitionParser * AnnotationDrivenBeanDefinitionParser (tx) * AnnotationDrivenBeanDefinitionParser (mvc) * DefaultServletHandlerBeanDefinitionParser * ResourcesBeanDefinitionParser * ViewControllerBeanDefinitionParser Update AopNamespaceUtils to decouple from XML (DOM API) Methods necessary for executing TxAnnotationDriven specification (and eventually, the AspectJAutoProxy specification) have been added that accept boolean arguments for whether to proxy target classes and whether to expose the proxy via threadlocal. Methods that accepted and introspected DOM Element objects still exist but have been deprecated. Introduce @FeatureConfiguration classes and @Feature methods Allow for creation and configuration of FeatureSpecification objects at the user level. A companion for @Configuration classes allowing for completely code-driven configuration of the Spring container. See changes in ConfigurationClassPostProcessor for implementation details. See Feature*Tests for usage examples. FeatureTestSuite in .integration-tests is a JUnit test suite designed to aggregate all BDP and Feature* related tests for a convenient way to confirm that Feature-related changes don't break anything. Uncomment this test and execute from Eclipse / IDEA. Due to classpath issues, this cannot be compiled by Ant/Ivy at the command line. Introduce @FeatureAnnotation meta-annotation and @ComponentScan impl @FeatureAnnotation provides an alternate mechanism for creating and executing FeatureSpecification objects. See @ComponentScan and its corresponding ComponentScanAnnotationParser implementation for details. See ComponentScanAnnotationIntegrationTests for usage examples Introduce Default[Formatting]ConversionService implementations Allows for convenient instantiation of ConversionService objects containing defaults appropriate for most environments. Replaces similar support originally in ConversionServiceFactory (which is now deprecated). This change was justified by the need to avoid use of FactoryBeans in @Configuration classes (such as FormattingConversionServiceFactoryBean). It is strongly preferred that users simply instantiate and configure the objects that underlie our FactoryBeans. In the case of the ConversionService types, the easiest way to do this is to create Default* subtypes. This also follows convention with the rest of the framework. Minor updates to util classes All in service of changes above. See diffs for self-explanatory details. * BeanUtils * ObjectUtils * ReflectionUtils
This commit is contained in:
parent
b04987ccc3
commit
b4fea47d5c
|
@ -16,12 +16,12 @@
|
|||
|
||||
package org.springframework.aop.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.ComponentRegistrar;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Utility class for handling registration of auto-proxy creators used internally
|
||||
|
@ -52,6 +52,11 @@ public abstract class AopNamespaceUtils {
|
|||
private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated since Spring 3.1 in favor of
|
||||
* {@link #registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry, ComponentRegistrar, Object, Boolean, Boolean)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static void registerAutoProxyCreatorIfNecessary(
|
||||
ParserContext parserContext, Element sourceElement) {
|
||||
|
||||
|
@ -61,6 +66,20 @@ public abstract class AopNamespaceUtils {
|
|||
registerComponentIfNecessary(beanDefinition, parserContext);
|
||||
}
|
||||
|
||||
public static void registerAutoProxyCreatorIfNecessary(
|
||||
BeanDefinitionRegistry registry, ComponentRegistrar parserContext, Object source, Boolean proxyTargetClass, Boolean exposeProxy) {
|
||||
|
||||
BeanDefinition beanDefinition =
|
||||
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry, source);
|
||||
useClassProxyingIfNecessary(registry, proxyTargetClass, exposeProxy);
|
||||
registerComponentIfNecessary(beanDefinition, parserContext);
|
||||
}
|
||||
|
||||
public static void registerAutoProxyCreatorIfNecessary(
|
||||
BeanDefinitionRegistry registry, ComponentRegistrar parserContext, Object source, Boolean proxyTargetClass) {
|
||||
registerAutoProxyCreatorIfNecessary(registry, parserContext, source, proxyTargetClass, false);
|
||||
}
|
||||
|
||||
public static void registerAspectJAutoProxyCreatorIfNecessary(
|
||||
ParserContext parserContext, Element sourceElement) {
|
||||
|
||||
|
@ -101,6 +120,12 @@ public abstract class AopNamespaceUtils {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated since Spring 3.1 in favor of
|
||||
* {@link #useClassProxyingIfNecessary(BeanDefinitionRegistry, Boolean, Boolean)}
|
||||
* which does not require a parameter of type org.w3c.dom.Element
|
||||
*/
|
||||
@Deprecated
|
||||
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
|
||||
if (sourceElement != null) {
|
||||
boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
|
||||
|
@ -114,11 +139,20 @@ public abstract class AopNamespaceUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) {
|
||||
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Boolean proxyTargetClass, Boolean exposeProxy) {
|
||||
if (proxyTargetClass) {
|
||||
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
|
||||
}
|
||||
if (exposeProxy) {
|
||||
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ComponentRegistrar componentRegistrar) {
|
||||
if (beanDefinition != null) {
|
||||
BeanComponentDefinition componentDefinition =
|
||||
new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
|
||||
parserContext.registerComponent(componentDefinition);
|
||||
componentRegistrar.registerComponent(componentDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ public abstract class BeanUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience method to instantiate a class using its no-arg constructor.
|
||||
* Instantiate a class using its no-arg constructor.
|
||||
* As this method doesn't try to load classes by name, it should avoid
|
||||
* class-loading issues.
|
||||
* <p>Note that this method tries to set the constructor accessible
|
||||
|
@ -108,6 +108,27 @@ public abstract class BeanUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a class using its no-arg constructor and return the new instance
|
||||
* as the the specified assignable type.
|
||||
* <p>Useful in cases where
|
||||
* the type of the class to instantiate (clazz) is not available, but the type
|
||||
* desired (assignableTo) is known.
|
||||
* <p>As this method doesn't try to load classes by name, it should avoid
|
||||
* class-loading issues.
|
||||
* <p>Note that this method tries to set the constructor accessible
|
||||
* if given a non-accessible (that is, non-public) constructor.
|
||||
* @param clazz class to instantiate
|
||||
* @param assignableTo type that clazz must be assignableTo
|
||||
* @return the new instance
|
||||
* @throws BeanInstantiationException if the bean cannot be instantiated
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo) throws BeanInstantiationException {
|
||||
Assert.isAssignable(assignableTo, clazz);
|
||||
return (T)instantiateClass(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to instantiate a class using the given constructor.
|
||||
* As this method doesn't try to load classes by name, it should avoid
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.beans.factory.parsing;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
|
||||
public interface BeanDefinitionRegistrar {
|
||||
|
||||
String registerWithGeneratedName(BeanDefinition beanDefinition);
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.beans.factory.parsing;
|
||||
|
||||
public interface ComponentRegistrar extends BeanDefinitionRegistrar {
|
||||
|
||||
void registerBeanComponent(BeanComponentDefinition component);
|
||||
|
||||
void registerComponent(ComponentDefinition component);
|
||||
}
|
|
@ -30,12 +30,13 @@ public class ReaderContext {
|
|||
|
||||
private final Resource resource;
|
||||
|
||||
private final ProblemReporter problemReporter;
|
||||
|
||||
private final ReaderEventListener eventListener;
|
||||
|
||||
private final SourceExtractor sourceExtractor;
|
||||
|
||||
// TODO SPR-7420: review exposing problem reporter
|
||||
protected final ProblemReporter problemReporter;
|
||||
|
||||
|
||||
public ReaderContext(Resource resource, ProblemReporter problemReporter,
|
||||
ReaderEventListener eventListener, SourceExtractor sourceExtractor) {
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.beans.factory.parsing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.io.DescriptiveResource;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class SimpleProblemCollector {
|
||||
|
||||
private Location location = null;
|
||||
private List<Problem> errors = new ArrayList<Problem>();
|
||||
|
||||
public SimpleProblemCollector(Object location) {
|
||||
if (location != null) {
|
||||
this.location = new Location(new DescriptiveResource(location.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public void error(String message) {
|
||||
this.errors.add(new Problem(message, this.location));
|
||||
}
|
||||
|
||||
public void error(String message, Throwable cause) {
|
||||
this.errors.add(new Problem(message, this.location, null, cause));
|
||||
}
|
||||
|
||||
public void reportProblems(ProblemReporter reporter) {
|
||||
for (Problem error : errors) {
|
||||
reporter.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasErrors() {
|
||||
return this.errors.size() > 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
|
|
@ -166,7 +166,7 @@ public class BeanDefinitionReaderUtils {
|
|||
* for the given bean definition or the definition cannot be registered
|
||||
*/
|
||||
public static String registerWithGeneratedName(
|
||||
AbstractBeanDefinition definition, BeanDefinitionRegistry registry)
|
||||
BeanDefinition definition, BeanDefinitionRegistry registry)
|
||||
throws BeanDefinitionStoreException {
|
||||
|
||||
String generatedName = generateBeanName(definition, registry, false);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -21,9 +21,8 @@ import org.w3c.dom.Element;
|
|||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
|
||||
/**
|
||||
* Interface used by the
|
||||
* {@link org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader} to
|
||||
* handle custom, top-level (directly under <code><beans></code>) tags.
|
||||
* Interface used by the {@link DefaultBeanDefinitionDocumentReader} to handle custom,
|
||||
* top-level (directly under {@code <beans>}) tags.
|
||||
*
|
||||
* <p>Implementations are free to turn the metadata in the custom tag into as many
|
||||
* {@link BeanDefinition BeanDefinitions} as required.
|
||||
|
@ -31,11 +30,19 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||
* <p>The parser locates a {@link BeanDefinitionParser} from the associated
|
||||
* {@link NamespaceHandler} for the namespace in which the custom tag resides.
|
||||
*
|
||||
* <p>Implementations are encouraged to decouple XML parsing from bean registration by
|
||||
* parsing element(s) into a {@link org.springframework.context.FeatureSpecification
|
||||
* FeatureSpecification} object and subsequently executing that specification.
|
||||
* Doing so allows for maximum reuse between XML-based and annotation-based
|
||||
* configuration options.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @since 2.0
|
||||
* @see NamespaceHandler
|
||||
* @see org.springframework.beans.factory.xml.BeanDefinitionDecorator
|
||||
* @see AbstractBeanDefinitionParser
|
||||
* @see org.springframework.beans.factory.xml.BeanDefinitionDecorator
|
||||
* @see org.springframework.context.FeatureSpecification
|
||||
* @see org.springframework.context.AbstractSpecificationExecutor
|
||||
*/
|
||||
public interface BeanDefinitionParser {
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Stack;
|
|||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.ComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.ComponentRegistrar;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
|
@ -36,7 +37,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|||
* @see XmlReaderContext
|
||||
* @see BeanDefinitionParserDelegate
|
||||
*/
|
||||
public final class ParserContext {
|
||||
public final class ParserContext implements ComponentRegistrar {
|
||||
|
||||
private final XmlReaderContext readerContext;
|
||||
|
||||
|
@ -121,4 +122,8 @@ public final class ParserContext {
|
|||
registerComponent(component);
|
||||
}
|
||||
|
||||
public String registerWithGeneratedName(BeanDefinition beanDefinition) {
|
||||
return this.readerContext.registerWithGeneratedName(beanDefinition);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -83,4 +83,9 @@ public class XmlReaderContext extends ReaderContext {
|
|||
return generatedName;
|
||||
}
|
||||
|
||||
// TODO SPR-7420: review exposing problem reporter
|
||||
public ProblemReporter getProblemReporter() {
|
||||
return this.problemReporter;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -16,6 +16,15 @@
|
|||
|
||||
package org.springframework.beans.factory;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.MalformedURLException;
|
||||
import java.security.AccessControlContext;
|
||||
|
@ -31,20 +40,13 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import test.beans.DerivedTestBean;
|
||||
import test.beans.DummyFactory;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.LifecycleBean;
|
||||
import test.beans.NestedTestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.NotWritablePropertyException;
|
||||
|
@ -75,12 +77,19 @@ import org.springframework.beans.propertyeditors.CustomNumberEditor;
|
|||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
import test.beans.DerivedTestBean;
|
||||
import test.beans.DummyFactory;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.LifecycleBean;
|
||||
import test.beans.NestedTestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests properties population and autowire behavior.
|
||||
*
|
||||
|
@ -849,7 +858,7 @@ public class DefaultListableBeanFactoryTests {
|
|||
@Test
|
||||
public void testCustomConverter() {
|
||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
GenericConversionService conversionService = (GenericConversionService) ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService conversionService = new DefaultConversionService();
|
||||
conversionService.addConverter(new Converter<String, Float>() {
|
||||
public Float convert(String source) {
|
||||
try {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<config>src/test/java/org/springframework/context/annotation/configuration/SecondLevelSubConfig-context.xml</config>
|
||||
<config>src/test/java/org/springframework/context/annotation/configuration/ImportXmlWithAopNamespace-context.xml</config>
|
||||
<config>src/test/java/org/springframework/context/annotation/Spr6602Tests-context.xml</config>
|
||||
<config>src/test/java/org/springframework/context/annotation/FeatureConfigurationImportResourceTests-context.xml</config>
|
||||
</configs>
|
||||
<configSets>
|
||||
</configSets>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -141,5 +141,4 @@ public class AnnotatedBeanDefinitionReader {
|
|||
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -37,10 +37,9 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||
|
||||
private Class<? extends Annotation> scopeAnnotationType = Scope.class;
|
||||
|
||||
private final ScopedProxyMode defaultProxyMode;
|
||||
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
|
||||
|
||||
private final ScopedProxyMode defaultProxyMode;
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>AnnotationScopeMetadataResolver</code> class.
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
/**
|
||||
* Utilities for processing {@link Bean}-annotated methods.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
class BeanAnnotationHelper {
|
||||
|
||||
/**
|
||||
* Return whether the given method is annotated directly or indirectly with @Bean.
|
||||
*/
|
||||
public static boolean isBeanAnnotated(Method method) {
|
||||
return AnnotationUtils.findAnnotation(method, Bean.class) != null;
|
||||
}
|
||||
|
||||
public static String determineBeanNameFor(Method beanMethod) {
|
||||
// by default the bean name is the name of the @Bean-annotated method
|
||||
String beanName = beanMethod.getName();
|
||||
|
||||
// check to see if the user has explicitly set the bean name
|
||||
Bean bean = AnnotationUtils.findAnnotation(beanMethod, Bean.class);
|
||||
if (bean != null && bean.name().length > 0) {
|
||||
beanName = bean.name()[0];
|
||||
}
|
||||
|
||||
return beanName;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -208,9 +208,10 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
|||
* <p>This method does <i>not</i> register an annotation config processor
|
||||
* but rather leaves this up to the caller.
|
||||
* @param basePackages the packages to check for annotated classes
|
||||
* @return number of beans registered
|
||||
* @return set of beans registered if any for tooling registration purposes (never {@code null})
|
||||
*/
|
||||
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
|
||||
Assert.notEmpty(basePackages, "At least one base package must be specified");
|
||||
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
|
||||
for (String basePackage : basePackages) {
|
||||
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
|
||||
|
|
|
@ -69,8 +69,7 @@ import org.springframework.util.ClassUtils;
|
|||
*/
|
||||
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
|
||||
|
||||
private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
||||
|
||||
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -16,59 +16,137 @@
|
|||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
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.support.BeanNameGenerator;
|
||||
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
|
||||
/**
|
||||
* Configures component scanning directives for use with {@link Configuration}
|
||||
* classes. Provides support parallel with Spring XML's
|
||||
* {@code <context:component-scan>} element.
|
||||
* Configures component scanning directives for use with {@link Configuration @Configuration}
|
||||
* classes. Provides support parallel with Spring XML's {@code <context:component-scan>}
|
||||
* element.
|
||||
*
|
||||
* TODO SPR-7508: complete documentation.
|
||||
* <p>One of {@link #basePackageClasses()}, {@link #basePackages()} or its alias {@link #value()}
|
||||
* must be specified.
|
||||
*
|
||||
* <p>Note that the {@code <context:component-scan>} element has an {@code annotation-config}
|
||||
* attribute, however this annotation does not. This is because in almost all cases when
|
||||
* using {@code @ComponentScan}, default annotation config processing (e.g.
|
||||
* processing {@code @Autowired} and friends) is assumed. Furthermore, when using
|
||||
* {@link AnnotationConfigApplicationContext}, annotation config processors are always
|
||||
* registered, meaning that any attempt to disable them at the {@code @ComponentScan} level
|
||||
* would be ignored.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see FilterType
|
||||
* @see Configuration
|
||||
*/
|
||||
@Documented
|
||||
@FeatureAnnotation(parser=ComponentScanAnnotationParser.class)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ComponentScan {
|
||||
|
||||
/** base packages to scan */
|
||||
/**
|
||||
* Alias for the {@link #basePackages()} attribute.
|
||||
* Allows for more concise annotation declarations e.g.:
|
||||
* {@code @ComponentScan("org.my.pkg")} instead of
|
||||
* {@code @ComponentScan(basePackages="org.my.pkg")}.
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
Class<?>[] packageOf() default Void.class;
|
||||
/**
|
||||
* Base packages to scan for annotated components.
|
||||
* <p>{@link #value()} is an alias for (and mutually exclusive with) this attribute.
|
||||
* <p>Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names.
|
||||
*/
|
||||
String[] basePackages() default {};
|
||||
|
||||
/**
|
||||
* Type-safe alternative to {@link #basePackages()} for specifying the packages
|
||||
* to scan for annotated components. The package of each class specified will be scanned.
|
||||
* <p>Consider creating a special no-op marker class or interface in each package
|
||||
* that serves no purpose other than being referenced by this attribute.
|
||||
*/
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
|
||||
/**
|
||||
* The {@link BeanNameGenerator} class to be used for naming detected components
|
||||
* within the Spring container.
|
||||
*/
|
||||
Class<? extends BeanNameGenerator> nameGenerator() default AnnotationBeanNameGenerator.class;
|
||||
|
||||
/**
|
||||
* The {@link ScopeMetadataResolver} to be used for resolving the scope of detected components.
|
||||
*/
|
||||
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
|
||||
|
||||
String resourcePattern() default "**/*.class";
|
||||
|
||||
/**
|
||||
* Indicates whether proxies should be generated for detected components, which may be
|
||||
* necessary when using scopes in a proxy-style fashion.
|
||||
* <p>The default is defer to the default behavior of the component scanner used to
|
||||
* execute the actual scan.
|
||||
* @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)
|
||||
*/
|
||||
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
|
||||
|
||||
/**
|
||||
* Controls the class files eligible for component detection.
|
||||
* <p>Consider use of {@link #includeFilters()} and {@link #excludeFilters()}
|
||||
* for a more flexible approach.
|
||||
*/
|
||||
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
|
||||
|
||||
/**
|
||||
* Indicates whether automatic detection of classes annotated with {@code @Component}
|
||||
* {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled.
|
||||
*/
|
||||
boolean useDefaultFilters() default true;
|
||||
|
||||
IncludeFilter[] includeFilters() default {};
|
||||
/**
|
||||
* Specifies which types are eligible for component scanning.
|
||||
* <p>Further narrows the set of candidate components from everything in
|
||||
* {@link #basePackages()} to everything in the base packages that matches
|
||||
* the given filter or filters.
|
||||
* @see #resourcePattern()
|
||||
*/
|
||||
Filter[] includeFilters() default {};
|
||||
|
||||
ExcludeFilter[] excludeFilters() default {};
|
||||
/**
|
||||
* Specifies which types are not eligible for component scanning.
|
||||
* @see #resourcePattern()
|
||||
*/
|
||||
Filter[] excludeFilters() default {};
|
||||
|
||||
|
||||
/**
|
||||
* Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters()
|
||||
* include filter} or {@linkplain ComponentScan#includeFilters() exclude filter}.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface IncludeFilter {
|
||||
@interface Filter {
|
||||
/**
|
||||
* The type of filter to use.
|
||||
* <p>Note that the filter types available are limited to those that may
|
||||
* be expressed as a {@code Class} in the {@link #value()} attribute. This is
|
||||
* in contrast to {@code <context:component-scan/>}, which allows for
|
||||
* expression-based (i.e., string-based) filters such as AspectJ pointcuts.
|
||||
* These filter types are intentionally not supported here, and not available
|
||||
* in the {@link FilterType} enum.
|
||||
* @see FilterType
|
||||
*/
|
||||
FilterType type() default FilterType.ANNOTATION;
|
||||
Class<?> value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface ExcludeFilter {
|
||||
FilterType type() default FilterType.ANNOTATION;
|
||||
/**
|
||||
* The class to use as the filter. In the case of {@link FilterType#ANNOTATION},
|
||||
* the class will be the annotation itself. In the case of
|
||||
* {@link FilterType#ASSIGNABLE_TYPE}, the class will be the type that detected
|
||||
* components should be assignable to. And in the case of {@link FilterType#CUSTOM},
|
||||
* the class will be an implementation of {@link TypeFilter}.
|
||||
*/
|
||||
Class<?> value();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 java.util.Map;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link FeatureAnnotationParser} implementation that reads attributes from a
|
||||
* {@link ComponentScan @ComponentScan} annotation into a {@link ComponentScanSpec}
|
||||
* which can in turn be executed by {@link ComponentScanExecutor}.
|
||||
* {@link ComponentScanBeanDefinitionParser} serves the same role for
|
||||
* the {@code <context:component-scan>} XML element.
|
||||
*
|
||||
* <p>Note that {@link ComponentScanSpec} objects may be directly
|
||||
* instantiated and returned from {@link Feature @Feature} methods as an
|
||||
* alternative to using the {@link ComponentScan @ComponentScan} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see ComponentScan
|
||||
* @see ComponentScanSpec
|
||||
* @see ComponentScanExecutor
|
||||
* @see ComponentScanBeanDefinitionParser
|
||||
* @see ConfigurationClassBeanDefinitionReader
|
||||
*/
|
||||
final class ComponentScanAnnotationParser implements FeatureAnnotationParser {
|
||||
|
||||
/**
|
||||
* Create and return a new {@link ComponentScanSpec} from the given
|
||||
* {@link ComponentScan} annotation metadata.
|
||||
* @throws IllegalArgumentException if ComponentScan attributes are not present in metadata
|
||||
*/
|
||||
public ComponentScanSpec process(AnnotationMetadata metadata) {
|
||||
Map<String, Object> attribs = metadata.getAnnotationAttributes(ComponentScan.class.getName(), true);
|
||||
Assert.notNull(attribs, String.format("@ComponentScan annotation not found " +
|
||||
"while parsing metadata for class [%s].", metadata.getClassName()));
|
||||
|
||||
ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
|
||||
ComponentScanSpec spec = new ComponentScanSpec();
|
||||
|
||||
for (String pkg : (String[])attribs.get("value")) {
|
||||
spec.addBasePackage(pkg);
|
||||
}
|
||||
for (String pkg : (String[])attribs.get("basePackages")) {
|
||||
spec.addBasePackage(pkg);
|
||||
}
|
||||
for (String className : (String[])attribs.get("basePackageClasses")) {
|
||||
spec.addBasePackage(className.substring(0, className.lastIndexOf('.')));
|
||||
}
|
||||
|
||||
String resolverAttribute = "scopeResolver";
|
||||
if (!((String)attribs.get(resolverAttribute)).equals(((Class<?>)AnnotationUtils.getDefaultValue(ComponentScan.class, resolverAttribute)).getName())) {
|
||||
spec.scopeMetadataResolver((String)attribs.get(resolverAttribute), classLoader);
|
||||
}
|
||||
String scopedProxyAttribute = "scopedProxy";
|
||||
ScopedProxyMode scopedProxyMode = (ScopedProxyMode) attribs.get(scopedProxyAttribute);
|
||||
if (scopedProxyMode != ((ScopedProxyMode)AnnotationUtils.getDefaultValue(ComponentScan.class, scopedProxyAttribute))) {
|
||||
spec.scopedProxyMode(scopedProxyMode);
|
||||
}
|
||||
|
||||
for (Filter filter : (Filter[]) attribs.get("includeFilters")) {
|
||||
spec.addIncludeFilter(filter.type().toString(), filter.value().getName(), classLoader);
|
||||
}
|
||||
for (Filter filter : (Filter[]) attribs.get("excludeFilters")) {
|
||||
spec.addExcludeFilter(filter.type().toString(), filter.value().getName(), classLoader);
|
||||
}
|
||||
|
||||
spec.resourcePattern((String)attribs.get("resourcePattern"))
|
||||
.useDefaultFilters((Boolean)attribs.get("useDefaultFilters"))
|
||||
.beanNameGenerator((String)attribs.get("nameGenerator"), classLoader)
|
||||
.source(metadata.getClassName())
|
||||
.sourceName(metadata.getClassName());
|
||||
|
||||
return spec;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -16,264 +16,80 @@
|
|||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.beans.factory.xml.XmlReaderContext;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.beans.factory.xml.XmlReaderContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.AspectJTypeFilter;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.core.type.filter.RegexPatternTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <context:component-scan/> element.
|
||||
*
|
||||
* Parser for the {@code <context:component-scan/>} element. Parsed metadata is
|
||||
* used to populate and execute a {@link ComponentScanSpec} instance.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Ramnivas Laddad
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 2.5
|
||||
* @see ComponentScan
|
||||
* @see ComponentScanSpec
|
||||
* @see ComponentScanExecutor
|
||||
*/
|
||||
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
|
||||
|
||||
private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern";
|
||||
|
||||
private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters";
|
||||
|
||||
private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config";
|
||||
|
||||
private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator";
|
||||
|
||||
private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver";
|
||||
|
||||
private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy";
|
||||
|
||||
private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter";
|
||||
|
||||
private static final String INCLUDE_FILTER_ELEMENT = "include-filter";
|
||||
|
||||
private static final String FILTER_TYPE_ATTRIBUTE = "type";
|
||||
|
||||
private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
|
||||
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
|
||||
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||
|
||||
// Actually scan for bean definitions and register them.
|
||||
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
|
||||
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
|
||||
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
|
||||
XmlReaderContext readerContext = parserContext.getReaderContext();
|
||||
ClassLoader classLoader = readerContext.getResourceLoader().getClassLoader();
|
||||
|
||||
boolean useDefaultFilters = true;
|
||||
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
|
||||
useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
|
||||
}
|
||||
|
||||
// Delegate bean definition registration to scanner class.
|
||||
ClassPathBeanDefinitionScanner scanner = createScanner(readerContext, useDefaultFilters);
|
||||
scanner.setResourceLoader(readerContext.getResourceLoader());
|
||||
scanner.setEnvironment(parserContext.getDelegate().getEnvironment());
|
||||
scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
|
||||
scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
|
||||
|
||||
if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
|
||||
scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
|
||||
}
|
||||
|
||||
try {
|
||||
parseBeanNameGenerator(element, scanner);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
|
||||
}
|
||||
|
||||
try {
|
||||
parseScope(element, scanner);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
|
||||
}
|
||||
|
||||
parseTypeFilters(element, scanner, readerContext, parserContext);
|
||||
|
||||
return scanner;
|
||||
}
|
||||
|
||||
protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
|
||||
return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters);
|
||||
}
|
||||
|
||||
protected void registerComponents(
|
||||
XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
|
||||
|
||||
Object source = readerContext.extractSource(element);
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
|
||||
|
||||
for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
|
||||
}
|
||||
|
||||
// Register annotation config processors, if necessary.
|
||||
boolean annotationConfig = true;
|
||||
if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
|
||||
annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
|
||||
}
|
||||
if (annotationConfig) {
|
||||
Set<BeanDefinitionHolder> processorDefinitions =
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
|
||||
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
|
||||
}
|
||||
}
|
||||
|
||||
readerContext.fireComponentRegistered(compositeDef);
|
||||
}
|
||||
|
||||
protected void parseBeanNameGenerator(Element element, ClassPathBeanDefinitionScanner scanner) {
|
||||
if (element.hasAttribute(NAME_GENERATOR_ATTRIBUTE)) {
|
||||
BeanNameGenerator beanNameGenerator = (BeanNameGenerator) instantiateUserDefinedStrategy(
|
||||
element.getAttribute(NAME_GENERATOR_ATTRIBUTE), BeanNameGenerator.class,
|
||||
scanner.getResourceLoader().getClassLoader());
|
||||
scanner.setBeanNameGenerator(beanNameGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseScope(Element element, ClassPathBeanDefinitionScanner scanner) {
|
||||
// Register ScopeMetadataResolver if class name provided.
|
||||
if (element.hasAttribute(SCOPE_RESOLVER_ATTRIBUTE)) {
|
||||
if (element.hasAttribute(SCOPED_PROXY_ATTRIBUTE)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot define both 'scope-resolver' and 'scoped-proxy' on <component-scan> tag");
|
||||
}
|
||||
ScopeMetadataResolver scopeMetadataResolver = (ScopeMetadataResolver) instantiateUserDefinedStrategy(
|
||||
element.getAttribute(SCOPE_RESOLVER_ATTRIBUTE), ScopeMetadataResolver.class,
|
||||
scanner.getResourceLoader().getClassLoader());
|
||||
scanner.setScopeMetadataResolver(scopeMetadataResolver);
|
||||
}
|
||||
|
||||
if (element.hasAttribute(SCOPED_PROXY_ATTRIBUTE)) {
|
||||
String mode = element.getAttribute(SCOPED_PROXY_ATTRIBUTE);
|
||||
if ("targetClass".equals(mode)) {
|
||||
scanner.setScopedProxyMode(ScopedProxyMode.TARGET_CLASS);
|
||||
}
|
||||
else if ("interfaces".equals(mode)) {
|
||||
scanner.setScopedProxyMode(ScopedProxyMode.INTERFACES);
|
||||
}
|
||||
else if ("no".equals(mode)) {
|
||||
scanner.setScopedProxyMode(ScopedProxyMode.NO);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("scoped-proxy only supports 'no', 'interfaces' and 'targetClass'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseTypeFilters(
|
||||
Element element, ClassPathBeanDefinitionScanner scanner, XmlReaderContext readerContext, ParserContext parserContext) {
|
||||
ComponentScanSpec spec =
|
||||
ComponentScanSpec.forDelimitedPackages(element.getAttribute("base-package"))
|
||||
.includeAnnotationConfig(element.getAttribute("annotation-config"))
|
||||
.useDefaultFilters(element.getAttribute("use-default-filters"))
|
||||
.resourcePattern(element.getAttribute("resource-pattern"))
|
||||
.beanNameGenerator(element.getAttribute("name-generator"), classLoader)
|
||||
.scopeMetadataResolver(element.getAttribute("scope-resolver"), classLoader)
|
||||
.scopedProxyMode(element.getAttribute("scoped-proxy"));
|
||||
|
||||
// Parse exclude and include filter elements.
|
||||
ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
|
||||
NodeList nodeList = element.getChildNodes();
|
||||
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||
Node node = nodeList.item(i);
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String localName = parserContext.getDelegate().getLocalName(node);
|
||||
try {
|
||||
if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
|
||||
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
|
||||
scanner.addIncludeFilter(typeFilter);
|
||||
}
|
||||
else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
|
||||
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
|
||||
scanner.addExcludeFilter(typeFilter);
|
||||
}
|
||||
String filterType = ((Element)node).getAttribute("type");
|
||||
String expression = ((Element)node).getAttribute("expression");
|
||||
if ("include-filter".equals(localName)) {
|
||||
spec.addIncludeFilter(filterType, expression, classLoader);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
|
||||
else if ("exclude-filter".equals(localName)) {
|
||||
spec.addExcludeFilter(filterType, expression, classLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spec.beanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults())
|
||||
.autowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns())
|
||||
.source(readerContext.extractSource(element))
|
||||
.sourceName(element.getTagName())
|
||||
.execute(createExecutorContext(parserContext));
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader) {
|
||||
String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
|
||||
String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
|
||||
try {
|
||||
if ("annotation".equals(filterType)) {
|
||||
return new AnnotationTypeFilter((Class<Annotation>) classLoader.loadClass(expression));
|
||||
}
|
||||
else if ("assignable".equals(filterType)) {
|
||||
return new AssignableTypeFilter(classLoader.loadClass(expression));
|
||||
}
|
||||
else if ("aspectj".equals(filterType)) {
|
||||
return new AspectJTypeFilter(expression, classLoader);
|
||||
}
|
||||
else if ("regex".equals(filterType)) {
|
||||
return new RegexPatternTypeFilter(Pattern.compile(expression));
|
||||
}
|
||||
else if ("custom".equals(filterType)) {
|
||||
Class filterClass = classLoader.loadClass(expression);
|
||||
if (!TypeFilter.class.isAssignableFrom(filterClass)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
|
||||
}
|
||||
return (TypeFilter) BeanUtils.instantiateClass(filterClass);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported filter type: " + filterType);
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new FatalBeanException("Type filter class not found: " + expression, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object instantiateUserDefinedStrategy(String className, Class strategyType, ClassLoader classLoader) {
|
||||
Object result = null;
|
||||
try {
|
||||
result = classLoader.loadClass(className).newInstance();
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalArgumentException("Class [" + className + "] for strategy [" +
|
||||
strategyType.getName() + "] not found", ex);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException("Unable to instantiate class [" + className + "] for strategy [" +
|
||||
strategyType.getName() + "]. A zero-argument constructor is required", ex);
|
||||
}
|
||||
|
||||
if (!strategyType.isAssignableFrom(result.getClass())) {
|
||||
throw new IllegalArgumentException("Provided class name must be an implementation of " + strategyType);
|
||||
}
|
||||
return result;
|
||||
// Adapt the given ParserContext instance into an ExecutorContext.
|
||||
// TODO SPR-7420: create a common ParserContext-to-ExecutorContext adapter utility
|
||||
// or otherwise unify these two types
|
||||
private ExecutorContext createExecutorContext(ParserContext parserContext) {
|
||||
ExecutorContext executorContext = new ExecutorContext();
|
||||
executorContext.setRegistry(parserContext.getRegistry());
|
||||
executorContext.setRegistrar(parserContext);
|
||||
executorContext.setResourceLoader(parserContext.getReaderContext().getResourceLoader());
|
||||
executorContext.setEnvironment(parserContext.getDelegate().getEnvironment());
|
||||
executorContext.setProblemReporter(parserContext.getReaderContext().getProblemReporter());
|
||||
return executorContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.config.AbstractSpecificationExecutor;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
|
||||
/**
|
||||
* Executes the {@link ComponentScanSpec} feature specification.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see ComponentScanSpec
|
||||
* @see ComponentScanBeanDefinitionParser
|
||||
* @see ComponentScan
|
||||
*/
|
||||
final class ComponentScanExecutor extends AbstractSpecificationExecutor<ComponentScanSpec> {
|
||||
|
||||
/**
|
||||
* Configure a {@link ClassPathBeanDefinitionScanner} based on the content of
|
||||
* the given specification and perform actual scanning and bean definition
|
||||
* registration.
|
||||
*/
|
||||
protected void doExecute(ComponentScanSpec spec, ExecutorContext executorContext) {
|
||||
BeanDefinitionRegistry registry = executorContext.getRegistry();
|
||||
ResourceLoader resourceLoader = executorContext.getResourceLoader();
|
||||
Environment environment = executorContext.getEnvironment();
|
||||
|
||||
ClassPathBeanDefinitionScanner scanner = spec.useDefaultFilters() == null ?
|
||||
new ClassPathBeanDefinitionScanner(registry) :
|
||||
new ClassPathBeanDefinitionScanner(registry, spec.useDefaultFilters());
|
||||
|
||||
scanner.setResourceLoader(resourceLoader);
|
||||
scanner.setEnvironment(environment);
|
||||
|
||||
if (spec.beanDefinitionDefaults() != null) {
|
||||
scanner.setBeanDefinitionDefaults(spec.beanDefinitionDefaults());
|
||||
}
|
||||
if (spec.autowireCandidatePatterns() != null) {
|
||||
scanner.setAutowireCandidatePatterns(spec.autowireCandidatePatterns());
|
||||
}
|
||||
|
||||
if (spec.resourcePattern() != null) {
|
||||
scanner.setResourcePattern(spec.resourcePattern());
|
||||
}
|
||||
if (spec.beanNameGenerator() != null) {
|
||||
scanner.setBeanNameGenerator(spec.beanNameGenerator());
|
||||
}
|
||||
if (spec.includeAnnotationConfig() != null) {
|
||||
scanner.setIncludeAnnotationConfig(spec.includeAnnotationConfig());
|
||||
}
|
||||
if (spec.scopeMetadataResolver() != null) {
|
||||
scanner.setScopeMetadataResolver(spec.scopeMetadataResolver());
|
||||
}
|
||||
if (spec.scopedProxyMode() != null) {
|
||||
scanner.setScopedProxyMode(spec.scopedProxyMode());
|
||||
}
|
||||
for (TypeFilter filter : spec.includeFilters()) {
|
||||
scanner.addIncludeFilter(filter);
|
||||
}
|
||||
for (TypeFilter filter : spec.excludeFilters()) {
|
||||
scanner.addExcludeFilter(filter);
|
||||
}
|
||||
|
||||
Set<BeanDefinitionHolder> scannedBeans = scanner.doScan(spec.basePackages());
|
||||
|
||||
Object source = spec.source();
|
||||
String sourceName = spec.sourceName();
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(sourceName, source);
|
||||
|
||||
for (BeanDefinitionHolder beanDefHolder : scannedBeans) {
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
|
||||
}
|
||||
|
||||
// Register annotation config processors, if necessary.
|
||||
if ((spec.includeAnnotationConfig() != null) && spec.includeAnnotationConfig()) {
|
||||
Set<BeanDefinitionHolder> processorDefinitions =
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(registry, source);
|
||||
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
|
||||
}
|
||||
}
|
||||
|
||||
executorContext.getRegistrar().registerComponent(compositeDef);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.parsing.SimpleProblemCollector;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionDefaults;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.AspectJTypeFilter;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.core.type.filter.RegexPatternTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Specifies the configuration of Spring's <em>component-scanning</em> feature.
|
||||
* May be used directly within a {@link Feature @Feature} method, or indirectly
|
||||
* through the {@link ComponentScan @ComponentScan} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see ComponentScan
|
||||
* @see ComponentScanAnnotationParser
|
||||
* @see ComponentScanBeanDefinitionParser
|
||||
* @see ComponentScanExecutor
|
||||
*/
|
||||
public final class ComponentScanSpec extends AbstractFeatureSpecification {
|
||||
|
||||
private static final Class<? extends FeatureSpecificationExecutor> EXECUTOR_TYPE = ComponentScanExecutor.class;
|
||||
|
||||
private Boolean includeAnnotationConfig = null;
|
||||
private String resourcePattern = null;
|
||||
private List<String> basePackages = new ArrayList<String>();
|
||||
private Object beanNameGenerator = null;
|
||||
private Object scopeMetadataResolver = null;
|
||||
private Object scopedProxyMode = null;
|
||||
private Boolean useDefaultFilters = null;
|
||||
private List<Object> includeFilters = new ArrayList<Object>();
|
||||
private List<Object> excludeFilters = new ArrayList<Object>();
|
||||
|
||||
private BeanDefinitionDefaults beanDefinitionDefaults;
|
||||
private String[] autowireCandidatePatterns;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* Package-visible constructor for use by {@link ComponentScanBeanDefinitionParser}.
|
||||
* End users should always call String... or Class<?>... constructors to specify
|
||||
* base packages.
|
||||
*
|
||||
* @see #validate()
|
||||
*/
|
||||
ComponentScanSpec() {
|
||||
super(EXECUTOR_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param basePackages
|
||||
* @see #forDelimitedPackages(String)
|
||||
*/
|
||||
public ComponentScanSpec(String... basePackages) {
|
||||
this();
|
||||
Assert.notEmpty(basePackages, "At least one base package must be specified");
|
||||
for (String basePackage : basePackages) {
|
||||
addBasePackage(basePackage);
|
||||
}
|
||||
}
|
||||
|
||||
public ComponentScanSpec(Class<?>... basePackageClasses) {
|
||||
this(packagesFor(basePackageClasses));
|
||||
}
|
||||
|
||||
|
||||
public ComponentScanSpec includeAnnotationConfig(Boolean includeAnnotationConfig) {
|
||||
this.includeAnnotationConfig = includeAnnotationConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
ComponentScanSpec includeAnnotationConfig(String includeAnnotationConfig) {
|
||||
if (StringUtils.hasText(includeAnnotationConfig)) {
|
||||
this.includeAnnotationConfig = Boolean.valueOf(includeAnnotationConfig);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Boolean includeAnnotationConfig() {
|
||||
return this.includeAnnotationConfig;
|
||||
}
|
||||
|
||||
public ComponentScanSpec resourcePattern(String resourcePattern) {
|
||||
if (StringUtils.hasText(resourcePattern)) {
|
||||
this.resourcePattern = resourcePattern;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
String resourcePattern() {
|
||||
return resourcePattern;
|
||||
}
|
||||
|
||||
ComponentScanSpec addBasePackage(String basePackage) {
|
||||
if (StringUtils.hasText(basePackage)) {
|
||||
this.basePackages.add(basePackage);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of base packages specified, never {@code null}, never empty
|
||||
* post-validation.
|
||||
* @see #doValidate(SimpleProblemReporter)
|
||||
*/
|
||||
String[] basePackages() {
|
||||
return this.basePackages.toArray(new String[this.basePackages.size()]);
|
||||
}
|
||||
|
||||
public ComponentScanSpec beanNameGenerator(BeanNameGenerator beanNameGenerator) {
|
||||
this.beanNameGenerator = beanNameGenerator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class name of the BeanNameGenerator to be used and the ClassLoader
|
||||
* to load it.
|
||||
*/
|
||||
ComponentScanSpec beanNameGenerator(String beanNameGenerator, ClassLoader classLoader) {
|
||||
setClassLoader(classLoader);
|
||||
if (StringUtils.hasText(beanNameGenerator)) {
|
||||
this.beanNameGenerator = beanNameGenerator;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
BeanNameGenerator beanNameGenerator() {
|
||||
return nullSafeTypedObject(this.beanNameGenerator, BeanNameGenerator.class);
|
||||
}
|
||||
|
||||
public ComponentScanSpec scopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
|
||||
this.scopeMetadataResolver = scopeMetadataResolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
ComponentScanSpec scopeMetadataResolver(String scopeMetadataResolver, ClassLoader classLoader) {
|
||||
setClassLoader(classLoader);
|
||||
if (StringUtils.hasText(scopeMetadataResolver)) {
|
||||
this.scopeMetadataResolver = scopeMetadataResolver;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
ScopeMetadataResolver scopeMetadataResolver() {
|
||||
return nullSafeTypedObject(this.scopeMetadataResolver, ScopeMetadataResolver.class);
|
||||
}
|
||||
|
||||
public ComponentScanSpec scopedProxyMode(ScopedProxyMode scopedProxyMode) {
|
||||
this.scopedProxyMode = scopedProxyMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
ComponentScanSpec scopedProxyMode(String scopedProxyMode) {
|
||||
if (StringUtils.hasText(scopedProxyMode)) {
|
||||
this.scopedProxyMode = scopedProxyMode;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
ScopedProxyMode scopedProxyMode() {
|
||||
return nullSafeTypedObject(this.scopedProxyMode, ScopedProxyMode.class);
|
||||
}
|
||||
|
||||
public ComponentScanSpec useDefaultFilters(Boolean useDefaultFilters) {
|
||||
this.useDefaultFilters = useDefaultFilters;
|
||||
return this;
|
||||
}
|
||||
|
||||
ComponentScanSpec useDefaultFilters(String useDefaultFilters) {
|
||||
if (StringUtils.hasText(useDefaultFilters)) {
|
||||
this.useDefaultFilters = Boolean.valueOf(useDefaultFilters);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Boolean useDefaultFilters() {
|
||||
return this.useDefaultFilters;
|
||||
}
|
||||
|
||||
public ComponentScanSpec includeFilters(TypeFilter... includeFilters) {
|
||||
this.includeFilters.clear();
|
||||
for (TypeFilter filter : includeFilters) {
|
||||
addIncludeFilter(filter);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
ComponentScanSpec addIncludeFilter(TypeFilter includeFilter) {
|
||||
Assert.notNull(includeFilter, "includeFilter must not be null");
|
||||
this.includeFilters.add(includeFilter);
|
||||
return this;
|
||||
}
|
||||
|
||||
ComponentScanSpec addIncludeFilter(String filterType, String expression, ClassLoader classLoader) {
|
||||
this.includeFilters.add(new FilterTypeDescriptor(filterType, expression, classLoader));
|
||||
return this;
|
||||
}
|
||||
|
||||
TypeFilter[] includeFilters() {
|
||||
return this.includeFilters.toArray(new TypeFilter[this.includeFilters.size()]);
|
||||
}
|
||||
|
||||
public ComponentScanSpec excludeFilters(TypeFilter... excludeFilters) {
|
||||
this.excludeFilters.clear();
|
||||
for (TypeFilter filter : excludeFilters) {
|
||||
addExcludeFilter(filter);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
ComponentScanSpec addExcludeFilter(TypeFilter excludeFilter) {
|
||||
Assert.notNull(excludeFilter, "excludeFilter must not be null");
|
||||
this.excludeFilters.add(excludeFilter);
|
||||
return this;
|
||||
}
|
||||
|
||||
ComponentScanSpec addExcludeFilter(String filterType, String expression, ClassLoader classLoader) {
|
||||
this.excludeFilters.add(new FilterTypeDescriptor(filterType, expression, classLoader));
|
||||
return this;
|
||||
}
|
||||
|
||||
TypeFilter[] excludeFilters() {
|
||||
return this.excludeFilters.toArray(new TypeFilter[this.excludeFilters.size()]);
|
||||
}
|
||||
|
||||
ComponentScanSpec beanDefinitionDefaults(BeanDefinitionDefaults beanDefinitionDefaults) {
|
||||
this.beanDefinitionDefaults = beanDefinitionDefaults;
|
||||
return this;
|
||||
}
|
||||
|
||||
BeanDefinitionDefaults beanDefinitionDefaults() {
|
||||
return this.beanDefinitionDefaults;
|
||||
}
|
||||
|
||||
ComponentScanSpec autowireCandidatePatterns(String[] autowireCandidatePatterns) {
|
||||
this.autowireCandidatePatterns = autowireCandidatePatterns;
|
||||
return this;
|
||||
}
|
||||
|
||||
String[] autowireCandidatePatterns() {
|
||||
return this.autowireCandidatePatterns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a ComponentScanSpec from a single string containing
|
||||
* delimited package names.
|
||||
* @see ConfigurableApplicationContext#CONFIG_LOCATION_DELIMITERS
|
||||
*/
|
||||
static ComponentScanSpec forDelimitedPackages(String basePackages) {
|
||||
Assert.notNull(basePackages, "base packages must not be null");
|
||||
return new ComponentScanSpec(
|
||||
StringUtils.tokenizeToStringArray(basePackages,
|
||||
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
|
||||
}
|
||||
|
||||
public void doValidate(SimpleProblemCollector problems) {
|
||||
if(this.basePackages.isEmpty()) {
|
||||
problems.error("At least one base package must be specified");
|
||||
}
|
||||
|
||||
if(this.beanNameGenerator instanceof String) {
|
||||
this.beanNameGenerator = instantiateUserDefinedType("bean name generator", BeanNameGenerator.class, this.beanNameGenerator, this.classLoader, problems);
|
||||
}
|
||||
|
||||
if(this.scopeMetadataResolver instanceof String) {
|
||||
this.scopeMetadataResolver = instantiateUserDefinedType("scope metadata resolver", ScopeMetadataResolver.class, this.scopeMetadataResolver, this.classLoader, problems);
|
||||
}
|
||||
|
||||
if (this.scopedProxyMode instanceof String) {
|
||||
if ("targetClass".equalsIgnoreCase((String)this.scopedProxyMode)) {
|
||||
this.scopedProxyMode = ScopedProxyMode.TARGET_CLASS;
|
||||
}
|
||||
else if ("interfaces".equalsIgnoreCase((String)this.scopedProxyMode)) {
|
||||
this.scopedProxyMode = ScopedProxyMode.INTERFACES;
|
||||
}
|
||||
else if ("no".equalsIgnoreCase((String)this.scopedProxyMode)) {
|
||||
this.scopedProxyMode = ScopedProxyMode.NO;
|
||||
}
|
||||
else {
|
||||
problems.error("invalid scoped proxy mode [%s] supported modes are " +
|
||||
"'no', 'interfaces' and 'targetClass'");
|
||||
this.scopedProxyMode = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.scopeMetadataResolver != null && this.scopedProxyMode != null) {
|
||||
problems.error("Cannot define both scope metadata resolver and scoped proxy mode");
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.includeFilters.size(); i++) {
|
||||
if (this.includeFilters.get(i) instanceof FilterTypeDescriptor) {
|
||||
this.includeFilters.set(i, ((FilterTypeDescriptor)this.includeFilters.get(i)).createTypeFilter(problems));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.excludeFilters.size(); i++) {
|
||||
if (this.excludeFilters.get(i) instanceof FilterTypeDescriptor) {
|
||||
this.excludeFilters.set(i, ((FilterTypeDescriptor)this.excludeFilters.get(i)).createTypeFilter(problems));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Object instantiateUserDefinedType(String description, Class<?> targetType, Object className, ClassLoader classLoader, SimpleProblemCollector problems) {
|
||||
Assert.isInstanceOf(String.class, className, "userType must be of type String");
|
||||
Assert.notNull(classLoader, "classLoader must not be null");
|
||||
Assert.notNull(targetType, "targetType must not be null");
|
||||
Object instance = null;
|
||||
try {
|
||||
instance = classLoader.loadClass((String)className).newInstance();
|
||||
if (!targetType.isAssignableFrom(instance.getClass())) {
|
||||
problems.error(description + " class name must be assignable to " + targetType.getSimpleName());
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
problems.error(String.format(description + " class [%s] not found", className), ex);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
problems.error(String.format("Unable to instantiate %s class [%s] for " +
|
||||
"strategy [%s]. Has a no-argument constructor been provided?",
|
||||
description, className, targetType.getClass().getSimpleName()), ex);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private void setClassLoader(ClassLoader classLoader) {
|
||||
Assert.notNull(classLoader, "classLoader must not be null");
|
||||
if (this.classLoader == null) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
else {
|
||||
Assert.isTrue(this.classLoader == classLoader, "A classLoader has already been assigned " +
|
||||
"and the supplied classLoader is not the same instance. Use the same classLoader " +
|
||||
"for all string-based class properties.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T nullSafeTypedObject(Object object, Class<T> type) {
|
||||
if (object != null) {
|
||||
if (!(type.isAssignableFrom(object.getClass()))) {
|
||||
throw new IllegalStateException(
|
||||
String.format("field must be of type %s but was actually of type %s", type, object.getClass()));
|
||||
}
|
||||
}
|
||||
return (T)object;
|
||||
}
|
||||
|
||||
private static String[] packagesFor(Class<?>[] classes) {
|
||||
ArrayList<String> packages = new ArrayList<String>();
|
||||
for (Class<?> clazz : classes) {
|
||||
packages.add(clazz.getPackage().getName());
|
||||
}
|
||||
return packages.toArray(new String[packages.size()]);
|
||||
}
|
||||
|
||||
|
||||
private static class FilterTypeDescriptor {
|
||||
private String filterType;
|
||||
private String expression;
|
||||
private ClassLoader classLoader;
|
||||
|
||||
FilterTypeDescriptor(String filterType, String expression, ClassLoader classLoader) {
|
||||
Assert.notNull(filterType, "filterType must not be null");
|
||||
Assert.notNull(expression, "expression must not be null");
|
||||
Assert.notNull(classLoader, "classLoader must not be null");
|
||||
this.filterType = filterType;
|
||||
this.expression = expression;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
TypeFilter createTypeFilter(SimpleProblemCollector problems) {
|
||||
try {
|
||||
if ("annotation".equalsIgnoreCase(this.filterType)) {
|
||||
return new AnnotationTypeFilter((Class<Annotation>) this.classLoader.loadClass(this.expression));
|
||||
}
|
||||
else if ("assignable".equalsIgnoreCase(this.filterType)
|
||||
|| "assignable_type".equalsIgnoreCase(this.filterType)) {
|
||||
return new AssignableTypeFilter(this.classLoader.loadClass(this.expression));
|
||||
}
|
||||
else if ("aspectj".equalsIgnoreCase(this.filterType)) {
|
||||
return new AspectJTypeFilter(this.expression, this.classLoader);
|
||||
}
|
||||
else if ("regex".equalsIgnoreCase(this.filterType)) {
|
||||
return new RegexPatternTypeFilter(Pattern.compile(this.expression));
|
||||
}
|
||||
else if ("custom".equalsIgnoreCase(this.filterType)) {
|
||||
Class<?> filterClass = this.classLoader.loadClass(this.expression);
|
||||
if (!TypeFilter.class.isAssignableFrom(filterClass)) {
|
||||
problems.error(String.format("custom type filter class [%s] must be assignable to %s",
|
||||
this.expression, TypeFilter.class));
|
||||
}
|
||||
return (TypeFilter) BeanUtils.instantiateClass(filterClass);
|
||||
}
|
||||
else {
|
||||
problems.error(String.format("Unsupported filter type [%s]; supported types are: " +
|
||||
"'annotation', 'assignable[_type]', 'aspectj', 'regex', 'custom'", this.filterType));
|
||||
}
|
||||
} catch (ClassNotFoundException ex) {
|
||||
problems.error("Type filter class not found: " + this.expression, ex);
|
||||
} catch (Exception ex) {
|
||||
problems.error(ex.getMessage(), ex.getCause());
|
||||
}
|
||||
|
||||
return new PlaceholderTypeFilter();
|
||||
}
|
||||
|
||||
|
||||
private class PlaceholderTypeFilter implements TypeFilter {
|
||||
|
||||
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
|
||||
throws IOException {
|
||||
throw new UnsupportedOperationException(
|
||||
String.format("match() method for placeholder type filter for " +
|
||||
"{filterType=%s,expression=%s} should never be invoked",
|
||||
filterType, expression));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -59,7 +59,7 @@ import org.springframework.stereotype.Component;
|
|||
* @see AnnotationConfigApplicationContext
|
||||
* @see org.springframework.context.annotation.Profile
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Component
|
||||
|
|
|
@ -175,5 +175,6 @@ final class ConfigurationClass {
|
|||
getSimpleName(), count, methodName), new Location(getResource(), getMetadata()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -27,12 +27,13 @@ import java.util.Set;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.parsing.Location;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
|
@ -43,23 +44,28 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
|||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Reads a given fully-populated configuration model, registering bean definitions
|
||||
* with the given {@link BeanDefinitionRegistry} based on its contents.
|
||||
* Reads a given fully-populated set of ConfigurationClass instances, registering bean
|
||||
* definitions with the given {@link BeanDefinitionRegistry} based on its contents.
|
||||
*
|
||||
* <p>This class was modeled after the {@link BeanDefinitionReader} hierarchy, but does
|
||||
* not implement/extend any of its artifacts as a configuration model is not a {@link Resource}.
|
||||
* not implement/extend any of its artifacts as a set of configuration classes is not a
|
||||
* {@link Resource}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
|
@ -85,6 +91,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
|
||||
private final MetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
private ExecutorContext executorContext;
|
||||
|
||||
/**
|
||||
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
|
||||
|
@ -92,13 +99,20 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
* @param problemReporter
|
||||
* @param metadataReaderFactory
|
||||
*/
|
||||
public ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
|
||||
ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory) {
|
||||
public ConfigurationClassBeanDefinitionReader(final BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
|
||||
ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory,
|
||||
ResourceLoader resourceLoader, Environment environment) {
|
||||
|
||||
this.registry = registry;
|
||||
this.sourceExtractor = sourceExtractor;
|
||||
this.problemReporter = problemReporter;
|
||||
this.metadataReaderFactory = metadataReaderFactory;
|
||||
this.executorContext = new ExecutorContext();
|
||||
this.executorContext.setRegistry(this.registry);
|
||||
this.executorContext.setRegistrar(new SimpleComponentRegistrar(this.registry));
|
||||
this.executorContext.setResourceLoader(resourceLoader);
|
||||
this.executorContext.setEnvironment(environment);
|
||||
this.executorContext.setProblemReporter(problemReporter);
|
||||
}
|
||||
|
||||
|
||||
|
@ -117,15 +131,38 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
* class itself, all its {@link Bean} methods
|
||||
*/
|
||||
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
|
||||
AnnotationMetadata metadata = configClass.getMetadata();
|
||||
processFeatureAnnotations(metadata);
|
||||
doLoadBeanDefinitionForConfigurationClassIfNecessary(configClass);
|
||||
for (ConfigurationClassMethod method : configClass.getMethods()) {
|
||||
loadBeanDefinitionsForModelMethod(method);
|
||||
for (ConfigurationClassMethod beanMethod : configClass.getMethods()) {
|
||||
loadBeanDefinitionsForBeanMethod(beanMethod);
|
||||
}
|
||||
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
|
||||
}
|
||||
|
||||
private void processFeatureAnnotations(AnnotationMetadata metadata) {
|
||||
try {
|
||||
for (String annotationType : metadata.getAnnotationTypes()) {
|
||||
MetadataReader metadataReader = new SimpleMetadataReaderFactory().getMetadataReader(annotationType);
|
||||
if (metadataReader.getAnnotationMetadata().isAnnotated(FeatureAnnotation.class.getName())) {
|
||||
Map<String, Object> annotationAttributes = metadataReader.getAnnotationMetadata().getAnnotationAttributes(FeatureAnnotation.class.getName(), true);
|
||||
// TODO SPR-7420: this is where we can catch user-defined types and avoid instantiating them for STS purposes
|
||||
FeatureAnnotationParser processor = (FeatureAnnotationParser) BeanUtils.instantiateClass(Class.forName((String)annotationAttributes.get("parser")));
|
||||
FeatureSpecification spec = processor.process(metadata);
|
||||
spec.execute(this.executorContext);
|
||||
}
|
||||
}
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// TODO SPR-7420: what exception to throw?
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the {@link Configuration} class itself as a bean definition.
|
||||
* Register the {@link Configuration} class itself as a bean definition.
|
||||
*/
|
||||
private void doLoadBeanDefinitionForConfigurationClassIfNecessary(ConfigurationClass configClass) {
|
||||
if (configClass.getBeanName() != null) {
|
||||
|
@ -161,9 +198,9 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
* Read a particular {@link ConfigurationClassMethod}, registering bean definitions
|
||||
* with the BeanDefinitionRegistry based on its contents.
|
||||
*/
|
||||
private void loadBeanDefinitionsForModelMethod(ConfigurationClassMethod method) {
|
||||
ConfigurationClass configClass = method.getConfigurationClass();
|
||||
MethodMetadata metadata = method.getMetadata();
|
||||
private void loadBeanDefinitionsForBeanMethod(ConfigurationClassMethod beanMethod) {
|
||||
ConfigurationClass configClass = beanMethod.getConfigurationClass();
|
||||
MethodMetadata metadata = beanMethod.getMetadata();
|
||||
|
||||
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass);
|
||||
beanDef.setResource(configClass.getResource());
|
||||
|
@ -176,7 +213,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
// consider name and any aliases
|
||||
Map<String, Object> beanAttributes = metadata.getAnnotationAttributes(Bean.class.getName());
|
||||
List<String> names = new ArrayList<String>(Arrays.asList((String[]) beanAttributes.get("name")));
|
||||
String beanName = (names.size() > 0 ? names.remove(0) : method.getMetadata().getMethodName());
|
||||
String beanName = (names.size() > 0 ? names.remove(0) : beanMethod.getMetadata().getMethodName());
|
||||
for (String alias : names) {
|
||||
this.registry.registerAlias(beanName, alias);
|
||||
}
|
||||
|
@ -190,7 +227,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
// overriding is legal, return immediately
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Skipping loading bean definition for %s: a definition for bean " +
|
||||
"'%s' already exists. This is likely due to an override in XML.", method, beanName));
|
||||
"'%s' already exists. This is likely due to an override in XML.", beanMethod, beanName));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -255,7 +292,8 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
|
||||
registry.registerBeanDefinition(beanName, beanDefToRegister);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void loadBeanDefinitionsFromImportedResources(Map<String, Class<?>> importedResources) {
|
||||
Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<Class<?>, BeanDefinitionReader>();
|
||||
for (Map.Entry<String, Class<?>> entry : importedResources.entrySet()) {
|
||||
|
@ -357,7 +395,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
|
||||
@Override
|
||||
public boolean isFactoryMethod(Method candidate) {
|
||||
return (super.isFactoryMethod(candidate) && AnnotationUtils.findAnnotation(candidate, Bean.class) != null);
|
||||
return (super.isFactoryMethod(candidate) && BeanAnnotationHelper.isBeanAnnotated(candidate));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -372,14 +410,12 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
* declare at least one {@link Bean @Bean} method.
|
||||
*/
|
||||
private static class InvalidConfigurationImportProblem extends Problem {
|
||||
|
||||
public InvalidConfigurationImportProblem(String className, Resource resource, AnnotationMetadata metadata) {
|
||||
super(String.format("%s was imported as a Configuration class but is not annotated " +
|
||||
"with @Configuration nor does it declare any @Bean methods. Update the class to " +
|
||||
"meet either of these requirements or do not attempt to import it.", className),
|
||||
super(String.format("%s was @Import'ed as a but is not annotated with @FeatureConfiguration or " +
|
||||
"@Configuration nor does it declare any @Bean methods. Update the class to " +
|
||||
"meet one of these requirements or do not attempt to @Import it.", className),
|
||||
new Location(resource, metadata));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ class ConfigurationClassEnhancer {
|
|||
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
||||
callbackFilter = new CallbackFilter() {
|
||||
public int accept(Method candidateMethod) {
|
||||
return (AnnotationUtils.findAnnotation(candidateMethod, Bean.class) != null ? 0 : 1);
|
||||
return (BeanAnnotationHelper.isBeanAnnotated(candidateMethod) ? 0 : 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -162,19 +162,21 @@ class ConfigurationClassEnhancer {
|
|||
/**
|
||||
* Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
|
||||
* existence of this bean object.
|
||||
*
|
||||
* @throws ProxyCreationException if an early bean reference proxy should be
|
||||
* created but the return type of the bean method being intercepted is not an
|
||||
* interface and thus not a candidate for JDK proxy creation.
|
||||
* @throws Throwable as a catch-all for any exception that may be thrown when
|
||||
* invoking the super implementation of the proxied method i.e., the actual
|
||||
* {@code @Bean} method.
|
||||
*/
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
// by default the bean name is the name of the @Bean-annotated method
|
||||
String beanName = method.getName();
|
||||
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
|
||||
MethodProxy cglibMethodProxy) throws ProxyCreationException, Throwable {
|
||||
|
||||
// check to see if the user has explicitly set the bean name
|
||||
Bean bean = AnnotationUtils.findAnnotation(method, Bean.class);
|
||||
if (bean != null && bean.name().length > 0) {
|
||||
beanName = bean.name()[0];
|
||||
}
|
||||
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
|
||||
|
||||
// determine whether this bean is a scoped-proxy
|
||||
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
|
||||
Scope scope = AnnotationUtils.findAnnotation(beanMethod, Scope.class);
|
||||
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
|
||||
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
|
||||
if (this.beanFactory.isCurrentlyInCreation(scopedBeanName)) {
|
||||
|
@ -206,8 +208,7 @@ class ConfigurationClassEnhancer {
|
|||
return this.beanFactory.getBean(beanName);
|
||||
}
|
||||
|
||||
// no cached instance of the bean exists - actually create and return the bean
|
||||
return proxy.invokeSuper(obj, args);
|
||||
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -67,7 +67,7 @@ final class ConfigurationClassMethod {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[%s:name=%s,declaringClass=%s]",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -16,9 +16,15 @@
|
|||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -26,10 +32,12 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.beans.factory.parsing.PassThroughSourceExtractor;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
|
@ -38,12 +46,24 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
|||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.context.config.SourceAwareSpecification;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
|
||||
|
@ -63,7 +83,7 @@ import org.springframework.util.ClassUtils;
|
|||
* @since 3.0
|
||||
*/
|
||||
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
|
||||
BeanClassLoaderAware, EnvironmentAware {
|
||||
ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
|
||||
|
||||
/** Whether the CGLIB2 library is present on the classpath */
|
||||
private static final boolean cglibAvailable = ClassUtils.isPresent(
|
||||
|
@ -76,6 +96,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
|
||||
private ProblemReporter problemReporter = new FailFastProblemReporter();
|
||||
|
||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
|
||||
|
@ -88,6 +110,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
|
||||
private Environment environment;
|
||||
|
||||
private ConfigurationClassBeanDefinitionReader reader;
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@link SourceExtractor} to use for generated bean definitions
|
||||
|
@ -118,6 +142,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
this.setMetadataReaderFactoryCalled = true;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
if (!this.setMetadataReaderFactoryCalled) {
|
||||
|
@ -126,6 +155,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
}
|
||||
|
||||
public void setEnvironment(Environment environment) {
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
|
@ -143,7 +173,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
"postProcessBeanFactory already called for this post-processor");
|
||||
}
|
||||
this.postProcessBeanDefinitionRegistryCalled = true;
|
||||
processConfigBeanDefinitions(registry);
|
||||
processConfigurationClasses(registry);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,18 +188,196 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
this.postProcessBeanFactoryCalled = true;
|
||||
if (!this.postProcessBeanDefinitionRegistryCalled) {
|
||||
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
|
||||
// Simply call processConfigBeanDefinitions lazily at this point then.
|
||||
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
|
||||
// Simply call processConfigurationClasses lazily at this point then.
|
||||
processConfigurationClasses((BeanDefinitionRegistry)beanFactory);
|
||||
}
|
||||
enhanceConfigurationClasses(beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and process all @Configuration classes with @Feature methods in the given registry.
|
||||
*/
|
||||
private void processConfigurationClasses(BeanDefinitionRegistry registry) {
|
||||
ConfigurationClassBeanDefinitionReader reader = getConfigurationClassBeanDefinitionReader(registry);
|
||||
processConfigBeanDefinitions(registry, reader);
|
||||
enhanceConfigurationClasses((ConfigurableListableBeanFactory)registry);
|
||||
processFeatureConfigurationClasses((ConfigurableListableBeanFactory) registry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process any @FeatureConfiguration classes
|
||||
*/
|
||||
private void processFeatureConfigurationClasses(final ConfigurableListableBeanFactory beanFactory) {
|
||||
Map<String, Object> featureConfigBeans = retrieveFeatureConfigurationBeans(beanFactory);
|
||||
|
||||
if (featureConfigBeans.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final Object featureConfigBean : featureConfigBeans.values()) {
|
||||
checkForBeanMethods(featureConfigBean.getClass());
|
||||
}
|
||||
|
||||
if (!cglibAvailable) {
|
||||
throw new IllegalStateException("CGLIB is required to process @FeatureConfiguration classes. " +
|
||||
"Either add CGLIB to the classpath or remove the following @FeatureConfiguration bean definitions: " +
|
||||
featureConfigBeans.keySet());
|
||||
}
|
||||
|
||||
final EarlyBeanReferenceProxyCreator proxyCreator = new EarlyBeanReferenceProxyCreator(beanFactory);
|
||||
final ExecutorContext executorContext = createExecutorContext(beanFactory);
|
||||
|
||||
for (final Object featureConfigBean : featureConfigBeans.values()) {
|
||||
ReflectionUtils.doWithMethods(featureConfigBean.getClass(),
|
||||
new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method featureMethod) throws IllegalArgumentException, IllegalAccessException {
|
||||
processFeatureMethod(featureMethod, featureConfigBean, executorContext, proxyCreator);
|
||||
} },
|
||||
new ReflectionUtils.MethodFilter() {
|
||||
public boolean matches(Method candidateMethod) {
|
||||
return candidateMethod.isAnnotationPresent(Feature.class);
|
||||
} });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternative to {@link ListableBeanFactory#getBeansWithAnnotation(Class)} that avoids
|
||||
* instantiating FactoryBean objects. FeatureConfiguration types cannot be registered as
|
||||
* FactoryBeans, so ignoring them won't cause a problem. On the other hand, using gBWA()
|
||||
* at this early phase of the container would cause all @Bean methods to be invoked, as they
|
||||
* are ultimately FactoryBeans underneath.
|
||||
*/
|
||||
private Map<String, Object> retrieveFeatureConfigurationBeans(ConfigurableListableBeanFactory beanFactory) {
|
||||
Map<String, Object> fcBeans = new HashMap<String, Object>();
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||
if (isFeatureConfiguration(beanDef)) {
|
||||
fcBeans.put(beanName, beanFactory.getBean(beanName));
|
||||
}
|
||||
}
|
||||
return fcBeans;
|
||||
}
|
||||
|
||||
private boolean isFeatureConfiguration(BeanDefinition candidate) {
|
||||
if (!(candidate instanceof AbstractBeanDefinition) || (candidate.getBeanClassName() == null)) {
|
||||
return false;
|
||||
}
|
||||
AbstractBeanDefinition beanDef = (AbstractBeanDefinition) candidate;
|
||||
if (beanDef.hasBeanClass()) {
|
||||
Class<?> beanClass = beanDef.getBeanClass();
|
||||
if (AnnotationUtils.findAnnotation(beanClass, FeatureConfiguration.class) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// in the case of @FeatureConfiguration classes included with @Import the bean class name
|
||||
// will still be in String form. Since we don't know whether the current bean definition
|
||||
// is a @FeatureConfiguration or not, carefully check for the annotation using ASM instead
|
||||
// eager classloading.
|
||||
String className = null;
|
||||
try {
|
||||
className = beanDef.getBeanClassName();
|
||||
MetadataReader metadataReader = new SimpleMetadataReaderFactory().getMetadataReader(className);
|
||||
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
|
||||
if (annotationMetadata.isAnnotated(FeatureConfiguration.class.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Could not create MetadataReader for class " + className, ex);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkForBeanMethods(final Class<?> featureConfigClass) {
|
||||
ReflectionUtils.doWithMethods(featureConfigClass,
|
||||
new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method beanMethod) throws IllegalArgumentException, IllegalAccessException {
|
||||
throw new FeatureMethodExecutionException(
|
||||
format("@FeatureConfiguration classes must not contain @Bean-annotated methods. " +
|
||||
"%s.%s() is annotated with @Bean and must be removed in order to proceed. " +
|
||||
"Consider moving this method into a dedicated @Configuration class and " +
|
||||
"injecting the bean as a parameter into any @Feature method(s) that need it.",
|
||||
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
|
||||
} },
|
||||
new ReflectionUtils.MethodFilter() {
|
||||
public boolean matches(Method candidateMethod) {
|
||||
return BeanAnnotationHelper.isBeanAnnotated(candidateMethod);
|
||||
} });
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: this method invokes user-supplied code, which is not going to fly for STS
|
||||
*
|
||||
* consider introducing some kind of check to see if we're in a tooling context and make guesses
|
||||
* based on return type rather than actually invoking the method and processing the the specification
|
||||
* object that returns.
|
||||
* @param beanFactory
|
||||
* @throws SecurityException
|
||||
*/
|
||||
private void processFeatureMethod(final Method featureMethod, Object configInstance,
|
||||
ExecutorContext executorContext, EarlyBeanReferenceProxyCreator proxyCreator) {
|
||||
try {
|
||||
// get the return type
|
||||
if (!(FeatureSpecification.class.isAssignableFrom(featureMethod.getReturnType()))) {
|
||||
// TODO SPR-7420: raise a Problem instead?
|
||||
throw new IllegalArgumentException(
|
||||
"return type from @Feature methods must be assignable to FeatureSpecification");
|
||||
}
|
||||
|
||||
List<Object> beanArgs = new ArrayList<Object>();
|
||||
Class<?>[] parameterTypes = featureMethod.getParameterTypes();
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
MethodParameter mp = new MethodParameter(featureMethod, i);
|
||||
DependencyDescriptor dd = new DependencyDescriptor(mp, true, false);
|
||||
Object proxiedBean = proxyCreator.createProxy(dd);
|
||||
beanArgs.add(proxiedBean);
|
||||
}
|
||||
|
||||
// reflectively invoke that method
|
||||
FeatureSpecification spec;
|
||||
featureMethod.setAccessible(true);
|
||||
spec = (FeatureSpecification) featureMethod.invoke(configInstance, beanArgs.toArray(new Object[beanArgs.size()]));
|
||||
|
||||
Assert.notNull(spec,
|
||||
format("The specification returned from @Feature method %s.%s() must not be null",
|
||||
featureMethod.getDeclaringClass().getSimpleName(), featureMethod.getName()));
|
||||
|
||||
if (spec instanceof SourceAwareSpecification) {
|
||||
((SourceAwareSpecification)spec).source(featureMethod);
|
||||
((SourceAwareSpecification)spec).sourceName(featureMethod.getName());
|
||||
}
|
||||
spec.execute(executorContext);
|
||||
} catch (Exception ex) {
|
||||
throw new FeatureMethodExecutionException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private ExecutorContext createExecutorContext(ConfigurableListableBeanFactory beanFactory) {
|
||||
final BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
|
||||
ExecutorContext executorContext = new ExecutorContext();
|
||||
executorContext.setEnvironment(this.environment);
|
||||
executorContext.setResourceLoader(this.resourceLoader);
|
||||
executorContext.setRegistry(registry);
|
||||
executorContext.setRegistrar(new SimpleComponentRegistrar(registry));
|
||||
// TODO SPR-7420: how to get hold of the current problem reporter here?
|
||||
executorContext.setProblemReporter(new FailFastProblemReporter());
|
||||
return executorContext;
|
||||
}
|
||||
|
||||
private ConfigurationClassBeanDefinitionReader getConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry) {
|
||||
if (this.reader == null) {
|
||||
this.reader = new ConfigurationClassBeanDefinitionReader(
|
||||
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory, this.resourceLoader, this.environment);
|
||||
}
|
||||
return this.reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and validate a configuration model based on the registry of
|
||||
* {@link Configuration} classes.
|
||||
*/
|
||||
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
|
||||
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry, ConfigurationClassBeanDefinitionReader reader) {
|
||||
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
|
||||
for (String beanName : registry.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
||||
|
@ -202,8 +410,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
parser.validate();
|
||||
|
||||
// Read the model and create bean definitions based on its content
|
||||
ConfigurationClassBeanDefinitionReader reader = new ConfigurationClassBeanDefinitionReader(
|
||||
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory);
|
||||
reader.loadBeanDefinitions(parser.getConfigurationClasses());
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
/**
|
||||
* Marker interface indicating that an object is a proxy for a bean referenced
|
||||
* from within a {@link Feature @Feature} method.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface EarlyBeanReferenceProxy {
|
||||
|
||||
Object dereferenceTargetBean();
|
||||
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Creates proxies for beans referenced from within @Feature methods.
|
||||
*
|
||||
* TODO SPR-7420: document
|
||||
* - discuss why proxies are important (avoiding side effects of early instantiation)
|
||||
* - discuss benefits of interface-based proxies over concrete proxies
|
||||
* - make it clear that both of the above are possible
|
||||
* - discuss invocation of @Bean methods and how they too return proxies.
|
||||
* this 'proxy returning a proxy' approach can be confusing at first, but the
|
||||
* implementation should help in making it clear.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
class EarlyBeanReferenceProxyCreator {
|
||||
|
||||
static final String FINAL_CLASS_ERROR_MESSAGE =
|
||||
"Cannot create subclass proxy for bean type %s because it is a final class. " +
|
||||
"Make the class non-final or inject the bean by interface rather than by concrete class.";
|
||||
|
||||
static final String MISSING_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE =
|
||||
"Cannot create subclass proxy for bean type %s because it does not have a no-arg constructor. " +
|
||||
"Add a no-arg constructor or attempt to inject the bean by interface rather than by concrete class.";
|
||||
|
||||
static final String PRIVATE_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE =
|
||||
"Cannot create subclass proxy for bean type %s because its no-arg constructor is private. " +
|
||||
"Increase the visibility of the no-arg constructor or attempt to inject the bean by interface rather " +
|
||||
"than by concrete class.";
|
||||
|
||||
private final AutowireCapableBeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new proxy creator that will dereference proxy target beans against
|
||||
* the given bean factory.
|
||||
*/
|
||||
public EarlyBeanReferenceProxyCreator(AutowireCapableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a proxy that will ultimately dereference its target object using
|
||||
* the given dependency descriptor.
|
||||
*/
|
||||
public Object createProxy(DependencyDescriptor descriptor) {
|
||||
return doCreateProxy(new ResolveDependencyTargetBeanDereferencingInterceptor(descriptor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a proxy that looks up target beans using the given dereferencing interceptor.
|
||||
*
|
||||
* @see EarlyBeanReferenceProxy#dereferenceTargetBean()
|
||||
*/
|
||||
private Object doCreateProxy(TargetBeanDereferencingInterceptor targetBeanDereferencingInterceptor) {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
Class<?> targetBeanType = targetBeanDereferencingInterceptor.getTargetBeanType();
|
||||
if (targetBeanType.isInterface()) {
|
||||
enhancer.setSuperclass(Object.class);
|
||||
enhancer.setInterfaces(new Class<?>[] {targetBeanType, EarlyBeanReferenceProxy.class});
|
||||
} else {
|
||||
assertClassIsProxyCapable(targetBeanType);
|
||||
enhancer.setSuperclass(targetBeanType);
|
||||
enhancer.setInterfaces(new Class<?>[] {EarlyBeanReferenceProxy.class});
|
||||
}
|
||||
enhancer.setCallbacks(new Callback[] {
|
||||
new BeanMethodInterceptor(),
|
||||
new ObjectMethodsInterceptor(),
|
||||
targetBeanDereferencingInterceptor,
|
||||
new TargetBeanDelegatingMethodInterceptor()
|
||||
});
|
||||
enhancer.setCallbackFilter(new CallbackFilter() {
|
||||
public int accept(Method method) {
|
||||
if (BeanAnnotationHelper.isBeanAnnotated(method)) {
|
||||
return 0;
|
||||
}
|
||||
if (ReflectionUtils.isObjectMethod(method)) {
|
||||
return 1;
|
||||
}
|
||||
if (method.getName().equals("dereferenceTargetBean")) {
|
||||
return 2;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
});
|
||||
return enhancer.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given class is capable of being subclass proxied by CGLIB.
|
||||
*/
|
||||
private static void assertClassIsProxyCapable(Class<?> clazz) {
|
||||
Assert.isTrue(!clazz.isInterface(), "class parameter must be a concrete type");
|
||||
if ((clazz.getModifiers() & Modifier.FINAL) != 0) {
|
||||
throw new ProxyCreationException(String.format(FINAL_CLASS_ERROR_MESSAGE, clazz.getName()));
|
||||
}
|
||||
try {
|
||||
// attempt to retrieve the no-arg constructor for the class
|
||||
Constructor<?> noArgCtor = clazz.getDeclaredConstructor();
|
||||
if ((noArgCtor.getModifiers() & Modifier.PRIVATE) != 0) {
|
||||
throw new ProxyCreationException(String.format(PRIVATE_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE, clazz.getName()));
|
||||
}
|
||||
} catch (NoSuchMethodException ex) {
|
||||
throw new ProxyCreationException(String.format(MISSING_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE, clazz.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interceptor for @Bean-annotated methods called from early-proxied bean instances, such as
|
||||
* @Configuration class instances. Invoking instance methods on early-proxied beans usually
|
||||
* causes an eager bean lookup, but in the case of @Bean methods, it is important to return
|
||||
* a proxy.
|
||||
*/
|
||||
private class BeanMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
public Object intercept(Object obj, final Method beanMethod, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
return doCreateProxy(new ByNameLookupTargetBeanDereferencingInterceptor(beanMethod));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interceptor for methods declared by java.lang.Object()
|
||||
*/
|
||||
private static class ObjectMethodsInterceptor implements MethodInterceptor {
|
||||
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
if (method.getName().equals("toString")) {
|
||||
return String.format("EarlyBeanReferenceProxy for bean of type %s",
|
||||
obj.getClass().getSuperclass().getSimpleName());
|
||||
}
|
||||
if (method.getName().equals("hashCode")) {
|
||||
return System.identityHashCode(obj);
|
||||
}
|
||||
if (method.getName().equals("equals")) {
|
||||
return obj == args[0];
|
||||
}
|
||||
if (method.getName().equals("finalize")) {
|
||||
return null;
|
||||
}
|
||||
return proxy.invokeSuper(obj, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Strategy interface allowing for various approaches to dereferencing (i.e. 'looking up')
|
||||
* the target bean for an early bean reference proxy.
|
||||
*
|
||||
* @see EarlyBeanReferenceProxy#dereferenceTargetBean()
|
||||
*/
|
||||
private interface TargetBeanDereferencingInterceptor extends MethodInterceptor {
|
||||
Class<?> getTargetBeanType();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interceptor that dereferences the target bean for the proxy by calling
|
||||
* {@link AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor, String)}.
|
||||
*
|
||||
* @see EarlyBeanReferenceProxy#dereferenceTargetBean()
|
||||
*/
|
||||
private class ResolveDependencyTargetBeanDereferencingInterceptor implements TargetBeanDereferencingInterceptor {
|
||||
|
||||
private final DependencyDescriptor descriptor;
|
||||
|
||||
public ResolveDependencyTargetBeanDereferencingInterceptor(DependencyDescriptor descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
return beanFactory.resolveDependency(descriptor, null);
|
||||
}
|
||||
|
||||
public Class<?> getTargetBeanType() {
|
||||
return this.descriptor.getDependencyType();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interceptor that dereferences the target bean for the proxy by calling BeanFactory#getBean(String).
|
||||
*
|
||||
* @see EarlyBeanReferenceProxy#dereferenceTargetBean()
|
||||
*/
|
||||
private class ByNameLookupTargetBeanDereferencingInterceptor implements TargetBeanDereferencingInterceptor {
|
||||
|
||||
private final Method beanMethod;
|
||||
|
||||
public ByNameLookupTargetBeanDereferencingInterceptor(Method beanMethod) {
|
||||
this.beanMethod = beanMethod;
|
||||
}
|
||||
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
return beanFactory.getBean(BeanAnnotationHelper.determineBeanNameFor(beanMethod));
|
||||
}
|
||||
|
||||
public Class<?> getTargetBeanType() {
|
||||
return beanMethod.getReturnType();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interceptor that dereferences the target bean for the proxy and delegates the
|
||||
* current method call to it.
|
||||
* @see TargetBeanDereferencingInterceptor
|
||||
*/
|
||||
private static class TargetBeanDelegatingMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
Object targetBean = ((EarlyBeanReferenceProxy)obj).dereferenceTargetBean();
|
||||
return method.invoke(targetBean, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Feature {
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Meta-annotation indicating that an annotation should be processed
|
||||
* to produce a {@code FeatureSpecification}.
|
||||
*
|
||||
* <p>See {@link ComponentScan @ComponentScan} for an implementation example.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see ComponentScan
|
||||
* @see org.springframework.context.config.FeatureSpecification
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
public @interface FeatureAnnotation {
|
||||
|
||||
/**
|
||||
* Indicate the class that should be used to parse this annotation
|
||||
* into a {@code FeatureSpecification}.
|
||||
*/
|
||||
Class<? extends FeatureAnnotationParser> parser();
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* Interface for parsing {@link AnnotationMetadata} from a {@link FeatureAnnotation}
|
||||
* into a {@link FeatureSpecification} object. Used in conjunction with a
|
||||
* {@link FeatureSpecificationExecutor} to provide a source-agnostic approach to
|
||||
* handling configuration metadata.
|
||||
*
|
||||
* <p>For example, Spring's component-scanning can be configured via XML using
|
||||
* the {@code context:component-scan} element or via the {@link ComponentScan}
|
||||
* annotation. In either case, the metadata is the same -- only the source
|
||||
* format differs. {@link ComponentScanBeanDefinitionParser} is used to create
|
||||
* a specification from the {@code <context:component-scan>} XML element, while
|
||||
* {@link ComponentScanAnnotationParser} creates a specification from the
|
||||
* the annotation style. They both produce a {@link ComponentScanSpec}
|
||||
* object that is ultimately delegated to a {@link ComponentScanExecutor}
|
||||
* which understands how to configure a {@link ClassPathBeanDefinitionScanner},
|
||||
* perform actual scanning, and register actual bean definitions against the
|
||||
* container.
|
||||
*
|
||||
* <p>Implementations must be instantiable via a no-arg constructor.
|
||||
*
|
||||
* TODO SPR-7420: documentation (clean up)
|
||||
* TODO SPR-7420: rework so annotations declare their creator.
|
||||
*
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see FeatureAnnotation#parser()
|
||||
* @see FeatureSpecification
|
||||
* @see FeatureSpecificationExecutor
|
||||
*/
|
||||
public interface FeatureAnnotationParser {
|
||||
|
||||
/**
|
||||
* Parse the given annotation metadata and populate a {@link FeatureSpecification}
|
||||
* object suitable for execution by a {@link FeatureSpecificationExecutor}.
|
||||
*/
|
||||
FeatureSpecification process(AnnotationMetadata metadata);
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see Configuration
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Component
|
||||
public @interface FeatureConfiguration {
|
||||
|
||||
/**
|
||||
* Explicitly specify the name of the Spring bean definition associated
|
||||
* with this FeatureConfiguration class. If left unspecified (the common case),
|
||||
* a bean name will be automatically generated.
|
||||
*
|
||||
* <p>The custom name applies only if the FeatureConfiguration class is picked up via
|
||||
* component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}.
|
||||
* If the FeatureConfiguration class is registered as a traditional XML bean definition,
|
||||
* the name/id of the bean element will take precedence.
|
||||
*
|
||||
* @return the specified bean name, if any
|
||||
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class FeatureMethodExecutionException extends RuntimeException {
|
||||
public FeatureMethodExecutionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public FeatureMethodExecutionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class ProxyCreationException extends RuntimeException {
|
||||
|
||||
public ProxyCreationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.ComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.ComponentRegistrar;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
|
||||
public class SimpleComponentRegistrar implements ComponentRegistrar {
|
||||
|
||||
private final BeanDefinitionRegistry registry;
|
||||
|
||||
public SimpleComponentRegistrar(BeanDefinitionRegistry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
public String registerWithGeneratedName(BeanDefinition beanDefinition) {
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, this.registry);
|
||||
}
|
||||
|
||||
public void registerBeanComponent(BeanComponentDefinition component) {
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(component, this.registry);
|
||||
registerComponent(component);
|
||||
}
|
||||
|
||||
public void registerComponent(ComponentDefinition component) {
|
||||
// no-op
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.config;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.beans.factory.parsing.SimpleProblemCollector;
|
||||
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractFeatureSpecification implements SourceAwareSpecification {
|
||||
|
||||
private static final Object DUMMY_SOURCE = new Object();
|
||||
private static final String DUMMY_SOURCE_NAME = "dummySource";
|
||||
|
||||
protected Class<? extends FeatureSpecificationExecutor> executorType;
|
||||
|
||||
private Object source = DUMMY_SOURCE;
|
||||
private String sourceName = DUMMY_SOURCE_NAME;
|
||||
|
||||
protected AbstractFeatureSpecification(Class<? extends FeatureSpecificationExecutor> executorType) {
|
||||
this.executorType = executorType;
|
||||
}
|
||||
|
||||
public final boolean validate(ProblemReporter problemReporter) {
|
||||
SimpleProblemCollector collector = new SimpleProblemCollector(this.source());
|
||||
this.doValidate(collector);
|
||||
collector.reportProblems(problemReporter);
|
||||
return collector.hasErrors() ? false : true;
|
||||
}
|
||||
|
||||
protected abstract void doValidate(SimpleProblemCollector reporter);
|
||||
|
||||
public AbstractFeatureSpecification source(Object source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object source() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
public AbstractFeatureSpecification sourceName(String sourceName) {
|
||||
this.sourceName = sourceName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String sourceName() {
|
||||
return this.sourceName;
|
||||
}
|
||||
|
||||
public void execute(ExecutorContext executorContext) {
|
||||
FeatureSpecificationExecutor executor =
|
||||
BeanUtils.instantiateClass(this.executorType, FeatureSpecificationExecutor.class);
|
||||
executor.execute(this, executorContext);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.config;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractSpecificationExecutor<S extends FeatureSpecification> implements FeatureSpecificationExecutor {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation {@linkplain FeatureSpecification#validate() validates} the
|
||||
* given specification and delegates it to {@link #doExecute(FeatureSpecification)}
|
||||
* only if valid.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void execute(FeatureSpecification spec, ExecutorContext executorContext) {
|
||||
Assert.notNull(spec, "Specification must not be null");
|
||||
Assert.notNull(spec, "ExecutorContext must not be null");
|
||||
Class<?> typeArg = GenericTypeResolver.resolveTypeArgument(this.getClass(), AbstractSpecificationExecutor.class);
|
||||
Assert.isTrue(typeArg.equals(spec.getClass()), "Specification cannot be executed by this executor");
|
||||
if (spec.validate(executorContext.getProblemReporter())) {
|
||||
doExecute((S)spec, executorContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given specification, usually resulting in registration of bean definitions
|
||||
* against a bean factory.
|
||||
* @param specification the {@linkplain FeatureSpecification#validate() validated} specification
|
||||
*/
|
||||
protected abstract void doExecute(S specification, ExecutorContext executorContext);
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.config;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public enum AdviceMode {
|
||||
PROXY,
|
||||
ASPECTJ
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.config;
|
||||
|
||||
import org.springframework.beans.factory.parsing.ComponentRegistrar;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* TODO: rename to SpecificationContext?
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ExecutorContext {
|
||||
|
||||
private BeanDefinitionRegistry registry;
|
||||
private ComponentRegistrar registrar;
|
||||
private ResourceLoader resourceLoader;
|
||||
private Environment environment;
|
||||
private ProblemReporter problemReporter;
|
||||
|
||||
public ExecutorContext() { }
|
||||
|
||||
public void setRegistry(BeanDefinitionRegistry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
public BeanDefinitionRegistry getRegistry() {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
public void setRegistrar(ComponentRegistrar registrar) {
|
||||
this.registrar = registrar;
|
||||
}
|
||||
|
||||
public ComponentRegistrar getRegistrar() {
|
||||
return this.registrar;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
public ResourceLoader getResourceLoader() {
|
||||
return this.resourceLoader;
|
||||
}
|
||||
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public Environment getEnvironment() {
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
public void setProblemReporter(ProblemReporter problemReporter) {
|
||||
this.problemReporter = problemReporter;
|
||||
}
|
||||
|
||||
public ProblemReporter getProblemReporter() {
|
||||
return this.problemReporter;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.config;
|
||||
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by objects that specify the configuration of a particular feature
|
||||
* of the Spring container e.g., component-scanning, JMX MBean exporting, AspectJ auto-proxying,
|
||||
* annotation-driven transaction management, and so on.
|
||||
*
|
||||
* <p>Many features of the Spring container can be configured using either XML or annotations.
|
||||
* As one example, Spring's <em>component scanning</em> feature may be configured using
|
||||
* either the {@code <context:component-scan>} XML element or (as of Spring 3.1) the
|
||||
* {@code @ComponentScan} annotation. These two options are equivalent to one another, and users may
|
||||
* choose between them as a matter of convention or preference. Fundamentally, both are declarative
|
||||
* mechanisms for <em>specifying</em> how the Spring container should be configured. A {@code
|
||||
* FeatureSpecification} object, then, is a way of representing this configuration information independent
|
||||
* of its original source format be it XML, annotations, or otherwise.
|
||||
*
|
||||
* <p>A {@code FeatureSpecification} is responsible for {@linkplain #validate validating itself}.
|
||||
* For example, a component-scanning specification would check that at least one base package has
|
||||
* been specified, and otherwise register a {@code Problem} with a {@link ProblemReporter}. Taking
|
||||
* this approach as opposed to throwing exceptions allows for maximum tooling and error reporting
|
||||
* flexibility.
|
||||
*
|
||||
* <p>A {@link FeatureSpecificationExecutor} is used to carry out the instructions within a populated
|
||||
* {@code FeatureSpecification}; this is where the "real work" happens. In the case of component scanning
|
||||
* as above, it is within a {@code FeatureSpecificationExecutor} that a bean definition scanner is created,
|
||||
* configured and invoked against the base packages specified.
|
||||
*
|
||||
* <p>{@code FeatureSpecification} objects may be populated and executed by Spring XML namespace element
|
||||
* parsers on order to separate the concerns of XML parsing from Spring bean definition creation and
|
||||
* registration. This type of use is mostly a framework-internal matter. More interesting to end users is
|
||||
* the use of {@code FeatureSpecification} objects within {@code @FeatureConfiguration} classes and their
|
||||
* {@code @Feature} methods. This functionality is new in Spring 3.1 and is the logical evolution of Spring's
|
||||
* Java-based configuration support first released in Spring 3.0 (see {@code @Configuration} classes and
|
||||
* {@code @Bean} methods). The primary goal of {@code Feature}-related support is to round out this
|
||||
* Java-based support and allow users to configure all aspects of the Spring-container without requiring
|
||||
* the use of XML. See "design notes" below for an example of this kind of use.
|
||||
*
|
||||
* <h2>Notes on designing {@code FeatureSpecification} implementations</h2>
|
||||
*
|
||||
* <p>The public API of a {@code FeatureSpecification} should be designed for maximum ease of use
|
||||
* within {@code @Feature} methods. Traditional JavaBean-style getters and setters should be dropped
|
||||
* for a more fluent style that allows for method chaining. Consider the following example of a
|
||||
* {@code @Feature} method:
|
||||
*
|
||||
* <pre>
|
||||
* @Feature
|
||||
* public TxAnnotationDriven tx(PlatformTransactionManager txManager) {
|
||||
* return new TxAnnotationDriven(txManager).proxyTargetClass(true);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Notice how the creation and configuration of the {@code TxAnnotationDriven} specification is
|
||||
* concise and reads well. This is facilitated by mutator methods that always return the
|
||||
* specification object's 'this' reference, allowing for method chaining. A secondary design goal
|
||||
* of this approach is that the resulting code tends to mirror corresponding XML namespace
|
||||
* declarations, which most Spring users are already familiar with. For example, compare the
|
||||
* code above with its XML counterpart:
|
||||
*
|
||||
* <p>{@code <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>}
|
||||
*
|
||||
* <p>Typically, a user will call only the constructor and 'mutator' methods of a specification
|
||||
* object. The accessor/getter methods, however, are typically called only by the specification's
|
||||
* {@linkplain FeatureSpecificationExecutor executor} for the purpose of populating and registering
|
||||
* bean definitions with the Spring container.For this reason, it is recommended that accessor
|
||||
* methods be given package-private visibility. This creates a better experience for users from
|
||||
* an IDE content-assist point of view as they will see only the public mutator methods, reducing
|
||||
* any possible confusion.
|
||||
*
|
||||
* <p>Implementations should take care to allow for use of string-based bean names, placeholder
|
||||
* (<code>"${...}"</code>) and SpEL (<code>"#{...}</code>) expressions wherever they may be useful.
|
||||
* While it is generally desirable to refer to dependent beans in pure Java, in certain cases a
|
||||
* user may wish or need to refer by bean name. For example, the {@code TxAnnotationDriven} specification
|
||||
* referenced above allows for specifying its transaction-manager reference by {@code String} or by
|
||||
* {@code PlatformTransactionManager} reference. Such strings should always be candidates for placeholder
|
||||
* replacement and SpEL evaluation for maximum configurability as well as parity with the featureset
|
||||
* available in Spring XML. With regard to SpEL expressions, users should assume that only expressions
|
||||
* evaluating to a bean name will be supported. While it is technically possible with SpEL to resolve
|
||||
* a bean instance, specification executors will not support such use unless explicitly indicated.
|
||||
*
|
||||
* <p>See the Javadoc for {@code @FeatureConfiguration} classes and {@code @Feature} methods for
|
||||
* information on their lifecycle and semantics.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see FeatureSpecificationExecutor
|
||||
* @see AbstractSpecificationExecutor
|
||||
* @see org.springframework.context.annotation.Feature
|
||||
* @see org.springframework.context.annotation.FeatureConfiguration
|
||||
*/
|
||||
public interface FeatureSpecification {
|
||||
|
||||
/**
|
||||
* Validate this specification instance to ensure all required properties
|
||||
* have been set, including checks on mutually exclusive or mutually
|
||||
* dependent properties. May in some cases modify the state of the
|
||||
* specification e.g., instantiating types specified as strings.
|
||||
* @see AbstractSpecificationExecutor#execute(Specification)
|
||||
* @return whether any problems occurred during validation
|
||||
*/
|
||||
boolean validate(ProblemReporter problemReporter);
|
||||
|
||||
/**
|
||||
* Execute this specification instance, carrying out the instructions
|
||||
* specified within. Should work by delegating to an underlying
|
||||
* {@link FeatureSpecificationExecutor} for proper separation of concerns.
|
||||
*/
|
||||
void execute(ExecutorContext executorContext);
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.config;
|
||||
|
||||
/**
|
||||
* Interface for executing a populated {@link FeatureSpecification}. Provides
|
||||
* a generic mechanism for handling container configuration metadata regardless of
|
||||
* origin in XML, annotations, or other source format.
|
||||
*
|
||||
* TODO SPR-7420: document (clean up)
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see AbstractSpecificationExecutor
|
||||
* @see org.springframework.beans.factory.xml.BeanDefinitionParser
|
||||
* @see org.springframework.context.annotation.FeatureAnnotationParser
|
||||
* @see org.springframework.context.annotation.ComponentScanExecutor
|
||||
*/
|
||||
public interface FeatureSpecificationExecutor {
|
||||
|
||||
/**
|
||||
* Execute the given specification, usually resulting in registration
|
||||
* of bean definitions against a bean factory.
|
||||
*/
|
||||
void execute(FeatureSpecification spec, ExecutorContext executorContext);
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.config;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface SourceAwareSpecification extends FeatureSpecification {
|
||||
|
||||
String sourceName();
|
||||
|
||||
SourceAwareSpecification sourceName(String sourceName);
|
||||
|
||||
Object source();
|
||||
|
||||
SourceAwareSpecification source(Object source);
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -22,16 +22,30 @@ import org.springframework.beans.factory.FactoryBean;
|
|||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
|
||||
/**
|
||||
* A factory for a ConversionService that installs default converters appropriate for most environments.
|
||||
* Set the {@link #setConverters "converters"} property to supplement or override the default converters.
|
||||
* A factory providing convenient access to a ConversionService configured with
|
||||
* converters appropriate for most environments. Set the {@link #setConverters
|
||||
* "converters"} property to supplement the default converters.
|
||||
*
|
||||
* <p>This implementation creates a {@link DefaultConversionService}. Subclasses
|
||||
* may override {@link #createConversionService()} in order to return a
|
||||
* {@link GenericConversionService} instance of their choosing.
|
||||
*
|
||||
* <p>Like all {@code FactoryBean} implementations, this class is suitable for
|
||||
* use when configuring a Spring application context using Spring {@code <beans>}
|
||||
* XML. When configuring the container with
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration}
|
||||
* classes, simply instantiate, configure and return the appropriate
|
||||
* {@code ConversionService} object from a {@link
|
||||
* org.springframework.context.annotation.Bean @Bean} method.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see ConversionServiceFactory#createDefaultConversionService()
|
||||
*/
|
||||
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
|
||||
|
||||
|
@ -52,17 +66,17 @@ public class ConversionServiceFactoryBean implements FactoryBean<ConversionServi
|
|||
|
||||
public void afterPropertiesSet() {
|
||||
this.conversionService = createConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(this.conversionService);
|
||||
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the ConversionService instance returned by this factory bean.
|
||||
* <p>Creates a simple {@link GenericConversionService} instance by default.
|
||||
* Subclasses may override to customize the ConversionService instance that gets created.
|
||||
* Subclasses may override to customize the ConversionService instance that
|
||||
* gets created.
|
||||
*/
|
||||
protected GenericConversionService createConversionService() {
|
||||
return new GenericConversionService();
|
||||
return new DefaultConversionService();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.format.support;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.AnnotationFormatterFactory;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.format.Parser;
|
||||
import org.springframework.format.Printer;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar;
|
||||
import org.springframework.format.number.NumberFormatAnnotationFormatterFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* A specialization of {@link FormattingConversionService} configured by default with
|
||||
* converters and formatters appropriate for most applications.
|
||||
*
|
||||
* <p>Designed for direct instantiation but also exposes the static {@link #addDefaultFormatters}
|
||||
* utility method for ad hoc use against any {@code FormatterRegistry} instance, just
|
||||
* as {@code DefaultConversionService} exposes its own
|
||||
* {@link DefaultConversionService#addDefaultConverters addDefaultConverters} method.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class DefaultFormattingConversionService extends FormattingConversionService {
|
||||
|
||||
private static final boolean jodaTimePresent = ClassUtils.isPresent(
|
||||
"org.joda.time.LocalDate", DefaultFormattingConversionService.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultFormattingConversionService} with the set of
|
||||
* {@linkplain DefaultConversionService#addDefaultConverters default converters} and
|
||||
* {@linkplain #addDefaultFormatters default formatters}.
|
||||
*/
|
||||
public DefaultFormattingConversionService() {
|
||||
this(null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultFormattingConversionService} with the set of
|
||||
* {@linkplain DefaultConversionService#addDefaultConverters default converters} and,
|
||||
* based on the value of {@code registerDefaultFormatters}, the set of
|
||||
* {@linkplain #addDefaultFormatters default formatters}.
|
||||
* @param registerDefaultFormatters whether to register default formatters
|
||||
*/
|
||||
public DefaultFormattingConversionService(boolean registerDefaultFormatters) {
|
||||
this(null, registerDefaultFormatters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultFormattingConversionService} with the set of
|
||||
* {@linkplain DefaultConversionService#addDefaultConverters default converters} and,
|
||||
* based on the value of {@code registerDefaultFormatters}, the set of
|
||||
* {@linkplain #addDefaultFormatters default formatters}
|
||||
* @param embeddedValueResolver delegated to {@link #setEmbeddedValueResolver(StringValueResolver)}
|
||||
* prior to calling {@link #addDefaultFormatters}.
|
||||
* @param registerDefaultFormatters whether to register default formatters
|
||||
*/
|
||||
public DefaultFormattingConversionService(StringValueResolver embeddedValueResolver, boolean registerDefaultFormatters) {
|
||||
this.setEmbeddedValueResolver(embeddedValueResolver);
|
||||
DefaultConversionService.addDefaultConverters(this);
|
||||
if (registerDefaultFormatters) {
|
||||
addDefaultFormatters(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add formatters appropriate for most environments, including number formatters and a Joda-Time
|
||||
* date formatter if Joda-Time is present on the classpath.
|
||||
* @param formatterRegistry the service to register default formatters against
|
||||
*/
|
||||
public static void addDefaultFormatters(FormatterRegistry formatterRegistry) {
|
||||
formatterRegistry.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
|
||||
if (jodaTimePresent) {
|
||||
new JodaTimeFormatterRegistrar().registerFormatters(formatterRegistry);
|
||||
} else {
|
||||
formatterRegistry.addFormatterForFieldAnnotation(new NoJodaDateTimeFormatAnnotationFormatterFactory());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dummy AnnotationFormatterFactory that simply fails if @DateTimeFormat is being used
|
||||
* without the JodaTime library being present.
|
||||
*/
|
||||
private static final class NoJodaDateTimeFormatAnnotationFormatterFactory
|
||||
implements AnnotationFormatterFactory<DateTimeFormat> {
|
||||
|
||||
private final Set<Class<?>> fieldTypes;
|
||||
|
||||
public NoJodaDateTimeFormatAnnotationFormatterFactory() {
|
||||
Set<Class<?>> rawFieldTypes = new HashSet<Class<?>>(4);
|
||||
rawFieldTypes.add(Date.class);
|
||||
rawFieldTypes.add(Calendar.class);
|
||||
rawFieldTypes.add(Long.class);
|
||||
this.fieldTypes = Collections.unmodifiableSet(rawFieldTypes);
|
||||
}
|
||||
|
||||
public Set<Class<?>> getFieldTypes() {
|
||||
return this.fieldTypes;
|
||||
}
|
||||
|
||||
public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) {
|
||||
throw new IllegalStateException("JodaTime library not available - @DateTimeFormat not supported");
|
||||
}
|
||||
|
||||
public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
|
||||
throw new IllegalStateException("JodaTime library not available - @DateTimeFormat not supported");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -16,10 +16,6 @@
|
|||
|
||||
package org.springframework.format.support;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
@ -32,48 +28,51 @@ import org.springframework.format.FormatterRegistrar;
|
|||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.format.Parser;
|
||||
import org.springframework.format.Printer;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar;
|
||||
import org.springframework.format.number.NumberFormatAnnotationFormatterFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* <p>A factory for a {@link FormattingConversionService} that installs default
|
||||
* converters and formatters for common types such as numbers and datetimes.
|
||||
*
|
||||
* <p>Converters and formatters can be registered declaratively through
|
||||
* A factory providing convenient access to a {@code FormattingConversionService}
|
||||
* configured with converters and formatters for common types such as numbers and
|
||||
* datetimes.
|
||||
*
|
||||
* <p>Additional converters and formatters can be registered declaratively through
|
||||
* {@link #setConverters(Set)} and {@link #setFormatters(Set)}. Another option
|
||||
* is to register converters and formatters in code by implementing the
|
||||
* {@link FormatterRegistrar} interface. You can then configure provide the set
|
||||
* is to register converters and formatters in code by implementing the
|
||||
* {@link FormatterRegistrar} interface. You can then configure provide the set
|
||||
* of registrars to use through {@link #setFormatterRegistrars(Set)}.
|
||||
*
|
||||
* <p>A good example for registering converters and formatters in code is
|
||||
* <code>JodaTimeFormatterRegistrar</code>, which registers a number of
|
||||
*
|
||||
* <p>A good example for registering converters and formatters in code is
|
||||
* <code>JodaTimeFormatterRegistrar</code>, which registers a number of
|
||||
* date-related formatters and converters. For a more detailed list of cases
|
||||
* see {@link #setFormatterRegistrars(Set)}
|
||||
*
|
||||
* see {@link #setFormatterRegistrars(Set)}
|
||||
*
|
||||
* <p>Like all {@code FactoryBean} implementations, this class is suitable for
|
||||
* use when configuring a Spring application context using Spring {@code <beans>}
|
||||
* XML. When configuring the container with
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration}
|
||||
* classes, simply instantiate, configure and return the appropriate
|
||||
* {@code FormattingConversionService} object from a
|
||||
* {@link org.springframework.context.annotation.Bean @Bean} method.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
*/
|
||||
public class FormattingConversionServiceFactoryBean
|
||||
implements FactoryBean<FormattingConversionService>, EmbeddedValueResolverAware, InitializingBean {
|
||||
|
||||
private static final boolean jodaTimePresent = ClassUtils.isPresent(
|
||||
"org.joda.time.LocalDate", FormattingConversionService.class.getClassLoader());
|
||||
|
||||
private Set<?> converters;
|
||||
|
||||
private Set<?> formatters;
|
||||
|
||||
private Set<FormatterRegistrar> formatterRegistrars;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
private FormattingConversionService conversionService;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
private boolean registerDefaultFormatters = true;
|
||||
|
||||
/**
|
||||
|
@ -129,12 +128,12 @@ public class FormattingConversionServiceFactoryBean
|
|||
this.registerDefaultFormatters = registerDefaultFormatters;
|
||||
}
|
||||
|
||||
|
||||
// implementing InitializingBean
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
this.conversionService = new FormattingConversionService();
|
||||
this.conversionService.setEmbeddedValueResolver(this.embeddedValueResolver);
|
||||
ConversionServiceFactory.addDefaultConverters(this.conversionService);
|
||||
this.conversionService = new DefaultFormattingConversionService(this.embeddedValueResolver, this.registerDefaultFormatters);
|
||||
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
|
||||
addDefaultFormatters();
|
||||
registerFormatters();
|
||||
}
|
||||
|
||||
|
@ -162,24 +161,14 @@ public class FormattingConversionServiceFactoryBean
|
|||
* through FormatterRegistrars.
|
||||
* @see #setFormatters(Set)
|
||||
* @see #setFormatterRegistrars(Set)
|
||||
* @deprecated since Spring 3.1 in favor of {@link #setFormatterRegistrars(Set)}
|
||||
*/
|
||||
@Deprecated
|
||||
protected void installFormatters(FormatterRegistry registry) {
|
||||
}
|
||||
|
||||
// private helper methods
|
||||
|
||||
private void addDefaultFormatters() {
|
||||
if (registerDefaultFormatters) {
|
||||
this.conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
|
||||
if (jodaTimePresent) {
|
||||
new JodaTimeFormatterRegistrar().registerFormatters(this.conversionService);
|
||||
} else {
|
||||
this.conversionService
|
||||
.addFormatterForFieldAnnotation(new NoJodaDateTimeFormatAnnotationFormatterFactory());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerFormatters() {
|
||||
if (this.formatters != null) {
|
||||
for (Object formatter : this.formatters) {
|
||||
|
@ -201,34 +190,4 @@ public class FormattingConversionServiceFactoryBean
|
|||
installFormatters(this.conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy AnnotationFormatterFactory that simply fails if @DateTimeFormat is being used
|
||||
* without the JodaTime library being present.
|
||||
*/
|
||||
private static final class NoJodaDateTimeFormatAnnotationFormatterFactory
|
||||
implements AnnotationFormatterFactory<DateTimeFormat> {
|
||||
|
||||
private final Set<Class<?>> fieldTypes;
|
||||
|
||||
public NoJodaDateTimeFormatAnnotationFormatterFactory() {
|
||||
Set<Class<?>> rawFieldTypes = new HashSet<Class<?>>(4);
|
||||
rawFieldTypes.add(Date.class);
|
||||
rawFieldTypes.add(Calendar.class);
|
||||
rawFieldTypes.add(Long.class);
|
||||
this.fieldTypes = Collections.unmodifiableSet(rawFieldTypes);
|
||||
}
|
||||
|
||||
public Set<Class<?>> getFieldTypes() {
|
||||
return this.fieldTypes;
|
||||
}
|
||||
|
||||
public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) {
|
||||
throw new IllegalStateException("JodaTime library not available - @DateTimeFormat not supported");
|
||||
}
|
||||
|
||||
public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
|
||||
throw new IllegalStateException("JodaTime library not available - @DateTimeFormat not supported");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
|
|
@ -191,6 +191,7 @@
|
|||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Controls the class files eligible for component detection. Defaults to "**/*.class", the recommended value.
|
||||
Consider use of the include-filter and exclude-filter elements for a more fine-grained approach.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
|
|
@ -65,6 +65,7 @@ public class FooServiceImpl implements FooService {
|
|||
|
||||
private boolean initCalled = false;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
if (this.initCalled) {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2002-2010 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 example.scannable;
|
||||
|
||||
|
||||
/**
|
||||
* Marker class for example.scannable package.
|
||||
*
|
||||
* @see org.springframework.context.annotation.ComponentScan#basePackageClasses()
|
||||
*/
|
||||
public class _package { }
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 example.scannable_scoped;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@MyScope(BeanDefinition.SCOPE_PROTOTYPE)
|
||||
public class CustomScopeAnnotationBean {
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 example.scannable_scoped;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
|
||||
public @interface MyScope {
|
||||
String value() default BeanDefinition.SCOPE_SINGLETON;
|
||||
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
/**
|
||||
* Tests that @FeatureConfiguration classes may implement Aware interfaces,
|
||||
* such as BeanFactoryAware. This is not generally recommended but occasionally
|
||||
* useful, particularly in testing.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class BeanFactoryAwareFeatureConfigurationTests {
|
||||
@Test
|
||||
public void test() {
|
||||
ConfigurableApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(FeatureConfig.class);
|
||||
FeatureConfig fc = ctx.getBean(FeatureConfig.class);
|
||||
assertThat(fc.featureMethodWasCalled, is(true));
|
||||
assertThat(fc.gotBeanFactoryInTime, is(true));
|
||||
assertThat(fc.beanFactory, is(ctx.getBeanFactory()));
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
static class FeatureConfig implements BeanFactoryAware {
|
||||
|
||||
ConfigurableListableBeanFactory beanFactory;
|
||||
boolean featureMethodWasCalled = false;
|
||||
boolean gotBeanFactoryInTime = false;
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = (ConfigurableListableBeanFactory)beanFactory;
|
||||
}
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f() {
|
||||
this.featureMethodWasCalled = true;
|
||||
this.gotBeanFactoryInTime = (this.beanFactory != null);
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.sameInstance;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.annotation.CustomAutowireConfigurer;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.SimpleMapScope;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.ComponentScanParserTests.CustomAnnotationAutowiredBean;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.util.SerializationTestUtils;
|
||||
|
||||
import example.scannable.FooService;
|
||||
import example.scannable.ScopedProxyTestBean;
|
||||
import example.scannable_scoped.CustomScopeAnnotationBean;
|
||||
import example.scannable_scoped.MyScope;
|
||||
|
||||
/**
|
||||
* Integration tests for processing ComponentScan-annotated Configuration
|
||||
* classes.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ComponentScanAnnotationIntegrationTests {
|
||||
@Test
|
||||
public void controlScan() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.scan(example.scannable._package.class.getPackage().getName());
|
||||
ctx.refresh();
|
||||
assertThat("control scan for example.scannable package failed to register FooServiceImpl bean",
|
||||
ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void viaContextRegistration() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanAnnotatedConfig.class);
|
||||
ctx.refresh();
|
||||
ctx.getBean(ComponentScanAnnotatedConfig.class);
|
||||
ctx.getBean(TestBean.class);
|
||||
assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig"), is(true));
|
||||
assertThat("@ComponentScan annotated @Configuration class registered directly against " +
|
||||
"AnnotationConfigApplicationContext did not trigger component scanning as expected",
|
||||
ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void viaContextRegistration_WithValueAttribute() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanAnnotatedConfig_WithValueAttribute.class);
|
||||
ctx.refresh();
|
||||
ctx.getBean(ComponentScanAnnotatedConfig_WithValueAttribute.class);
|
||||
ctx.getBean(TestBean.class);
|
||||
assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig_WithValueAttribute"), is(true));
|
||||
assertThat("@ComponentScan annotated @Configuration class registered directly against " +
|
||||
"AnnotationConfigApplicationContext did not trigger component scanning as expected",
|
||||
ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void viaBeanRegistration() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerBeanDefinition("componentScanAnnotatedConfig",
|
||||
genericBeanDefinition(ComponentScanAnnotatedConfig.class).getBeanDefinition());
|
||||
bf.registerBeanDefinition("configurationClassPostProcessor",
|
||||
genericBeanDefinition(ConfigurationClassPostProcessor.class).getBeanDefinition());
|
||||
GenericApplicationContext ctx = new GenericApplicationContext(bf);
|
||||
ctx.refresh();
|
||||
ctx.getBean(ComponentScanAnnotatedConfig.class);
|
||||
ctx.getBean(TestBean.class);
|
||||
assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig"), is(true));
|
||||
assertThat("@ComponentScan annotated @Configuration class registered " +
|
||||
"as bean definition did not trigger component scanning as expected",
|
||||
ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidComponentScanDeclaration_noPackagesSpecified() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanWithNoPackagesConfig.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
fail("Expected exception when parsing @ComponentScan definition that declares no packages");
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertThat(ex.getMessage(), containsString("At least one base package must be specified"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withCustomBeanNameGenerator() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanWithBeanNameGenenerator.class);
|
||||
ctx.refresh();
|
||||
assertThat(ctx.containsBean("custom_fooServiceImpl"), is(true));
|
||||
assertThat(ctx.containsBean("fooServiceImpl"), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withScopeResolver() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithScopeResolver.class);
|
||||
// custom scope annotation makes the bean prototype scoped. subsequent calls
|
||||
// to getBean should return distinct instances.
|
||||
assertThat(ctx.getBean(CustomScopeAnnotationBean.class), not(sameInstance(ctx.getBean(CustomScopeAnnotationBean.class))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withCustomTypeFilter() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithCustomTypeFilter.class);
|
||||
CustomAnnotationAutowiredBean testBean = ctx.getBean(CustomAnnotationAutowiredBean.class);
|
||||
assertThat(testBean.getDependency(), notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withScopedProxy() throws IOException, ClassNotFoundException {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanWithScopedProxy.class);
|
||||
ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope());
|
||||
ctx.refresh();
|
||||
// should cast to the interface
|
||||
FooService bean = (FooService) ctx.getBean("scopedProxyTestBean");
|
||||
// should be dynamic proxy
|
||||
assertThat(AopUtils.isJdkDynamicProxy(bean), is(true));
|
||||
// test serializability
|
||||
assertThat(bean.foo(1), equalTo("bar"));
|
||||
FooService deserialized = (FooService) SerializationTestUtils.serializeAndDeserialize(bean);
|
||||
assertThat(deserialized, notNullValue());
|
||||
assertThat(deserialized.foo(1), equalTo("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withBasePackagesAndValueAlias() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanWithBasePackagesAndValueAlias.class);
|
||||
ctx.refresh();
|
||||
assertThat(ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses=example.scannable._package.class)
|
||||
class ComponentScanAnnotatedConfig {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan("example.scannable")
|
||||
class ComponentScanAnnotatedConfig_WithValueAttribute {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
class ComponentScanWithNoPackagesConfig { }
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages="example.scannable", nameGenerator=MyBeanNameGenerator.class)
|
||||
class ComponentScanWithBeanNameGenenerator { }
|
||||
|
||||
class MyBeanNameGenerator extends AnnotationBeanNameGenerator {
|
||||
@Override
|
||||
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
|
||||
return "custom_" + super.generateBeanName(definition, registry);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages="example.scannable_scoped", scopeResolver=MyScopeMetadataResolver.class)
|
||||
class ComponentScanWithScopeResolver { }
|
||||
|
||||
class MyScopeMetadataResolver extends AnnotationScopeMetadataResolver {
|
||||
MyScopeMetadataResolver() {
|
||||
this.scopeAnnotationType = MyScope.class;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages="org.springframework.context.annotation",
|
||||
useDefaultFilters=false,
|
||||
includeFilters=@Filter(type=FilterType.CUSTOM, value=ComponentScanParserTests.CustomTypeFilter.class),
|
||||
// exclude this class from scanning since it's in the scanned package
|
||||
excludeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=ComponentScanWithCustomTypeFilter.class))
|
||||
class ComponentScanWithCustomTypeFilter {
|
||||
@Bean
|
||||
@SuppressWarnings({ "rawtypes", "serial", "unchecked" })
|
||||
public CustomAutowireConfigurer customAutowireConfigurer() {
|
||||
CustomAutowireConfigurer cac = new CustomAutowireConfigurer();
|
||||
cac.setCustomQualifierTypes(new HashSet() {{ add(ComponentScanParserTests.CustomAnnotation.class); }});
|
||||
return cac;
|
||||
}
|
||||
|
||||
public ComponentScanParserTests.CustomAnnotationAutowiredBean testBean() {
|
||||
return new ComponentScanParserTests.CustomAnnotationAutowiredBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages="example.scannable",
|
||||
scopedProxy=ScopedProxyMode.INTERFACES,
|
||||
useDefaultFilters=false,
|
||||
includeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=ScopedProxyTestBean.class))
|
||||
class ComponentScanWithScopedProxy { }
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
value="example.scannable",
|
||||
basePackages="example.scannable",
|
||||
basePackageClasses=example.scannable._package.class)
|
||||
class ComponentScanWithBasePackagesAndValueAlias { }
|
|
@ -18,8 +18,7 @@ package org.springframework.context.annotation;
|
|||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
|
||||
import org.springframework.context.annotation.ComponentScan.ExcludeFilter;
|
||||
import org.springframework.context.annotation.ComponentScan.IncludeFilter;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
|
||||
/**
|
||||
|
@ -27,11 +26,13 @@ import org.springframework.core.type.filter.TypeFilter;
|
|||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see ComponentScanAnnotationIntegrationTests
|
||||
*/
|
||||
public class ComponentScanAnnotationTests {
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
public void noop() {
|
||||
// no-op; the @ComponentScan-annotated MyConfig class below simply excercises
|
||||
// available attributes of the annotation.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,22 +40,22 @@ public class ComponentScanAnnotationTests {
|
|||
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
packageOf={TestBean.class},
|
||||
basePackageClasses={TestBean.class},
|
||||
nameGenerator = DefaultBeanNameGenerator.class,
|
||||
scopedProxy = ScopedProxyMode.NO,
|
||||
scopeResolver = AnnotationScopeMetadataResolver.class,
|
||||
useDefaultFilters = false,
|
||||
resourcePattern = "**/*custom.class",
|
||||
includeFilters = {
|
||||
@IncludeFilter(type = FilterType.ANNOTATION, value = MyAnnotation.class)
|
||||
@Filter(type = FilterType.ANNOTATION, value = MyAnnotation.class)
|
||||
},
|
||||
excludeFilters = {
|
||||
@ExcludeFilter(type = FilterType.CUSTOM, value = TypeFilter.class)
|
||||
@Filter(type = FilterType.CUSTOM, value = TypeFilter.class)
|
||||
}
|
||||
)
|
||||
class MyConfig {
|
||||
|
||||
}
|
||||
|
||||
@ComponentScan(packageOf=example.scannable.NamedComponent.class)
|
||||
@ComponentScan(basePackageClasses=example.scannable.NamedComponent.class)
|
||||
class SimpleConfig { }
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ComponentScanExecutor}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ComponentScanExecutorTests {
|
||||
|
||||
private ComponentScanExecutor executor;
|
||||
private ExecutorContext executorContext;
|
||||
private DefaultListableBeanFactory bf;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.bf = new DefaultListableBeanFactory();
|
||||
this.executor = new ComponentScanExecutor();
|
||||
this.executorContext = new ExecutorContext();
|
||||
this.executorContext.setRegistry(bf);
|
||||
this.executorContext.setResourceLoader(new DefaultResourceLoader());
|
||||
this.executorContext.setEnvironment(new MockEnvironment());
|
||||
this.executorContext.setRegistrar(new SimpleComponentRegistrar(bf));
|
||||
this.executorContext.setProblemReporter(new FailFastProblemReporter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validSpec() {
|
||||
this.executor.execute(new ComponentScanSpec("example.scannable"), this.executorContext);
|
||||
assertThat(bf.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
@Test(expected=BeanDefinitionParsingException.class)
|
||||
public void invalidSpec() {
|
||||
// ff problem reporter should throw due to no packages specified
|
||||
this.executor.execute(new ComponentScanSpec(), this.executorContext);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ComponentScanFeatureTests {
|
||||
@Test
|
||||
public void viaContextRegistration() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanFeatureConfig.class);
|
||||
ctx.register(ComponentScanFeatureConfig.Features.class);
|
||||
ctx.refresh();
|
||||
ctx.getBean(ComponentScanFeatureConfig.class);
|
||||
ctx.getBean(TestBean.class);
|
||||
assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanFeatureConfig"), is(true));
|
||||
assertThat("@ComponentScan annotated @Configuration class registered directly against " +
|
||||
"AnnotationConfigApplicationContext did not trigger component scanning as expected",
|
||||
ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
//@Import(ComponentScanFeatureConfig.Features.class)
|
||||
class ComponentScanFeatureConfig {
|
||||
@FeatureConfiguration
|
||||
static class Features {
|
||||
@Feature
|
||||
public ComponentScanSpec componentScan() {
|
||||
return new ComponentScanSpec(example.scannable._package.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.util.StringUtils.arrayToCommaDelimitedString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ComponentScanSpec}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ComponentScanSpecTests {
|
||||
|
||||
private CollatingProblemReporter problemReporter;
|
||||
private ClassLoader classLoader;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
problemReporter = new CollatingProblemReporter();
|
||||
classLoader = ClassUtils.getDefaultClassLoader();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includeAnnotationConfig() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
assertThat(spec.includeAnnotationConfig(), nullValue());
|
||||
spec.includeAnnotationConfig(true);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(true));
|
||||
spec.includeAnnotationConfig(false);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(false));
|
||||
spec.includeAnnotationConfig("trUE");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(true));
|
||||
spec.includeAnnotationConfig("falSE");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(false));
|
||||
spec.includeAnnotationConfig("");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(false));
|
||||
spec.includeAnnotationConfig((String)null);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(false));
|
||||
spec.includeAnnotationConfig((Boolean)null);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourcePattern() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
assertThat(spec.resourcePattern(), nullValue());
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.resourcePattern("**/Foo*.class");
|
||||
assertThat(spec.resourcePattern(), is("**/Foo*.class"));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.resourcePattern("");
|
||||
assertThat(spec.resourcePattern(), is("**/Foo*.class"));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.resourcePattern(null);
|
||||
assertThat(spec.resourcePattern(), is("**/Foo*.class"));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void useDefaultFilters() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
assertThat(spec.useDefaultFilters(), nullValue());
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters((Boolean)null);
|
||||
assertThat(spec.useDefaultFilters(), nullValue());
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters(true);
|
||||
assertThat(spec.useDefaultFilters(), is(true));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters(false);
|
||||
assertThat(spec.useDefaultFilters(), is(false));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters("trUE");
|
||||
assertThat(spec.useDefaultFilters(), is(true));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters("falSE");
|
||||
assertThat(spec.useDefaultFilters(), is(false));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters("");
|
||||
assertThat(spec.useDefaultFilters(), is(false));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters((String)null);
|
||||
assertThat(spec.useDefaultFilters(), is(false));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includeFilters() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.includeFilters(
|
||||
new AnnotationTypeFilter(MyAnnotation.class),
|
||||
new AssignableTypeFilter(Object.class));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeFilters().length, is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringIncludeExcludeFilters() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.addIncludeFilter("annotation", MyAnnotation.class.getName(), classLoader);
|
||||
spec.addExcludeFilter("assignable", Object.class.getName(), classLoader);
|
||||
spec.addExcludeFilter("annotation", Override.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeFilters().length, is(1));
|
||||
assertThat(spec.excludeFilters().length, is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bogusStringIncludeFilter() throws IOException {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.addIncludeFilter("bogus-type", "bogus-expr", classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
assertThat(spec.includeFilters().length, is(1));
|
||||
try {
|
||||
spec.includeFilters()[0].match(null, null);
|
||||
fail("expected placholder TypeFilter to throw exception");
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exerciseFilterTypes() throws IOException {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.addIncludeFilter("aspectj", "*..Bogus", classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.addIncludeFilter("regex", ".*Foo", classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.addIncludeFilter("custom", StubTypeFilter.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.addIncludeFilter("custom", "org.NonExistentTypeFilter", classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
spec.addIncludeFilter("custom", NonNoArgResolver.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingBasePackages() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec();
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withBasePackageViaAdderMethod() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec();
|
||||
spec.addBasePackage("org.p1");
|
||||
spec.addBasePackage("org.p2");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertExactContents(spec.basePackages(), "org.p1", "org.p2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withBasePackagesViaStringConstructor() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1", "org.p2");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertExactContents(spec.basePackages(), "org.p1", "org.p2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withBasePackagesViaClassConstructor() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec(java.lang.Object.class, java.io.Closeable.class);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertExactContents(spec.basePackages(), "java.lang", "java.io");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forDelimitedPackages() {
|
||||
ComponentScanSpec spec = ComponentScanSpec.forDelimitedPackages("pkg.one,pkg.two");
|
||||
assertTrue(ObjectUtils.containsElement(spec.basePackages(), "pkg.one"));
|
||||
assertTrue(ObjectUtils.containsElement(spec.basePackages(), "pkg.two"));
|
||||
assertThat(spec.basePackages().length, is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withSomeEmptyBasePackages() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1", "", "org.p3");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertExactContents(spec.basePackages(), "org.p1", "org.p3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withAllEmptyBasePackages() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("", "", "");
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withInstanceBeanNameGenerator() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
assertThat(spec.beanNameGenerator(), nullValue());
|
||||
BeanNameGenerator bng = new DefaultBeanNameGenerator();
|
||||
spec.beanNameGenerator(bng);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.beanNameGenerator(), is(bng));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withStringBeanNameGenerator() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.beanNameGenerator(DefaultBeanNameGenerator.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.beanNameGenerator(), instanceOf(DefaultBeanNameGenerator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withInstanceScopeMetadataResolver() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
assertThat(spec.scopeMetadataResolver(), nullValue());
|
||||
ScopeMetadataResolver smr = new AnnotationScopeMetadataResolver();
|
||||
spec.scopeMetadataResolver(smr);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.scopeMetadataResolver(), is(smr));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withStringScopeMetadataResolver() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopeMetadataResolver(AnnotationScopeMetadataResolver.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.scopeMetadataResolver(), instanceOf(AnnotationScopeMetadataResolver.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNonAssignableStringScopeMetadataResolver() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopeMetadataResolver(Object.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNonExistentStringScopeMetadataResolver() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopeMetadataResolver("org.Bogus", classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNonNoArgStringScopeMetadataResolver() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopeMetadataResolver(NonNoArgResolver.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withStringScopedProxyMode() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopedProxyMode("targetCLASS");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.scopedProxyMode(), is(ScopedProxyMode.TARGET_CLASS));
|
||||
spec.scopedProxyMode("interFACES");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.scopedProxyMode(), is(ScopedProxyMode.INTERFACES));
|
||||
spec.scopedProxyMode("nO");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.scopedProxyMode(), is(ScopedProxyMode.NO));
|
||||
spec.scopedProxyMode("bogus");
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
assertThat(spec.scopedProxyMode(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withScopeMetadataResolverAndScopedProxyMode() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopeMetadataResolver(new AnnotationScopeMetadataResolver());
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.scopedProxyMode(ScopedProxyMode.INTERFACES);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addBasePackage() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec();
|
||||
spec.addBasePackage("foo.bar");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.basePackages().length, is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addBasePackageWithConstructor() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("my.pkg");
|
||||
spec.addBasePackage("foo.bar");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.basePackages().length, is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addExcludeFilterString() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("my.pkg");
|
||||
spec.addExcludeFilter("annotation", MyAnnotation.class.getName(), ClassUtils.getDefaultClassLoader());
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.excludeFilters().length, is(1));
|
||||
assertThat(spec.excludeFilters()[0], instanceOf(AnnotationTypeFilter.class));
|
||||
}
|
||||
|
||||
@Test(expected=BeanDefinitionParsingException.class)
|
||||
public void withFailFastProblemReporter() {
|
||||
new ComponentScanSpec().validate(new FailFastProblemReporter());
|
||||
}
|
||||
|
||||
private <T> void assertExactContents(T[] actual, T... expected) {
|
||||
if (actual.length >= expected.length) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertThat(
|
||||
String.format("element number %d in actual is incorrect. actual: [%s], expected: [%s]",
|
||||
i, arrayToCommaDelimitedString(actual), arrayToCommaDelimitedString(expected)),
|
||||
actual[i], equalTo(expected[i]));
|
||||
}
|
||||
}
|
||||
assertThat(String.format("actual contains incorrect number of arguments. actual: [%s], expected: [%s]",
|
||||
arrayToCommaDelimitedString(actual), arrayToCommaDelimitedString(expected)),
|
||||
actual.length, equalTo(expected.length));
|
||||
}
|
||||
|
||||
|
||||
private static class CollatingProblemReporter implements ProblemReporter {
|
||||
|
||||
private List<Problem> errors = new ArrayList<Problem>();
|
||||
|
||||
private List<Problem> warnings = new ArrayList<Problem>();
|
||||
|
||||
|
||||
public void fatal(Problem problem) {
|
||||
throw new BeanDefinitionParsingException(problem);
|
||||
}
|
||||
|
||||
public void error(Problem problem) {
|
||||
this.errors.add(problem);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Problem[] getErrors() {
|
||||
return this.errors.toArray(new Problem[this.errors.size()]);
|
||||
}
|
||||
|
||||
public void warning(Problem problem) {
|
||||
System.out.println(problem);
|
||||
this.warnings.add(problem);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Problem[] getWarnings() {
|
||||
return this.warnings.toArray(new Problem[this.warnings.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class NonNoArgResolver implements ScopeMetadataResolver {
|
||||
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private static class StubTypeFilter implements TypeFilter {
|
||||
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
|
||||
throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 java.lang.String.format;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.sameInstance;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.context.annotation.EarlyBeanReferenceProxyCreator.FINAL_CLASS_ERROR_MESSAGE;
|
||||
import static org.springframework.context.annotation.EarlyBeanReferenceProxyCreator.MISSING_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE;
|
||||
import static org.springframework.context.annotation.EarlyBeanReferenceProxyCreator.PRIVATE_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.MethodParameter;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link EarlyBeanReferenceProxyCreator}, ensuring that
|
||||
* {@link EarlyBeanReferenceProxy} objects behave properly.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class EarlyBeanReferenceProxyCreatorTests {
|
||||
|
||||
private DefaultListableBeanFactory bf;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
bf = new DefaultListableBeanFactory();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyToStringAvoidsEagerInstantiation() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy.toString(), equalTo("EarlyBeanReferenceProxy for bean of type TestBean"));
|
||||
}
|
||||
|
||||
@Test(expected=NoSuchBeanDefinitionException.class)
|
||||
public void proxyThrowsNoSuchBeanDefinitionExceptionWhenDelegatingMethodCallToNonExistentBean() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
proxy.getName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyHashCodeAvoidsEagerInstantiation() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy.hashCode(), equalTo(System.identityHashCode(proxy)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyEqualsAvoidsEagerInstantiation() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy.equals(new Object()), is(false));
|
||||
assertThat(proxy.equals(proxy), is(true));
|
||||
|
||||
TestBean proxy2 = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy, not(sameInstance(proxy2)));
|
||||
assertThat(proxy.equals(proxy2), is(false));
|
||||
assertThat(proxy2.equals(proxy), is(false));
|
||||
assertThat(proxy2.equals(proxy2), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyFinalizeAvoidsEagerInstantiation() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
|
||||
BeanWithFinalizer proxy = (BeanWithFinalizer) pc.createProxy(descriptorFor(BeanWithFinalizer.class));
|
||||
|
||||
assertThat(BeanWithFinalizer.finalizerWasCalled, is(false));
|
||||
BeanWithFinalizer.class.getDeclaredMethod("finalize").invoke(proxy);
|
||||
assertThat(BeanWithFinalizer.finalizerWasCalled, is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyMethodsDelegateToTargetBeanCausingSingletonRegistrationIfNecessary() throws Exception {
|
||||
bf.registerBeanDefinition("testBean",
|
||||
BeanDefinitionBuilder.rootBeanDefinition(TestBean.class)
|
||||
.addPropertyValue("name", "testBeanName").getBeanDefinition());
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(bf.containsBeanDefinition("testBean"), is(true));
|
||||
assertThat(bf.containsSingleton("testBean"), is(false));
|
||||
assertThat(proxy.getName(), equalTo("testBeanName"));
|
||||
assertThat(bf.containsSingleton("testBean"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanAnnotatedMethodsReturnEarlyProxyAsWell() throws Exception {
|
||||
bf.registerBeanDefinition("componentWithInterfaceBeanMethod", new RootBeanDefinition(ComponentWithInterfaceBeanMethod.class));
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
ComponentWithInterfaceBeanMethod proxy = (ComponentWithInterfaceBeanMethod) pc.createProxy(descriptorFor(ComponentWithInterfaceBeanMethod.class));
|
||||
|
||||
ITestBean bean = proxy.aBeanMethod();
|
||||
assertThat(bean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(bf.containsBeanDefinition("componentWithInterfaceBeanMethod"), is(true));
|
||||
assertThat("calling a @Bean method on an EarlyBeanReferenceProxy object " +
|
||||
"should not cause its instantation/registration",
|
||||
bf.containsSingleton("componentWithInterfaceBeanMethod"), is(false));
|
||||
|
||||
Object obj = proxy.normalInstanceMethod();
|
||||
assertThat(bf.containsSingleton("componentWithInterfaceBeanMethod"), is(true));
|
||||
assertThat(obj, not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxiesReturnedFromBeanAnnotatedMethodsDereferenceAndDelegateToTheirTargetBean() throws Exception {
|
||||
bf.registerBeanDefinition("componentWithConcreteBeanMethod", new RootBeanDefinition(ComponentWithConcreteBeanMethod.class));
|
||||
RootBeanDefinition beanMethodBeanDef = new RootBeanDefinition();
|
||||
beanMethodBeanDef.setFactoryBeanName("componentWithConcreteBeanMethod");
|
||||
beanMethodBeanDef.setFactoryMethodName("aBeanMethod");
|
||||
bf.registerBeanDefinition("aBeanMethod", beanMethodBeanDef);
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
ComponentWithConcreteBeanMethod proxy = (ComponentWithConcreteBeanMethod) pc.createProxy(descriptorFor(ComponentWithConcreteBeanMethod.class));
|
||||
|
||||
TestBean bean = proxy.aBeanMethod();
|
||||
assertThat(bean.getName(), equalTo("concrete"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interfaceBeansAreProxied() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
ITestBean proxy = (ITestBean) pc.createProxy(descriptorFor(ITestBean.class));
|
||||
|
||||
assertThat(proxy, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(AopUtils.isCglibProxyClass(proxy.getClass()), is(true));
|
||||
assertEquals(
|
||||
"interface-based bean proxies should have Object as superclass",
|
||||
proxy.getClass().getSuperclass(), Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void concreteBeansAreProxied() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(AopUtils.isCglibProxyClass(proxy.getClass()), is(true));
|
||||
assertEquals(
|
||||
"concrete bean proxies should have the bean class as superclass",
|
||||
proxy.getClass().getSuperclass(), TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanAnnotatedMethodsWithInterfaceReturnTypeAreProxied() throws Exception {
|
||||
bf.registerBeanDefinition("componentWithInterfaceBeanMethod", new RootBeanDefinition(ComponentWithInterfaceBeanMethod.class));
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
ComponentWithInterfaceBeanMethod proxy = (ComponentWithInterfaceBeanMethod) pc.createProxy(descriptorFor(ComponentWithInterfaceBeanMethod.class));
|
||||
|
||||
ITestBean bean = proxy.aBeanMethod();
|
||||
assertThat(bean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(AopUtils.isCglibProxyClass(bean.getClass()), is(true));
|
||||
assertEquals(
|
||||
"interface-based bean proxies should have Object as superclass",
|
||||
bean.getClass().getSuperclass(), Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanAnnotatedMethodsWithConcreteReturnTypeAreProxied() throws Exception {
|
||||
bf.registerBeanDefinition("componentWithConcreteBeanMethod", new RootBeanDefinition(ComponentWithConcreteBeanMethod.class));
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
ComponentWithConcreteBeanMethod proxy = (ComponentWithConcreteBeanMethod) pc.createProxy(descriptorFor(ComponentWithConcreteBeanMethod.class));
|
||||
|
||||
TestBean bean = proxy.aBeanMethod();
|
||||
assertThat(bean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(AopUtils.isCglibProxyClass(bean.getClass()), is(true));
|
||||
assertEquals(
|
||||
"concrete bean proxies should have the bean class as superclass",
|
||||
bean.getClass().getSuperclass(), TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attemptToProxyClassMissingNoArgConstructorFailsGracefully() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
try {
|
||||
pc.createProxy(descriptorFor(BeanMissingNoArgConstructor.class));
|
||||
fail("expected ProxyCreationException");
|
||||
} catch(ProxyCreationException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo(format(MISSING_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE, BeanMissingNoArgConstructor.class.getName())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attemptToProxyClassWithPrivateNoArgConstructorFailsGracefully() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
try {
|
||||
pc.createProxy(descriptorFor(BeanWithPrivateNoArgConstructor.class));
|
||||
fail("expected ProxyCreationException");
|
||||
} catch(ProxyCreationException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo(format(PRIVATE_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE, BeanWithPrivateNoArgConstructor.class.getName())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attemptToProxyFinalClassFailsGracefully() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
try {
|
||||
pc.createProxy(descriptorFor(FinalBean.class));
|
||||
fail("expected ProxyCreationException");
|
||||
} catch(ProxyCreationException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo(format(FINAL_CLASS_ERROR_MESSAGE, FinalBean.class.getName())));
|
||||
}
|
||||
}
|
||||
|
||||
private DependencyDescriptor descriptorFor(Class<?> paramType) throws Exception {
|
||||
@SuppressWarnings("unused")
|
||||
class C {
|
||||
void m(ITestBean p) { }
|
||||
void m(TestBean p) { }
|
||||
void m(BeanMissingNoArgConstructor p) { }
|
||||
void m(BeanWithPrivateNoArgConstructor p) { }
|
||||
void m(FinalBean p) { }
|
||||
void m(BeanWithFinalizer p) { }
|
||||
void m(ComponentWithConcreteBeanMethod p) { }
|
||||
void m(ComponentWithInterfaceBeanMethod p) { }
|
||||
}
|
||||
|
||||
Method targetMethod = C.class.getDeclaredMethod("m", new Class<?>[] { paramType });
|
||||
MethodParameter mp = new MethodParameter(targetMethod, 0);
|
||||
DependencyDescriptor dd = new DependencyDescriptor(mp, true, false);
|
||||
return dd;
|
||||
}
|
||||
|
||||
|
||||
static class BeanMissingNoArgConstructor {
|
||||
BeanMissingNoArgConstructor(Object o) { }
|
||||
}
|
||||
|
||||
|
||||
static class BeanWithPrivateNoArgConstructor {
|
||||
private BeanWithPrivateNoArgConstructor() { }
|
||||
}
|
||||
|
||||
|
||||
static final class FinalBean {
|
||||
}
|
||||
|
||||
|
||||
static class BeanWithFinalizer {
|
||||
static Boolean finalizerWasCalled = false;
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
finalizerWasCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ComponentWithConcreteBeanMethod {
|
||||
@Bean
|
||||
public TestBean aBeanMethod() {
|
||||
return new TestBean("concrete");
|
||||
}
|
||||
|
||||
public Object normalInstanceMethod() {
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ComponentWithInterfaceBeanMethod {
|
||||
@Bean
|
||||
public ITestBean aBeanMethod() {
|
||||
return new TestBean("interface");
|
||||
}
|
||||
|
||||
public Object normalInstanceMethod() {
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 org.junit.Test;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
public class FeatureConfigurationClassTests {
|
||||
|
||||
@Test(expected=FeatureMethodExecutionException.class)
|
||||
public void featureConfigurationClassesMustNotContainBeanAnnotatedMethods() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(FeatureConfigWithBeanAnnotatedMethod.class);
|
||||
ctx.refresh();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
class FeatureConfigWithBeanAnnotatedMethod {
|
||||
/**
|
||||
* This is illegal use. @FeatureConfiguration classes cannot have @Bean methods.
|
||||
*/
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* This will never get called. An exception will first be raised regarding the illegal @Bean method above.
|
||||
*/
|
||||
@Feature
|
||||
public FeatureSpecification feature() {
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:c="http://www.springframework.org/schema/c"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
|
||||
|
||||
<bean class="test.beans.TestBean" c:name="beanFromXml"/>
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests proving that @FeatureConfiguration classes may be use @ImportResource
|
||||
* and then parameter autowire beans declared in the imported resource(s).
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureConfigurationImportResourceTests {
|
||||
|
||||
@Test
|
||||
public void importResourceFromFeatureConfiguration() {
|
||||
ApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(ImportingFeatureConfig.class);
|
||||
TestBean testBean = ctx.getBean(TestBean.class);
|
||||
assertThat(testBean.getName(), equalTo("beanFromXml"));
|
||||
|
||||
// and just quickly prove that the target of the bean proxied for the Feature method
|
||||
// is indeed the same singleton instance as the one we just pulled from the container
|
||||
ImportingFeatureConfig ifc = ctx.getBean(ImportingFeatureConfig.class);
|
||||
TestBean proxyBean = ifc.testBean;
|
||||
assertThat(proxyBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertNotSame(proxyBean, testBean);
|
||||
assertSame(((EarlyBeanReferenceProxy)proxyBean).dereferenceTargetBean(), testBean);
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
@ImportResource("org/springframework/context/annotation/FeatureConfigurationImportResourceTests-context.xml")
|
||||
static class ImportingFeatureConfig {
|
||||
TestBean testBean;
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f(TestBean testBean) {
|
||||
this.testBean = testBean;
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
/**
|
||||
* Tests proving that @Configuration classes may @Import @FeatureConfiguration
|
||||
* classes, and vice versa.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureConfigurationImportTests {
|
||||
|
||||
@Test
|
||||
public void importFeatureConfigurationFromConfiguration() {
|
||||
ConfigurableApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(ImportingConfig.class);
|
||||
ImportedFeatureConfig ifc = ctx.getBean(ImportedFeatureConfig.class);
|
||||
assertThat(
|
||||
"@FeatureConfiguration class was imported and registered " +
|
||||
"as a bean but its @Feature method was never called",
|
||||
ifc.featureMethodWasCalled, is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importConfigurationFromFeatureConfiguration() {
|
||||
ConfigurableApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(ImportingFeatureConfig.class);
|
||||
ImportingFeatureConfig ifc = ctx.getBean(ImportingFeatureConfig.class);
|
||||
ImportedConfig ic = ctx.getBean(ImportedConfig.class);
|
||||
assertThat(
|
||||
"@FeatureConfiguration class was registered directly against " +
|
||||
"the container but its @Feature method was never called",
|
||||
ifc.featureMethodWasCalled, is(true));
|
||||
assertThat(
|
||||
"@Configuration class was @Imported but its @Bean method" +
|
||||
"was never registered / called",
|
||||
ic.beanMethodWasCalled, is(true));
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Import(ImportedFeatureConfig.class)
|
||||
static class ImportingConfig {
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class ImportedFeatureConfig {
|
||||
boolean featureMethodWasCalled = false;
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f() {
|
||||
this.featureMethodWasCalled = true;
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class ImportedConfig {
|
||||
boolean beanMethodWasCalled = true;
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
this.beanMethodWasCalled = true;
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
@Import(ImportedConfig.class)
|
||||
static class ImportingFeatureConfig {
|
||||
boolean featureMethodWasCalled = false;
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f() {
|
||||
this.featureMethodWasCalled = true;
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests proving that @Feature methods may reference the product of @Bean methods.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureMethodBeanReferenceTests {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(FeatureConfig.class, Config.class);
|
||||
ctx.refresh();
|
||||
|
||||
TestBean registeredBean = ctx.getBean("testBean", TestBean.class);
|
||||
TestBean proxiedBean = ctx.getBean(FeatureConfig.class).testBean;
|
||||
|
||||
assertThat(registeredBean, not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
assertThat(proxiedBean, notNullValue());
|
||||
assertThat(proxiedBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(proxiedBean.getSpouse(), is(registeredBean.getSpouse()));
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class FeatureConfig {
|
||||
TestBean testBean;
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f(TestBean testBean) {
|
||||
this.testBean = testBean;
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
@Bean
|
||||
public ITestBean testBean() {
|
||||
return new TestBean(new TestBean("mySpouse"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests that @Bean methods referenced from within @Feature methods
|
||||
* get proxied early to avoid premature instantiation of actual
|
||||
* bean instances.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureMethodEarlyBeanProxyTests {
|
||||
|
||||
@Test
|
||||
public void earlyProxyCreationAndBeanRegistrationLifecycle() {
|
||||
AnnotationConfigApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(FeatureConfig.class);
|
||||
|
||||
//
|
||||
// see additional assertions in FeatureConfig#feature()
|
||||
//
|
||||
|
||||
// sanity check that all the bean definitions we expecting are present
|
||||
assertThat(ctx.getBeanFactory().containsBeanDefinition("lazyHelperBean"), is(true));
|
||||
assertThat(ctx.getBeanFactory().containsBeanDefinition("eagerHelperBean"), is(true));
|
||||
assertThat(ctx.getBeanFactory().containsBeanDefinition("lazyPassthroughBean"), is(true));
|
||||
assertThat(ctx.getBeanFactory().containsBeanDefinition("eagerPassthroughBean"), is(true));
|
||||
|
||||
|
||||
// the lazy helper bean had methods invoked during feature method execution. it should be registered
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("lazyHelperBean"), is(true));
|
||||
|
||||
// the eager helper bean had methods invoked but should be registered in any case is it is non-lazy
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("eagerHelperBean"), is(true));
|
||||
|
||||
// the lazy passthrough bean was referenced in the feature method, but never invoked. it should not be registered
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("lazyPassthroughBean"), is(false));
|
||||
|
||||
// the eager passthrough bean should be registered in any case as it is non-lazy
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("eagerPassthroughBean"), is(true));
|
||||
|
||||
|
||||
// now actually fetch all the beans. none should be proxies
|
||||
assertThat(ctx.getBean("lazyHelperBean"), not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
assertThat(ctx.getBean("eagerHelperBean"), not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
assertThat(ctx.getBean("lazyPassthroughBean"), not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
assertThat(ctx.getBean("eagerPassthroughBean"), not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void earlyProxyBeansMayBeInterfaceBasedOrConcrete() {
|
||||
new AnnotationConfigApplicationContext(FeatureConfigReferencingNonInterfaceBeans.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
@Import(TestBeanConfig.class)
|
||||
class FeatureConfig implements BeanFactoryAware {
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = (DefaultListableBeanFactory)beanFactory;
|
||||
}
|
||||
|
||||
@Feature
|
||||
public StubSpecification feature(TestBeanConfig beans) {
|
||||
|
||||
assertThat(
|
||||
"The @Configuration class instance itself should be an early-ref proxy",
|
||||
beans, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
|
||||
// invocation of @Bean methods within @Feature methods should return proxies
|
||||
ITestBean lazyHelperBean = beans.lazyHelperBean();
|
||||
ITestBean eagerHelperBean = beans.eagerHelperBean();
|
||||
ITestBean lazyPassthroughBean = beans.lazyPassthroughBean();
|
||||
ITestBean eagerPassthroughBean = beans.eagerPassthroughBean();
|
||||
|
||||
assertThat(lazyHelperBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(eagerHelperBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(lazyPassthroughBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(eagerPassthroughBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
|
||||
// but at this point, the proxy instances should not have
|
||||
// been registered as singletons with the container.
|
||||
assertThat(this.beanFactory.containsSingleton("lazyHelperBean"), is(false));
|
||||
assertThat(this.beanFactory.containsSingleton("eagerHelperBean"), is(false));
|
||||
assertThat(this.beanFactory.containsSingleton("lazyPassthroughBean"), is(false));
|
||||
assertThat(this.beanFactory.containsSingleton("eagerPassthroughBean"), is(false));
|
||||
|
||||
// invoking a method on the proxy should cause it to pass through
|
||||
// to the container, instantiate the actual bean in question and
|
||||
// register that actual underlying instance as a singleton.
|
||||
assertThat(lazyHelperBean.getName(), equalTo("lazyHelper"));
|
||||
assertThat(eagerHelperBean.getName(), equalTo("eagerHelper"));
|
||||
|
||||
assertThat(this.beanFactory.containsSingleton("lazyHelperBean"), is(true));
|
||||
assertThat(this.beanFactory.containsSingleton("eagerHelperBean"), is(true));
|
||||
|
||||
// since no methods were called on the passthrough beans, they should remain
|
||||
// uncreated / unregistered.
|
||||
assertThat(this.beanFactory.containsSingleton("lazyPassthroughBean"), is(false));
|
||||
assertThat(this.beanFactory.containsSingleton("eagerPassthroughBean"), is(false));
|
||||
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
class TestBeanConfig {
|
||||
|
||||
@Lazy @Bean
|
||||
public ITestBean lazyHelperBean() {
|
||||
return new TestBean("lazyHelper");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITestBean eagerHelperBean() {
|
||||
return new TestBean("eagerHelper");
|
||||
}
|
||||
|
||||
@Lazy @Bean
|
||||
public ITestBean lazyPassthroughBean() {
|
||||
return new TestBean("lazyPassthrough");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITestBean eagerPassthroughBean() {
|
||||
return new TestBean("eagerPassthrough");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
@Import(NonInterfaceBeans.class)
|
||||
class FeatureConfigReferencingNonInterfaceBeans {
|
||||
@Feature
|
||||
public FeatureSpecification feature1(NonInterfaceBeans beans) throws Throwable {
|
||||
beans.testBean();
|
||||
return new StubSpecification();
|
||||
}
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification feature2(TestBean testBean) throws Throwable {
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
class NonInterfaceBeans {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean("invalid");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests proving that @Feature methods may reference beans using @Qualifier
|
||||
* as a parameter annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureMethodQualifiedBeanReferenceTests {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(Features.class, TestBeans.class);
|
||||
ctx.refresh();
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
static class Features {
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f(@Qualifier("testBean1") ITestBean testBean) {
|
||||
assertThat(testBean.getName(), equalTo("one"));
|
||||
return new StubSpecification();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestBeans {
|
||||
@Bean
|
||||
public ITestBean testBean1() {
|
||||
return new TestBean("one");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITestBean testBean2() {
|
||||
return new TestBean("two");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple tests to ensure that @Feature methods are invoked and that the
|
||||
* resulting returned {@link FeatureSpecification} object is delegated to
|
||||
* the correct {@link FeatureSpecificationExecutor}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class SimpleFeatureMethodProcessingTests {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(FeatureConfig.class);
|
||||
assertThat(MySpecificationExecutor.executeMethodWasCalled, is(false));
|
||||
ctx.refresh();
|
||||
assertThat(MySpecificationExecutor.executeMethodWasCalled, is(true));
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
static class FeatureConfig {
|
||||
@Feature
|
||||
public FeatureSpecification f() {
|
||||
return new StubSpecification(MySpecificationExecutor.class);
|
||||
}
|
||||
}
|
||||
|
||||
static class MySpecificationExecutor implements FeatureSpecificationExecutor {
|
||||
static boolean executeMethodWasCalled = false;
|
||||
public void execute(FeatureSpecification spec, ExecutorContext executorContext) {
|
||||
Assert.state(executeMethodWasCalled == false);
|
||||
executeMethodWasCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -16,11 +16,13 @@
|
|||
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
|
@ -43,7 +45,9 @@ import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
|||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.configuration;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* A configuration class that registers a placeholder configurer @Bean method
|
||||
* cannot also have @Value fields. Logically, the config class must be instantiated
|
||||
* in order to invoke the placeholder configurer bean method, and it is a
|
||||
* chicken-and-egg problem to process the @Value field.
|
||||
*
|
||||
* Therefore, placeholder configurers should be put in separate configuration classes
|
||||
* as has been done in the test below. Simply said, placeholder configurer @Bean methods
|
||||
* and @Value fields in the same configuration class are mutually exclusive.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class ConfigurationClassWithPlaceholderConfigurerBeanTests {
|
||||
|
||||
/**
|
||||
* Intentionally ignored test proving that a property placeholder bean
|
||||
* cannot be declared in the same configuration class that has a @Value
|
||||
* field in need of placeholder replacement. It's an obvious chicken-and-egg issue.
|
||||
* The solution is to do as {@link #valueFieldsAreProcessedWhenPlaceholderConfigurerIsSegregated()}
|
||||
* does and segragate the two bean definitions across configuration classes.
|
||||
*/
|
||||
@Ignore @Test
|
||||
public void valueFieldsAreNotProcessedWhenPlaceholderConfigurerIsIntegrated() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ConfigWithValueFieldAndPlaceholderConfigurer.class);
|
||||
System.setProperty("test.name", "foo");
|
||||
ctx.refresh();
|
||||
System.clearProperty("test.name");
|
||||
|
||||
TestBean testBean = ctx.getBean(TestBean.class);
|
||||
assertThat(testBean.getName(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valueFieldsAreProcessedWhenPlaceholderConfigurerIsSegregated() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ConfigWithValueField.class);
|
||||
ctx.register(ConfigWithPlaceholderConfigurer.class);
|
||||
System.setProperty("test.name", "foo");
|
||||
ctx.refresh();
|
||||
System.clearProperty("test.name");
|
||||
|
||||
TestBean testBean = ctx.getBean(TestBean.class);
|
||||
assertThat(testBean.getName(), equalTo("foo"));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class ConfigWithValueField {
|
||||
|
||||
@Value("${test.name}")
|
||||
private String name;
|
||||
|
||||
@Bean
|
||||
public ITestBean testBean() {
|
||||
return new TestBean(this.name);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class ConfigWithPlaceholderConfigurer {
|
||||
|
||||
@Bean
|
||||
public PropertySourcesPlaceholderConfigurer ppc() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class ConfigWithValueFieldAndPlaceholderConfigurer {
|
||||
|
||||
@Value("${test.name}")
|
||||
private String name;
|
||||
|
||||
@Bean
|
||||
public ITestBean testBean() {
|
||||
return new TestBean(this.name);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PropertySourcesPlaceholderConfigurer ppc() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean class="org.springframework.context.annotation.configuration.ColourHolder"/>
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.configuration;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
import org.springframework.context.annotation.FeatureMethodEarlyBeanProxyTests;
|
||||
|
||||
/**
|
||||
* Test suite that groups all tests related to @Feature method lifecycle issues.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({
|
||||
FeatureMethodEarlyBeanProxyTests.class,
|
||||
ConfigurationClassWithPlaceholderConfigurerBeanTests.class,
|
||||
})
|
||||
public class FeatureMethodLifecycleIssueTestSuite {
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.configuration;
|
||||
|
||||
import org.springframework.beans.factory.parsing.SimpleProblemCollector;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
|
||||
public class StubSpecification extends AbstractFeatureSpecification {
|
||||
|
||||
public StubSpecification() {
|
||||
this(StubSpecificationExecutor.class);
|
||||
}
|
||||
|
||||
public StubSpecification(Class<? extends FeatureSpecificationExecutor> excecutorType) {
|
||||
super(excecutorType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(SimpleProblemCollector reporter) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class StubSpecificationExecutor implements FeatureSpecificationExecutor {
|
||||
|
||||
public void execute(FeatureSpecification spec, ExecutorContext executorContext) {
|
||||
}
|
||||
|
||||
}
|
|
@ -36,7 +36,7 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
|
@ -54,7 +54,7 @@ public class JodaTimeFormattingTests {
|
|||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
|
||||
JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
|
||||
registrar.registerFormatters(conversionService);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -27,7 +27,7 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.annotation.NumberFormat;
|
||||
import org.springframework.format.annotation.NumberFormat.Style;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
|
@ -46,7 +46,7 @@ public class NumberFormattingTests {
|
|||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.setEmbeddedValueResolver(new StringValueResolver() {
|
||||
public String resolveStringValue(String strVal) {
|
||||
if ("${pattern}".equals(strVal)) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -16,6 +16,9 @@
|
|||
|
||||
package org.springframework.format.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
@ -27,10 +30,8 @@ import org.joda.time.DateTime;
|
|||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.junit.After;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
|
@ -40,7 +41,7 @@ import org.springframework.context.i18n.LocaleContextHolder;
|
|||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.datetime.joda.DateTimeParser;
|
||||
import org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory;
|
||||
import org.springframework.format.datetime.joda.ReadablePartialPrinter;
|
||||
|
@ -57,7 +58,7 @@ public class FormattingConversionServiceTests {
|
|||
@Before
|
||||
public void setUp() {
|
||||
formattingService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(formattingService);
|
||||
DefaultConversionService.addDefaultConverters(formattingService);
|
||||
LocaleContextHolder.setLocale(Locale.US);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -30,8 +30,6 @@ import java.util.TreeSet;
|
|||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeanWithObjectProperty;
|
||||
import org.springframework.beans.DerivedTestBean;
|
||||
import org.springframework.beans.ITestBean;
|
||||
|
@ -46,7 +44,7 @@ import org.springframework.beans.propertyeditors.CustomNumberEditor;
|
|||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.support.ResourceBundleMessageSource;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.number.NumberFormatter;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -319,7 +317,7 @@ public class DataBinderTests extends TestCase {
|
|||
TestBean tb = new TestBean();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
|
@ -350,7 +348,7 @@ public class DataBinderTests extends TestCase {
|
|||
TestBean tb = new TestBean();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
|
@ -372,7 +370,7 @@ public class DataBinderTests extends TestCase {
|
|||
BeanWithIntegerList tb = new BeanWithIntegerList();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
|
@ -393,7 +391,7 @@ public class DataBinderTests extends TestCase {
|
|||
BeanWithIntegerList tb = new BeanWithIntegerList();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
|
@ -415,7 +413,7 @@ public class DataBinderTests extends TestCase {
|
|||
TestBean tb = new TestBean();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
binder.initDirectFieldAccess();
|
||||
|
@ -448,7 +446,7 @@ public class DataBinderTests extends TestCase {
|
|||
DataBinder binder = new DataBinder(tb);
|
||||
binder.initDirectFieldAccess();
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
|
@ -553,7 +551,7 @@ public class DataBinderTests extends TestCase {
|
|||
assertEquals(1, disallowedFields.length);
|
||||
assertEquals("age", disallowedFields[0]);
|
||||
|
||||
Map m = binder.getBindingResult().getModel();
|
||||
Map<?,?> m = binder.getBindingResult().getModel();
|
||||
assertTrue("There is one element in map", m.size() == 2);
|
||||
TestBean tb = (TestBean) m.get("person");
|
||||
assertTrue("Same object", tb.equals(rod));
|
||||
|
@ -1415,6 +1413,7 @@ public class DataBinderTests extends TestCase {
|
|||
assertEquals("badName", nameError.getCode());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testBindingWithResortedList() {
|
||||
IndexedTestBean tb = new IndexedTestBean();
|
||||
DataBinder binder = new DataBinder(tb, "tb");
|
||||
|
|
|
@ -29,66 +29,16 @@ import org.springframework.core.convert.converter.GenericConverter;
|
|||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class ConversionServiceFactory {
|
||||
|
||||
/**
|
||||
* Create a new default ConversionService instance that can be safely modified.
|
||||
*/
|
||||
public static GenericConversionService createDefaultConversionService() {
|
||||
GenericConversionService conversionService = new GenericConversionService();
|
||||
addDefaultConverters(conversionService);
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the given ConversionService instance with all applicable default converters.
|
||||
*/
|
||||
public static void addDefaultConverters(GenericConversionService conversionService) {
|
||||
conversionService.addConverter(new ArrayToCollectionConverter(conversionService));
|
||||
conversionService.addConverter(new CollectionToArrayConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new ArrayToStringConverter(conversionService));
|
||||
conversionService.addConverter(new StringToArrayConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new ArrayToObjectConverter(conversionService));
|
||||
conversionService.addConverter(new ObjectToArrayConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new CollectionToStringConverter(conversionService));
|
||||
conversionService.addConverter(new StringToCollectionConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new CollectionToObjectConverter(conversionService));
|
||||
conversionService.addConverter(new ObjectToCollectionConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new ArrayToArrayConverter(conversionService));
|
||||
conversionService.addConverter(new CollectionToCollectionConverter(conversionService));
|
||||
conversionService.addConverter(new MapToMapConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new PropertiesToStringConverter());
|
||||
conversionService.addConverter(new StringToPropertiesConverter());
|
||||
|
||||
conversionService.addConverter(new StringToBooleanConverter());
|
||||
conversionService.addConverter(new StringToCharacterConverter());
|
||||
conversionService.addConverter(new StringToLocaleConverter());
|
||||
conversionService.addConverterFactory(new StringToNumberConverterFactory());
|
||||
conversionService.addConverterFactory(new StringToEnumConverterFactory());
|
||||
|
||||
conversionService.addConverter(new NumberToCharacterConverter());
|
||||
conversionService.addConverterFactory(new CharacterToNumberFactory());
|
||||
|
||||
conversionService.addConverterFactory(new NumberToNumberConverterFactory());
|
||||
|
||||
conversionService.addConverter(new ObjectToStringConverter());
|
||||
conversionService.addConverter(new ObjectToObjectConverter());
|
||||
conversionService.addConverter(new IdToEntityConverter(conversionService));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given converter objects with the given target registry.
|
||||
* Register the given Converter objects with the given target ConverterRegistry.
|
||||
* @param converters the converter objects: implementing {@link Converter},
|
||||
* {@link ConverterFactory}, or {@link GenericConverter}
|
||||
* @param registry the target registry to register with
|
||||
* @param registry the target registry
|
||||
*/
|
||||
public static void registerConverters(Set<?> converters, ConverterRegistry registry) {
|
||||
if (converters != null) {
|
||||
|
@ -110,4 +60,22 @@ public abstract class ConversionServiceFactory {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new default ConversionService instance that can be safely modified.
|
||||
*
|
||||
* @deprecated in Spring 3.1 in favor of {@link DefaultConversionService#DefaultConversionService()}
|
||||
*/
|
||||
public static GenericConversionService createDefaultConversionService() {
|
||||
return new DefaultConversionService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the given ConversionService instance with all applicable default converters.
|
||||
*
|
||||
* @deprecated in Spring 3.1 in favor of {@link DefaultConversionService#addDefaultConverters}
|
||||
*/
|
||||
public static void addDefaultConverters(GenericConversionService conversionService) {
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.core.convert.support;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
|
||||
/**
|
||||
* A specialization of {@link GenericConversionService} configured by default with
|
||||
* converters appropriate for most applications.
|
||||
*
|
||||
* <p>Designed for direct instantiation but also exposes the static
|
||||
* {@link #addDefaultConverters} utility method for ad hoc use against any
|
||||
* {@code GenericConversionService} instance.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class DefaultConversionService extends GenericConversionService {
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultConversionService} with the set of
|
||||
* {@linkplain DefaultConversionService#addDefaultConverters default converters}.
|
||||
*/
|
||||
public DefaultConversionService() {
|
||||
addDefaultConverters(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add converters appropriate for most environments.
|
||||
* @param conversionService the service to register default formatters against
|
||||
*/
|
||||
public static void addDefaultConverters(GenericConversionService conversionService) {
|
||||
conversionService.addConverter(new ArrayToCollectionConverter(conversionService));
|
||||
conversionService.addConverter(new CollectionToArrayConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new ArrayToStringConverter(conversionService));
|
||||
conversionService.addConverter(new StringToArrayConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new ArrayToObjectConverter(conversionService));
|
||||
conversionService.addConverter(new ObjectToArrayConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new CollectionToStringConverter(conversionService));
|
||||
conversionService.addConverter(new StringToCollectionConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new CollectionToObjectConverter(conversionService));
|
||||
conversionService.addConverter(new ObjectToCollectionConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new ArrayToArrayConverter(conversionService));
|
||||
conversionService.addConverter(new CollectionToCollectionConverter(conversionService));
|
||||
conversionService.addConverter(new MapToMapConverter(conversionService));
|
||||
|
||||
conversionService.addConverter(new PropertiesToStringConverter());
|
||||
conversionService.addConverter(new StringToPropertiesConverter());
|
||||
|
||||
conversionService.addConverter(new StringToBooleanConverter());
|
||||
conversionService.addConverter(new StringToCharacterConverter());
|
||||
conversionService.addConverter(new StringToLocaleConverter());
|
||||
conversionService.addConverterFactory(new StringToNumberConverterFactory());
|
||||
conversionService.addConverterFactory(new StringToEnumConverterFactory());
|
||||
|
||||
conversionService.addConverter(new NumberToCharacterConverter());
|
||||
conversionService.addConverterFactory(new CharacterToNumberFactory());
|
||||
|
||||
conversionService.addConverterFactory(new NumberToNumberConverterFactory());
|
||||
|
||||
conversionService.addConverter(new ObjectToStringConverter());
|
||||
conversionService.addConverter(new ObjectToObjectConverter());
|
||||
conversionService.addConverter(new IdToEntityConverter(conversionService));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -24,11 +24,10 @@ import static org.springframework.util.SystemPropertyUtils.VALUE_SEPARATOR;
|
|||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.util.PropertyPlaceholderHelper;
|
||||
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base class for resolving properties against any underlying source.
|
||||
*
|
||||
|
@ -39,7 +38,7 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
|
|||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
protected ConversionService conversionService = new DefaultConversionService();
|
||||
|
||||
private PropertyPlaceholderHelper nonStrictHelper;
|
||||
private PropertyPlaceholderHelper strictHelper;
|
||||
|
|
|
@ -69,7 +69,7 @@ public interface Environment extends PropertyResolver {
|
|||
* Return the set of profiles explicitly made active for this environment. Profiles are used for
|
||||
* creating logical groupings of bean definitions to be registered conditionally, often based on
|
||||
* deployment environment. Profiles can be activated by setting {@linkplain
|
||||
* AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME "spring.profiles.active"} as a system property
|
||||
* AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME "spring.profile.active"} as a system property
|
||||
* or by calling {@link ConfigurableEnvironment#setActiveProfiles(String...)}.
|
||||
*
|
||||
* <p>If no profiles have explicitly been specified as active, then any 'default' profiles will implicitly
|
||||
|
|
|
@ -36,6 +36,11 @@ public interface MethodMetadata {
|
|||
*/
|
||||
String getMethodName();
|
||||
|
||||
/**
|
||||
* Return the fully-qualified name of the return type of the method.
|
||||
*/
|
||||
String getMethodReturnType();
|
||||
|
||||
/**
|
||||
* Return the fully-qualified name of the class that declares this method.
|
||||
*/
|
||||
|
|
|
@ -58,7 +58,11 @@ public class StandardMethodMetadata implements MethodMetadata {
|
|||
public String getMethodName() {
|
||||
return this.introspectedMethod.getName();
|
||||
}
|
||||
|
||||
|
||||
public String getMethodReturnType() {
|
||||
return this.introspectedMethod.getReturnType().getName();
|
||||
}
|
||||
|
||||
public String getDeclaringClassName() {
|
||||
return this.introspectedMethod.getDeclaringClass().getName();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -62,7 +62,7 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor
|
|||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
return new MethodMetadataReadingVisitor(name, access, this.getClassName(), this.classLoader, this.methodMetadataMap);
|
||||
return new MethodMetadataReadingVisitor(name, getReturnTypeFromAsmMethodDescriptor(desc), access, this.getClassName(), this.classLoader, this.methodMetadataMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -125,6 +125,19 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor
|
|||
}
|
||||
value = convArray;
|
||||
}
|
||||
else if (classValuesAsString) {
|
||||
if (value instanceof Class) {
|
||||
value = ((Class) value).getName();
|
||||
}
|
||||
else if (value instanceof Class[]) {
|
||||
Class[] clazzArray = (Class[]) value;
|
||||
String[] newValue = new String[clazzArray.length];
|
||||
for (int i = 0; i < clazzArray.length; i++) {
|
||||
newValue[i] = clazzArray[i].getName();
|
||||
}
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
result.put(entry.getKey(), value);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
@ -148,4 +161,46 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor
|
|||
return annotatedMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a type descriptor to a classname suitable for classloading with
|
||||
* Class.forName().
|
||||
*
|
||||
* @param typeDescriptor see ASM guide section 2.1.3
|
||||
*/
|
||||
private static String convertAsmTypeDescriptorToClassName(String typeDescriptor) {
|
||||
final String internalName; // See ASM guide section 2.1.2
|
||||
|
||||
if ("V".equals(typeDescriptor))
|
||||
return Void.class.getName();
|
||||
if ("I".equals(typeDescriptor))
|
||||
return Integer.class.getName();
|
||||
if ("Z".equals(typeDescriptor))
|
||||
return Boolean.class.getName();
|
||||
|
||||
// strip the leading array/object/primitive identifier
|
||||
if (typeDescriptor.startsWith("[["))
|
||||
internalName = typeDescriptor.substring(3);
|
||||
else if (typeDescriptor.startsWith("["))
|
||||
internalName = typeDescriptor.substring(2);
|
||||
else
|
||||
internalName = typeDescriptor.substring(1);
|
||||
|
||||
// convert slashes to dots
|
||||
String className = internalName.replace('/', '.');
|
||||
|
||||
// and strip trailing semicolon (if present)
|
||||
if (className.endsWith(";"))
|
||||
className = className.substring(0, internalName.length() - 1);
|
||||
|
||||
return className;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param methodDescriptor see ASM guide section 2.1.4
|
||||
*/
|
||||
private static String getReturnTypeFromAsmMethodDescriptor(String methodDescriptor) {
|
||||
String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')') + 1);
|
||||
return convertAsmTypeDescriptorToClassName(returnTypeDescriptor);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ final class MethodMetadataReadingVisitor extends MethodAdapter implements Method
|
|||
|
||||
private final int access;
|
||||
|
||||
private String returnType;
|
||||
|
||||
private String declaringClassName;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
@ -51,10 +53,11 @@ final class MethodMetadataReadingVisitor extends MethodAdapter implements Method
|
|||
|
||||
private final Map<String, Map<String, Object>> attributeMap = new LinkedHashMap<String, Map<String, Object>>(2);
|
||||
|
||||
public MethodMetadataReadingVisitor(String name, int access, String declaringClassName, ClassLoader classLoader,
|
||||
public MethodMetadataReadingVisitor(String name, String returnType, int access, String declaringClassName, ClassLoader classLoader,
|
||||
MultiValueMap<String, MethodMetadata> methodMetadataMap) {
|
||||
super(new EmptyVisitor());
|
||||
this.name = name;
|
||||
this.returnType = returnType;
|
||||
this.access = access;
|
||||
this.declaringClassName = declaringClassName;
|
||||
this.classLoader = classLoader;
|
||||
|
@ -72,6 +75,10 @@ final class MethodMetadataReadingVisitor extends MethodAdapter implements Method
|
|||
return this.name;
|
||||
}
|
||||
|
||||
public String getMethodReturnType() {
|
||||
return this.returnType;
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
return ((this.access & Opcodes.ACC_STATIC) != 0);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -32,6 +32,7 @@ import java.util.Arrays;
|
|||
* @author Keith Donald
|
||||
* @author Rod Johnson
|
||||
* @author Rob Harrop
|
||||
* @author Chris Beams
|
||||
* @since 19.03.2004
|
||||
* @see org.apache.commons.lang.ObjectUtils
|
||||
*/
|
||||
|
@ -121,6 +122,54 @@ public abstract class ObjectUtils {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given array of enum constants contains a constant with the given name,
|
||||
* ignoring case when determining a match.
|
||||
* @param enumValues the enum values to check, typically the product of a call to MyEnum.values()
|
||||
* @param constant the constant name to find (must not be null or empty string)
|
||||
* @return whether the constant has been found in the given array
|
||||
*/
|
||||
public static boolean containsConstant(Enum<?>[] enumValues, String constant) {
|
||||
return containsConstant(enumValues, constant, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given array of enum constants contains a constant with the given name.
|
||||
* @param enumValues the enum values to check, typically the product of a call to MyEnum.values()
|
||||
* @param constant the constant name to find (must not be null or empty string)
|
||||
* @param caseSensitive whether case is significant in determining a match
|
||||
* @return whether the constant has been found in the given array
|
||||
*/
|
||||
public static boolean containsConstant(Enum<?>[] enumValues, String constant, boolean caseSensitive) {
|
||||
for (Enum<?> candidate : enumValues) {
|
||||
if (caseSensitive ?
|
||||
candidate.toString().equals(constant) :
|
||||
candidate.toString().equalsIgnoreCase(constant)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Case insensitive alternative to {@link Enum#valueOf(Class, String)}.
|
||||
* @param <E> the concrete Enum type
|
||||
* @param enumValues the array of all Enum constants in question, usually per Enum.values()
|
||||
* @param constant the constant to get the enum value of
|
||||
* @throws IllegalArgumentException if the given constant is not found in the given array
|
||||
* of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to avoid this exception.
|
||||
*/
|
||||
public static <E extends Enum<?>> E caseInsensitiveValueOf(E[] enumValues, String constant) {
|
||||
for (E candidate : enumValues) {
|
||||
if(candidate.toString().equalsIgnoreCase(constant)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
String.format("constant [%s] does not exist in enum type %s",
|
||||
constant, enumValues.getClass().getComponentType().getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the given object to the given array, returning a new array
|
||||
* consisting of the input array contents plus the given object.
|
||||
|
|
|
@ -372,6 +372,20 @@ public abstract class ReflectionUtils {
|
|||
return (method != null && method.getName().equals("toString") && method.getParameterTypes().length == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given method is originally declared by {@link java.lang.Object}.
|
||||
*/
|
||||
public static boolean isObjectMethod(Method method) {
|
||||
try {
|
||||
Object.class.getDeclaredMethod(method.getName(), method.getParameterTypes());
|
||||
return true;
|
||||
} catch (SecurityException ex) {
|
||||
return false;
|
||||
} catch (NoSuchMethodException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the given field accessible, explicitly setting it accessible if
|
||||
* necessary. The <code>setAccessible(true)</code> method is only called
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -55,7 +55,7 @@ import org.springframework.core.convert.converter.ConverterRegistry;
|
|||
*/
|
||||
public class DefaultConversionTests {
|
||||
|
||||
private ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
private ConversionService conversionService = new DefaultConversionService();
|
||||
|
||||
@Test
|
||||
public void testStringToCharacter() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -78,6 +78,7 @@ public class GenericConversionServiceTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void addConverterNoSourceTargetClassInfoAvailable() {
|
||||
try {
|
||||
conversionService.addConverter(new Converter() {
|
||||
|
@ -109,7 +110,7 @@ public class GenericConversionServiceTests {
|
|||
}
|
||||
|
||||
public void convertNullTargetClass() {
|
||||
assertNull(conversionService.convert("3", (Class) null));
|
||||
assertNull(conversionService.convert("3", (Class<?>) null));
|
||||
assertNull(conversionService.convert("3", TypeDescriptor.NULL));
|
||||
}
|
||||
|
||||
|
@ -226,7 +227,7 @@ public class GenericConversionServiceTests {
|
|||
|
||||
@Test
|
||||
public void testStringArrayToResourceArray() {
|
||||
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService conversionService = new DefaultConversionService();
|
||||
conversionService.addConverter(new MyStringArrayToResourceArrayConverter());
|
||||
Resource[] converted = conversionService.convert(new String[] {"x1", "z3"}, Resource[].class);
|
||||
assertEquals(2, converted.length);
|
||||
|
@ -236,7 +237,7 @@ public class GenericConversionServiceTests {
|
|||
|
||||
@Test
|
||||
public void testStringArrayToIntegerArray() {
|
||||
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService conversionService = new DefaultConversionService();
|
||||
conversionService.addConverter(new MyStringArrayToIntegerArrayConverter());
|
||||
Integer[] converted = conversionService.convert(new String[] {"x1", "z3"}, Integer[].class);
|
||||
assertEquals(2, converted.length);
|
||||
|
@ -246,7 +247,7 @@ public class GenericConversionServiceTests {
|
|||
|
||||
@Test
|
||||
public void testStringToIntegerArray() {
|
||||
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService conversionService = new DefaultConversionService();
|
||||
conversionService.addConverter(new MyStringToIntegerArrayConverter());
|
||||
Integer[] converted = conversionService.convert("x1,z3", Integer[].class);
|
||||
assertEquals(2, converted.length);
|
||||
|
@ -256,7 +257,7 @@ public class GenericConversionServiceTests {
|
|||
|
||||
@Test
|
||||
public void testWildcardMap() throws Exception {
|
||||
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService conversionService = new DefaultConversionService();
|
||||
Map<String, String> input = new LinkedHashMap<String, String>();
|
||||
input.put("key", "value");
|
||||
Object converted = conversionService.convert(input, new TypeDescriptor(getClass().getField("wildcardMap")));
|
||||
|
@ -265,14 +266,14 @@ public class GenericConversionServiceTests {
|
|||
|
||||
@Test
|
||||
public void testListOfList() {
|
||||
GenericConversionService service = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService service = new DefaultConversionService();
|
||||
List<List<String>> list = Collections.singletonList(Collections.singletonList("Foo"));
|
||||
assertNotNull(service.convert(list, String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringToString() {
|
||||
GenericConversionService service = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService service = new DefaultConversionService();
|
||||
String value = "myValue";
|
||||
String result = service.convert(value, String.class);
|
||||
assertSame(value, result);
|
||||
|
@ -280,7 +281,7 @@ public class GenericConversionServiceTests {
|
|||
|
||||
@Test
|
||||
public void testStringToObject() {
|
||||
GenericConversionService service = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService service = new DefaultConversionService();
|
||||
String value = "myValue";
|
||||
Object result = service.convert(value, Object.class);
|
||||
assertSame(value, result);
|
||||
|
@ -288,7 +289,7 @@ public class GenericConversionServiceTests {
|
|||
|
||||
@Test
|
||||
public void testIgnoreCopyConstructor() {
|
||||
GenericConversionService service = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService service = new DefaultConversionService();
|
||||
WithCopyConstructor value = new WithCopyConstructor();
|
||||
Object result = service.convert(value, WithCopyConstructor.class);
|
||||
assertSame(value, result);
|
||||
|
@ -296,7 +297,7 @@ public class GenericConversionServiceTests {
|
|||
|
||||
@Test
|
||||
public void testPerformance1() {
|
||||
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService conversionService = new DefaultConversionService();
|
||||
StopWatch watch = new StopWatch("integer->string conversionPerformance");
|
||||
watch.start("convert 4,000,000 with conversion service");
|
||||
for (int i = 0; i < 4000000; i++) {
|
||||
|
@ -313,7 +314,7 @@ public class GenericConversionServiceTests {
|
|||
|
||||
@Test
|
||||
public void testPerformance2() throws Exception {
|
||||
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService conversionService = new DefaultConversionService();
|
||||
StopWatch watch = new StopWatch("list<string> -> list<integer> conversionPerformance");
|
||||
watch.start("convert 4,000,000 with conversion service");
|
||||
List<String> source = new LinkedList<String>();
|
||||
|
@ -340,7 +341,7 @@ public class GenericConversionServiceTests {
|
|||
|
||||
@Test
|
||||
public void testPerformance3() throws Exception {
|
||||
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
GenericConversionService conversionService = new DefaultConversionService();
|
||||
StopWatch watch = new StopWatch("map<string, string> -> map<string, integer> conversionPerformance");
|
||||
watch.start("convert 4,000,000 with conversion service");
|
||||
Map<String, String> source = new HashMap<String, String>();
|
||||
|
@ -387,6 +388,7 @@ public class GenericConversionServiceTests {
|
|||
TypeDescriptor sourceType = TypeDescriptor.forObject(list);
|
||||
TypeDescriptor targetType = new TypeDescriptor(getClass().getField("emptyListDifferentTarget"));
|
||||
assertTrue(conversionService.canConvert(sourceType, targetType));
|
||||
@SuppressWarnings("unchecked")
|
||||
LinkedList<Integer> result = (LinkedList<Integer>) conversionService.convert(list, sourceType, targetType);
|
||||
assertEquals(LinkedList.class, result.getClass());
|
||||
assertTrue(result.isEmpty());
|
||||
|
@ -437,6 +439,7 @@ public class GenericConversionServiceTests {
|
|||
TypeDescriptor sourceType = TypeDescriptor.forObject(map);
|
||||
TypeDescriptor targetType = new TypeDescriptor(getClass().getField("emptyMapDifferentTarget"));
|
||||
assertTrue(conversionService.canConvert(sourceType, targetType));
|
||||
@SuppressWarnings("unchecked")
|
||||
LinkedHashMap<String, String> result = (LinkedHashMap<String, String>) conversionService.convert(map, sourceType, targetType);
|
||||
assertEquals(map, result);
|
||||
assertEquals(LinkedHashMap.class, result.getClass());
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package org.springframework.util;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
|
@ -43,10 +46,10 @@ public final class ObjectUtilsTests extends TestCase {
|
|||
}
|
||||
|
||||
public void testIsCompatibleWithThrowsClause() {
|
||||
Class[] empty = new Class[0];
|
||||
Class[] exception = new Class[] {Exception.class};
|
||||
Class[] sqlAndIO = new Class[] {SQLException.class, IOException.class};
|
||||
Class[] throwable = new Class[] {Throwable.class};
|
||||
Class<?>[] empty = new Class[0];
|
||||
Class<?>[] exception = new Class[] {Exception.class};
|
||||
Class<?>[] sqlAndIO = new Class[] {SQLException.class, IOException.class};
|
||||
Class<?>[] throwable = new Class[] {Throwable.class};
|
||||
|
||||
assertTrue(ObjectUtils.isCompatibleWithThrowsClause(new RuntimeException(), null));
|
||||
assertTrue(ObjectUtils.isCompatibleWithThrowsClause(new RuntimeException(), empty));
|
||||
|
@ -617,6 +620,35 @@ public final class ObjectUtilsTests extends TestCase {
|
|||
assertEquals("null", ObjectUtils.nullSafeToString((String[]) null));
|
||||
}
|
||||
|
||||
enum Tropes { FOO, BAR, baz };
|
||||
|
||||
public void testContainsConstant() {
|
||||
assertThat(ObjectUtils.containsConstant(Tropes.values(), "FOO"), is(true));
|
||||
assertThat(ObjectUtils.containsConstant(Tropes.values(), "foo"), is(true));
|
||||
assertThat(ObjectUtils.containsConstant(Tropes.values(), "BaR"), is(true));
|
||||
assertThat(ObjectUtils.containsConstant(Tropes.values(), "bar"), is(true));
|
||||
assertThat(ObjectUtils.containsConstant(Tropes.values(), "BAZ"), is(true));
|
||||
assertThat(ObjectUtils.containsConstant(Tropes.values(), "baz"), is(true));
|
||||
|
||||
assertThat(ObjectUtils.containsConstant(Tropes.values(), "BOGUS"), is(false));
|
||||
|
||||
assertThat(ObjectUtils.containsConstant(Tropes.values(), "FOO", true), is(true));
|
||||
assertThat(ObjectUtils.containsConstant(Tropes.values(), "foo", true), is(false));
|
||||
}
|
||||
|
||||
public void testCaseInsensitiveValueOf() {
|
||||
assertThat(ObjectUtils.caseInsensitiveValueOf(Tropes.values(), "foo"), is(Tropes.FOO));
|
||||
assertThat(ObjectUtils.caseInsensitiveValueOf(Tropes.values(), "BAR"), is(Tropes.BAR));
|
||||
try {
|
||||
ObjectUtils.caseInsensitiveValueOf(Tropes.values(), "bogus");
|
||||
fail("expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
is("constant [bogus] does not exist in enum type " +
|
||||
"org.springframework.util.ObjectUtilsTests$Tropes"));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertEqualHashCodes(int expected, Object array) {
|
||||
int actual = ObjectUtils.nullSafeHashCode(array);
|
||||
assertEquals(expected, actual);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -20,7 +20,7 @@ import org.springframework.core.convert.ConversionException;
|
|||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.ConverterNotFoundException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
|
@ -45,7 +45,7 @@ public class StandardTypeConverter implements TypeConverter {
|
|||
public StandardTypeConverter() {
|
||||
synchronized (this) {
|
||||
if (defaultConversionService == null) {
|
||||
defaultConversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
defaultConversionService = new DefaultConversionService();
|
||||
}
|
||||
}
|
||||
this.conversionService = defaultConversionService;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -29,7 +29,7 @@ import org.junit.Test;
|
|||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
|
@ -139,7 +139,7 @@ public class ExpressionTestsUsingCoreConversionService extends ExpressionTestCas
|
|||
*/
|
||||
private static class TypeConvertorUsingConversionService implements TypeConverter {
|
||||
|
||||
private final ConversionService service = ConversionServiceFactory.createDefaultConversionService();
|
||||
private final ConversionService service = new DefaultConversionService();
|
||||
|
||||
public Object convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException {
|
||||
return this.service.convert(value, typeDescriptor);
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
|
||||
/**
|
||||
* Tests directly or indirectly related to {@link FeatureConfiguration} class and
|
||||
* {@link Feature} method processing.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
/*
|
||||
* commented due to classpath visibility differences between Eclipse
|
||||
* and Ant/Ivy at the command line. Eclipse can see classes across
|
||||
* project test folders, Ant/Ivy are not configured to do so. Uncomment
|
||||
* as necessary when doing @Feature-related work.
|
||||
*
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({
|
||||
EarlyBeanReferenceProxyCreatorTests.class,
|
||||
SimpleFeatureMethodProcessingTests.class,
|
||||
BeanFactoryAwareFeatureConfigurationTests.class,
|
||||
FeatureMethodBeanReferenceTests.class,
|
||||
FeatureMethodQualifiedBeanReferenceTests.class,
|
||||
FeatureConfigurationClassTests.class,
|
||||
FeatureMethodEarlyBeanProxyTests.class,
|
||||
FeatureConfigurationImportTests.class,
|
||||
FeatureConfigurationImportResourceTests.class,
|
||||
|
||||
// context:component-scan related
|
||||
ComponentScanFeatureTests.class,
|
||||
ComponentScanSpecTests.class,
|
||||
ComponentScanAnnotationTests.class,
|
||||
ComponentScanAnnotationIntegrationTests.class,
|
||||
|
||||
// tx-related
|
||||
TxAnnotationDrivenFeatureTests.class,
|
||||
TxNamespaceHandlerTests.class,
|
||||
AnnotationTransactionNamespaceHandlerTests.class,
|
||||
AnnotationDrivenTests.class,
|
||||
|
||||
// mvc-related
|
||||
AnnotationDrivenBeanDefinitionParserTests.class,
|
||||
MvcAnnotationDrivenFeatureTests.class,
|
||||
MvcViewControllersTests.class,
|
||||
MvcResourcesTests.class,
|
||||
MvcDefaultServletHandlerTests.class,
|
||||
MvcNamespaceTests.class,
|
||||
})
|
||||
*/
|
||||
public class FeatureTestSuite {
|
||||
|
||||
}
|
|
@ -1,4 +1,21 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.expression.spel.support;
|
||||
|
||||
import java.beans.PropertyEditor;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
|
@ -8,7 +25,7 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
|||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
|
||||
/**
|
||||
|
@ -26,7 +43,7 @@ class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware {
|
|||
public BeanFactoryTypeConverter() {
|
||||
synchronized (this) {
|
||||
if (defaultConversionService == null) {
|
||||
defaultConversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
defaultConversionService = new DefaultConversionService();
|
||||
}
|
||||
}
|
||||
this.conversionService = defaultConversionService;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
|
@ -16,24 +16,18 @@
|
|||
|
||||
package org.springframework.transaction.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.aop.config.AopNamespaceUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
* implementation that allows users to easily configure all the infrastructure
|
||||
* beans required to enable annotation-driven transaction demarcation.
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser
|
||||
* BeanDefinitionParser} implementation that allows users to easily configure
|
||||
* all the infrastructure beans required to enable annotation-driven transaction
|
||||
* demarcation.
|
||||
*
|
||||
* <p>By default, all proxies are created as JDK proxies. This may cause some
|
||||
* problems if you are injecting objects as concrete classes rather than
|
||||
|
@ -43,7 +37,9 @@ import org.springframework.transaction.interceptor.TransactionInterceptor;
|
|||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @author Chris Beams
|
||||
* @since 2.0
|
||||
* @see TxAnnotationDriven
|
||||
*/
|
||||
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
|
@ -51,95 +47,43 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
|||
* The bean name of the internally managed transaction advisor (mode="proxy").
|
||||
*/
|
||||
public static final String TRANSACTION_ADVISOR_BEAN_NAME =
|
||||
"org.springframework.transaction.config.internalTransactionAdvisor";
|
||||
TxAnnotationDrivenExecutor.TRANSACTION_ADVISOR_BEAN_NAME;
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed transaction aspect (mode="aspectj").
|
||||
*/
|
||||
public static final String TRANSACTION_ASPECT_BEAN_NAME =
|
||||
"org.springframework.transaction.config.internalTransactionAspect";
|
||||
|
||||
private static final String TRANSACTION_ASPECT_CLASS_NAME =
|
||||
"org.springframework.transaction.aspectj.AnnotationTransactionAspect";
|
||||
TxAnnotationDrivenExecutor.TRANSACTION_ASPECT_BEAN_NAME;
|
||||
|
||||
|
||||
/**
|
||||
* Parses the '<code><tx:annotation-driven/></code>' tag. Will
|
||||
* Parses the {@code <tx:annotation-driven/>} tag. Will
|
||||
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
|
||||
* with the container as necessary.
|
||||
*/
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
String mode = element.getAttribute("mode");
|
||||
if ("aspectj".equals(mode)) {
|
||||
// mode="aspectj"
|
||||
registerTransactionAspect(element, parserContext);
|
||||
}
|
||||
else {
|
||||
// mode="proxy"
|
||||
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
|
||||
}
|
||||
new TxAnnotationDriven(element.getAttribute("transaction-manager"))
|
||||
.order(element.getAttribute("order"))
|
||||
.mode(element.getAttribute("mode"))
|
||||
.proxyTargetClass(Boolean.valueOf(element.getAttribute("proxy-target-class")))
|
||||
.source(parserContext.extractSource(element))
|
||||
.sourceName(element.getTagName())
|
||||
.execute(createExecutorContext(parserContext));
|
||||
return null;
|
||||
}
|
||||
|
||||
private void registerTransactionAspect(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(TRANSACTION_ASPECT_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(TRANSACTION_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
registerTransactionManager(element, def);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(def, TRANSACTION_ASPECT_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerTransactionManager(Element element, BeanDefinition def) {
|
||||
def.getPropertyValues().add("transactionManagerBeanName",
|
||||
TxNamespaceHandler.getTransactionManagerName(element));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to just introduce an AOP framework dependency when actually in proxy mode.
|
||||
* Adapt the given ParserContext instance into an ExecutorContext.
|
||||
*
|
||||
* TODO SPR-7420: consider unifying the two through a superinterface.
|
||||
* TODO SPR-7420: create a common ParserContext-to-ExecutorContext adapter util
|
||||
*/
|
||||
private static class AopAutoProxyConfigurer {
|
||||
|
||||
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
|
||||
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
|
||||
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME)) {
|
||||
Object eleSource = parserContext.extractSource(element);
|
||||
|
||||
// Create the TransactionAttributeSource definition.
|
||||
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class);
|
||||
sourceDef.setSource(eleSource);
|
||||
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
|
||||
|
||||
// Create the TransactionInterceptor definition.
|
||||
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
|
||||
interceptorDef.setSource(eleSource);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registerTransactionManager(element, interceptorDef);
|
||||
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
|
||||
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||
|
||||
// Create the TransactionAttributeSourceAdvisor definition.
|
||||
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
|
||||
advisorDef.setSource(eleSource);
|
||||
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
|
||||
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
|
||||
if (element.hasAttribute("order")) {
|
||||
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
|
||||
}
|
||||
parserContext.getRegistry().registerBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME, advisorDef);
|
||||
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, TRANSACTION_ADVISOR_BEAN_NAME));
|
||||
parserContext.registerComponent(compositeDef);
|
||||
}
|
||||
}
|
||||
private ExecutorContext createExecutorContext(ParserContext parserContext) {
|
||||
ExecutorContext executorContext = new ExecutorContext();
|
||||
executorContext.setRegistry(parserContext.getRegistry());
|
||||
executorContext.setRegistrar(parserContext);
|
||||
executorContext.setProblemReporter(parserContext.getReaderContext().getProblemReporter());
|
||||
return executorContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ public class JtaTransactionManagerBeanDefinitionParser extends AbstractSingleBea
|
|||
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
|
||||
return TxNamespaceHandler.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME;
|
||||
return TxAnnotationDriven.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,50 +37,55 @@ import org.springframework.util.StringUtils;
|
|||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
* for the <code><tx:advice></code> tag.
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser
|
||||
* BeanDefinitionParser} for the {@code <tx:advice/>} tag.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Adrian Colyer
|
||||
* @author Chris Beams
|
||||
* @since 2.0
|
||||
*/
|
||||
class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String ATTRIBUTES = "attributes";
|
||||
private static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
|
||||
|
||||
private static final String TIMEOUT = "timeout";
|
||||
private static final String METHOD_ELEMENT = "method";
|
||||
|
||||
private static final String READ_ONLY = "read-only";
|
||||
private static final String METHOD_NAME_ATTRIBUTE = "name";
|
||||
|
||||
private static final String NAME_MAP = "nameMap";
|
||||
private static final String ATTRIBUTES_ELEMENT = "attributes";
|
||||
|
||||
private static final String PROPAGATION = "propagation";
|
||||
private static final String TIMEOUT_ATTRIBUTE = "timeout";
|
||||
|
||||
private static final String ISOLATION = "isolation";
|
||||
private static final String READ_ONLY_ATTRIBUTE = "read-only";
|
||||
|
||||
private static final String ROLLBACK_FOR = "rollback-for";
|
||||
private static final String PROPAGATION_ATTRIBUTE = "propagation";
|
||||
|
||||
private static final String NO_ROLLBACK_FOR = "no-rollback-for";
|
||||
private static final String ISOLATION_ATTRIBUTE = "isolation";
|
||||
|
||||
private static final String ROLLBACK_FOR_ATTRIBUTE = "rollback-for";
|
||||
|
||||
private static final String NO_ROLLBACK_FOR_ATTRIBUTE = "no-rollback-for";
|
||||
|
||||
|
||||
@Override
|
||||
protected Class getBeanClass(Element element) {
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return TransactionInterceptor.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
builder.addPropertyReference("transactionManager", TxNamespaceHandler.getTransactionManagerName(element));
|
||||
builder.addPropertyReference("transactionManager", element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE));
|
||||
|
||||
List txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES);
|
||||
List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);
|
||||
if (txAttributes.size() > 1) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Element <attributes> is allowed at most once inside element <advice>", element);
|
||||
}
|
||||
else if (txAttributes.size() == 1) {
|
||||
// Using attributes source.
|
||||
Element attributeSourceElement = (Element) txAttributes.get(0);
|
||||
Element attributeSourceElement = txAttributes.get(0);
|
||||
RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
|
||||
builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
|
||||
}
|
||||
|
@ -92,20 +97,21 @@ class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
|||
}
|
||||
|
||||
private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
|
||||
List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, "method");
|
||||
ManagedMap transactionAttributeMap = new ManagedMap(methods.size());
|
||||
List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
|
||||
ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
|
||||
new ManagedMap<TypedStringValue, RuleBasedTransactionAttribute>(methods.size());
|
||||
transactionAttributeMap.setSource(parserContext.extractSource(attrEle));
|
||||
|
||||
for (Element methodEle : methods) {
|
||||
String name = methodEle.getAttribute("name");
|
||||
String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
|
||||
TypedStringValue nameHolder = new TypedStringValue(name);
|
||||
nameHolder.setSource(parserContext.extractSource(methodEle));
|
||||
|
||||
RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
|
||||
String propagation = methodEle.getAttribute(PROPAGATION);
|
||||
String isolation = methodEle.getAttribute(ISOLATION);
|
||||
String timeout = methodEle.getAttribute(TIMEOUT);
|
||||
String readOnly = methodEle.getAttribute(READ_ONLY);
|
||||
String propagation = methodEle.getAttribute(PROPAGATION_ATTRIBUTE);
|
||||
String isolation = methodEle.getAttribute(ISOLATION_ATTRIBUTE);
|
||||
String timeout = methodEle.getAttribute(TIMEOUT_ATTRIBUTE);
|
||||
String readOnly = methodEle.getAttribute(READ_ONLY_ATTRIBUTE);
|
||||
if (StringUtils.hasText(propagation)) {
|
||||
attribute.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);
|
||||
}
|
||||
|
@ -121,16 +127,16 @@ class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
|||
}
|
||||
}
|
||||
if (StringUtils.hasText(readOnly)) {
|
||||
attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute(READ_ONLY)));
|
||||
attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
|
||||
}
|
||||
|
||||
List<RollbackRuleAttribute> rollbackRules = new LinkedList<RollbackRuleAttribute>();
|
||||
if (methodEle.hasAttribute(ROLLBACK_FOR)) {
|
||||
String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR);
|
||||
if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {
|
||||
String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);
|
||||
addRollbackRuleAttributesTo(rollbackRules,rollbackForValue);
|
||||
}
|
||||
if (methodEle.hasAttribute(NO_ROLLBACK_FOR)) {
|
||||
String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR);
|
||||
if (methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) {
|
||||
String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE);
|
||||
addNoRollbackRuleAttributesTo(rollbackRules,noRollbackForValue);
|
||||
}
|
||||
attribute.setRollbackRules(rollbackRules);
|
||||
|
@ -140,7 +146,7 @@ class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
|||
|
||||
RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
|
||||
attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
|
||||
attributeSourceDefinition.getPropertyValues().add(NAME_MAP, transactionAttributeMap);
|
||||
attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
|
||||
return attributeSourceDefinition;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.transaction.config;
|
||||
|
||||
import org.springframework.beans.factory.parsing.SimpleProblemCollector;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.AdviceMode;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class TxAnnotationDriven extends AbstractFeatureSpecification {
|
||||
|
||||
static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
|
||||
|
||||
private static final Class<? extends FeatureSpecificationExecutor> EXECUTOR_TYPE = TxAnnotationDrivenExecutor.class;
|
||||
|
||||
private Object txManager = null;
|
||||
|
||||
private Object order = null;
|
||||
|
||||
private Boolean proxyTargetClass = false;
|
||||
|
||||
private Object mode = AdviceMode.PROXY;
|
||||
|
||||
/**
|
||||
* Create a {@code TxAnnotationDriven} specification assumes the presence of a
|
||||
* {@link PlatformTransactionManager} bean named {@value #DEFAULT_TRANSACTION_MANAGER_BEAN_NAME}.
|
||||
*
|
||||
* <p>See the alternate constructors defined here if your transaction manager does
|
||||
* not follow this default naming or you wish to refer to it by bean instance rather
|
||||
* than by bean name.
|
||||
* @see #TxAnnotationDriven(String)
|
||||
* @see #TxAnnotationDriven(PlatformTransactionManager)
|
||||
*/
|
||||
public TxAnnotationDriven() {
|
||||
this(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code TxAnnotationDriven} specification that will use the specified
|
||||
* transaction manager bean name.
|
||||
*
|
||||
* @param txManagerBeanName name of {@link PlatformTransactionManager} bean or a
|
||||
* ${placeholder} or SpEL #{expression} resolving to bean name. If {@code null},
|
||||
* falls back to default value of {@value #DEFAULT_TRANSACTION_MANAGER_BEAN_NAME}.
|
||||
*/
|
||||
public TxAnnotationDriven(String txManagerBeanName) {
|
||||
super(EXECUTOR_TYPE);
|
||||
this.txManager = txManagerBeanName != null ?
|
||||
txManagerBeanName :
|
||||
DEFAULT_TRANSACTION_MANAGER_BEAN_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new TxAnnotationDriven specification that will use the specified transaction
|
||||
* manager.
|
||||
*
|
||||
* @param txManager the {@link PlatformTransactionManager} bean to use. Must not be {@code null}.
|
||||
*/
|
||||
public TxAnnotationDriven(PlatformTransactionManager txManager) {
|
||||
super(EXECUTOR_TYPE);
|
||||
Assert.notNull(txManager, "transaction manager must not be null");
|
||||
this.txManager = txManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction manager to use. May be a {@link PlatformTransactionManager}
|
||||
* instance or a String representing the bean name or a placeholder or SpEL expression
|
||||
* that resolves to the bean name.
|
||||
*/
|
||||
Object transactionManager() {
|
||||
return this.txManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate how transactional advice should be applied.
|
||||
* @see AdviceMode
|
||||
*/
|
||||
public TxAnnotationDriven mode(AdviceMode mode) {
|
||||
this.mode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate how transactional advice should be applied.
|
||||
* @param name matching one of the labels in the AdviceMode enum;
|
||||
* placeholder and SpEL expressions are not allowed.
|
||||
* @see AdviceMode
|
||||
*/
|
||||
TxAnnotationDriven mode(String mode) {
|
||||
if (StringUtils.hasText(mode)) {
|
||||
this.mode = mode;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return how transactional advice should be applied.
|
||||
*/
|
||||
AdviceMode mode() {
|
||||
if (this.mode instanceof AdviceMode) {
|
||||
return (AdviceMode)this.mode;
|
||||
}
|
||||
if (this.mode instanceof String) {
|
||||
return ObjectUtils.caseInsensitiveValueOf(AdviceMode.values(), (String)this.mode);
|
||||
}
|
||||
// TODO SPR-7420: deal with in validate & raise problem
|
||||
throw new IllegalStateException(
|
||||
"invalid type for field 'mode' (must be of type AdviceMode or String): "
|
||||
+ this.mode.getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether class-based (CGLIB) proxies are to be created as opposed
|
||||
* to standard Java interface-based proxies.
|
||||
*
|
||||
* <p>Note: Class-based proxies require the {@link Transactional @Transactional}
|
||||
* annotation to be defined on the concrete class. Annotations in interfaces will
|
||||
* not work in that case (they will rather only work with interface-based proxies)!
|
||||
*/
|
||||
public TxAnnotationDriven proxyTargetClass(Boolean proxyTargetClass) {
|
||||
this.proxyTargetClass = proxyTargetClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether class-based (CGLIB) proxies are to be created as opposed
|
||||
* to standard Java interface-based proxies.
|
||||
*/
|
||||
Boolean proxyTargetClass() {
|
||||
return this.proxyTargetClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the ordering of the execution of the transaction advisor
|
||||
* when multiple advice executes at a specific joinpoint. The default is
|
||||
* {@code null}, indicating that default ordering should be used.
|
||||
*/
|
||||
public TxAnnotationDriven order(int order) {
|
||||
this.order = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the ordering of the execution of the transaction advisor
|
||||
* when multiple advice executes at a specific joinpoint. The default is
|
||||
* {@code null}, indicating that default ordering should be used.
|
||||
*/
|
||||
public TxAnnotationDriven order(String order) {
|
||||
if (StringUtils.hasText(order)) {
|
||||
this.order = order;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ordering of the execution of the transaction advisor
|
||||
* when multiple advice executes at a specific joinpoint. May return
|
||||
* {@code null}, indicating that default ordering should be used.
|
||||
*/
|
||||
Object order() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(SimpleProblemCollector problems) {
|
||||
if (this.mode instanceof String) {
|
||||
if (!ObjectUtils.containsConstant(AdviceMode.values(), (String)this.mode)) {
|
||||
problems.error("no such mode name: " + this.mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.transaction.config;
|
||||
|
||||
import org.springframework.aop.config.AopNamespaceUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.ComponentRegistrar;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.config.AbstractSpecificationExecutor;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
final class TxAnnotationDrivenExecutor extends AbstractSpecificationExecutor<TxAnnotationDriven> {
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed transaction advisor (used when mode == PROXY).
|
||||
*/
|
||||
public static final String TRANSACTION_ADVISOR_BEAN_NAME =
|
||||
"org.springframework.transaction.config.internalTransactionAdvisor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed transaction aspect (used when mode == ASPECTJ).
|
||||
*/
|
||||
public static final String TRANSACTION_ASPECT_BEAN_NAME =
|
||||
"org.springframework.transaction.config.internalTransactionAspect";
|
||||
|
||||
private static final String TRANSACTION_ASPECT_CLASS_NAME =
|
||||
"org.springframework.transaction.aspectj.AnnotationTransactionAspect";
|
||||
|
||||
|
||||
@Override
|
||||
protected void doExecute(TxAnnotationDriven txSpec, ExecutorContext executorContext) {
|
||||
BeanDefinitionRegistry registry = executorContext.getRegistry();
|
||||
ComponentRegistrar registrar = executorContext.getRegistrar();
|
||||
switch (txSpec.mode()) {
|
||||
case ASPECTJ:
|
||||
registerTransactionAspect(txSpec, registry, registrar);
|
||||
break;
|
||||
case PROXY:
|
||||
AopAutoProxyConfigurer.configureAutoProxyCreator(txSpec, registry, registrar);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
String.format("AdviceMode %s is not supported", txSpec.mode()));
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTransactionAspect(TxAnnotationDriven spec, BeanDefinitionRegistry registry, ComponentRegistrar registrar) {
|
||||
if (!registry.containsBeanDefinition(TRANSACTION_ASPECT_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(TRANSACTION_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
registerTransactionManager(spec, def);
|
||||
registrar.registerBeanComponent(new BeanComponentDefinition(def, TRANSACTION_ASPECT_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerTransactionManager(TxAnnotationDriven spec, BeanDefinition def) {
|
||||
Object txManager = spec.transactionManager();
|
||||
Assert.notNull(txManager, "transactionManager must be specified");
|
||||
if (txManager instanceof String) {
|
||||
def.getPropertyValues().add("transactionManagerBeanName", txManager);
|
||||
} else {
|
||||
def.getPropertyValues().add("transactionManager", txManager);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to just introduce an AOP framework dependency when actually in proxy mode.
|
||||
*/
|
||||
private static class AopAutoProxyConfigurer {
|
||||
|
||||
public static void configureAutoProxyCreator(TxAnnotationDriven txSpec, BeanDefinitionRegistry registry, ComponentRegistrar registrar) {
|
||||
Object source = txSpec.source();
|
||||
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(registry, registrar, source, txSpec.proxyTargetClass());
|
||||
|
||||
if (!registry.containsBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME)) {
|
||||
|
||||
// Create the TransactionAttributeSource definition.
|
||||
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class);
|
||||
sourceDef.setSource(source);
|
||||
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String sourceName = registrar.registerWithGeneratedName(sourceDef);
|
||||
|
||||
// Create the TransactionInterceptor definition.
|
||||
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
|
||||
interceptorDef.setSource(source);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registerTransactionManager(txSpec, interceptorDef);
|
||||
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
|
||||
String interceptorName = registrar.registerWithGeneratedName(interceptorDef);
|
||||
|
||||
// Create the TransactionAttributeSourceAdvisor definition.
|
||||
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
|
||||
advisorDef.setSource(source);
|
||||
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
|
||||
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
|
||||
if (txSpec.order() != null) {
|
||||
advisorDef.getPropertyValues().add("order", txSpec.order());
|
||||
}
|
||||
registry.registerBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME, advisorDef);
|
||||
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(txSpec.sourceName(), source);
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, TRANSACTION_ADVISOR_BEAN_NAME));
|
||||
registrar.registerComponent(compositeDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue