Remove "Feature" support introduced in 3.1 M1
Feature-related support such as @Feature, @FeatureConfiguration, and FeatureSpecification types will be replaced by framework-provided @Configuration classes and convenience annotations such as @ComponentScan (already exists), @EnableAsync, @EnableScheduling, @EnableTransactionManagement and others. Issue: SPR-8012,SPR-8034,SPR-8039,SPR-8188,SPR-8206,SPR-8223, SPR-8225,SPR-8226,SPR-8227 git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4255 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
e6f3fd2de0
commit
c892028705
|
|
@ -7,6 +7,8 @@ Changes in version 3.1 M2 (2011-??-??)
|
|||
--------------------------------------
|
||||
|
||||
* deprecated AbstractJUnit38SpringContextTests and AbstractTransactionalJUnit38SpringContextTests
|
||||
* eliminated @Feature support in favor of @Enable* and framework-provided @Configuration classes
|
||||
* introduced @EnableTransactionManagement, @EnableScheduling, and other @Enable* annotations
|
||||
|
||||
|
||||
Changes in version 3.1 M1 (2011-02-11)
|
||||
|
|
|
|||
|
|
@ -16,13 +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.parsing.ComponentRegistrarAdapter;
|
||||
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
|
||||
|
|
@ -53,41 +52,22 @@ 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) {
|
||||
|
||||
BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
|
||||
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
|
||||
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
|
||||
registerComponentIfNecessary(beanDefinition, new ComponentRegistrarAdapter(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) {
|
||||
|
||||
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
|
||||
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
|
||||
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
|
||||
registerComponentIfNecessary(beanDefinition, new ComponentRegistrarAdapter(parserContext));
|
||||
registerComponentIfNecessary(beanDefinition, parserContext);
|
||||
}
|
||||
|
||||
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
|
||||
|
|
@ -96,7 +76,7 @@ public abstract class AopNamespaceUtils {
|
|||
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
|
||||
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
|
||||
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
|
||||
registerComponentIfNecessary(beanDefinition, new ComponentRegistrarAdapter(parserContext));
|
||||
registerComponentIfNecessary(beanDefinition, parserContext);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -108,7 +88,7 @@ public abstract class AopNamespaceUtils {
|
|||
public static void registerAutoProxyCreatorIfNecessary(ParserContext parserContext, Object source) {
|
||||
BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
|
||||
parserContext.getRegistry(), source);
|
||||
registerComponentIfNecessary(beanDefinition, new ComponentRegistrarAdapter(parserContext));
|
||||
registerComponentIfNecessary(beanDefinition, parserContext);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -121,12 +101,6 @@ 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));
|
||||
|
|
@ -140,20 +114,11 @@ public abstract class AopNamespaceUtils {
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) {
|
||||
if (beanDefinition != null) {
|
||||
BeanComponentDefinition componentDefinition =
|
||||
new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
|
||||
componentRegistrar.registerComponent(componentDefinition);
|
||||
parserContext.registerComponent(componentDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface ComponentRegistrar {
|
||||
|
||||
String registerWithGeneratedName(BeanDefinition beanDefinition);
|
||||
|
||||
void registerBeanComponent(BeanComponentDefinition component);
|
||||
|
||||
void registerComponent(ComponentDefinition component);
|
||||
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
* <p>Adapter is necessary as opposed to having ParserContext
|
||||
* implement ComponentRegistrar directly due to tooling issues.
|
||||
* STS may ship with a version of Spring older that 3.1 (when
|
||||
* this type was introduced), and will run into
|
||||
* IncompatibleClassChangeErrors when it's (3.0.5) ParserContext
|
||||
* tries to mix with our (3.1.0) BeanDefinitionParser
|
||||
* (and related) infrastructure.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ComponentRegistrarAdapter implements ComponentRegistrar {
|
||||
|
||||
private final ParserContext parserContext;
|
||||
|
||||
public ComponentRegistrarAdapter(ParserContext parserContext) {
|
||||
this.parserContext = parserContext;
|
||||
}
|
||||
|
||||
public String registerWithGeneratedName(BeanDefinition beanDefinition) {
|
||||
return this.parserContext.getReaderContext().registerWithGeneratedName(beanDefinition);
|
||||
}
|
||||
|
||||
public void registerBeanComponent(BeanComponentDefinition component) {
|
||||
this.parserContext.registerBeanComponent(component);
|
||||
}
|
||||
|
||||
public void registerComponent(ComponentDefinition component) {
|
||||
this.parserContext.registerComponent(component);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface ProblemCollector {
|
||||
|
||||
void error(String message);
|
||||
|
||||
void error(String message, Throwable cause);
|
||||
|
||||
void reportProblems(ProblemReporter reporter);
|
||||
|
||||
boolean hasErrors();
|
||||
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* 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 implements ProblemCollector {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||
|
||||
/**
|
||||
* Interface used by the {@link DefaultBeanDefinitionDocumentReader} to handle custom,
|
||||
* top-level (directly under {@code <beans>}) tags.
|
||||
* 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.
|
||||
|
|
@ -30,19 +30,10 @@ 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 AbstractBeanDefinitionParser
|
||||
* @see org.springframework.beans.factory.xml.BeanDefinitionDecorator
|
||||
* @see org.springframework.context.FeatureSpecification
|
||||
* @see org.springframework.context.AbstractSpecificationExecutor
|
||||
*/
|
||||
public interface BeanDefinitionParser {
|
||||
|
||||
|
|
@ -53,12 +44,11 @@ public interface BeanDefinitionParser {
|
|||
* embedded in the supplied {@link ParserContext}.
|
||||
* <p>Implementations must return the primary {@link BeanDefinition} that results
|
||||
* from the parse if they will ever be used in a nested fashion (for example as
|
||||
* an inner tag in a <code><property/></code> tag). Implementations may return
|
||||
* <code>null</code> if they will <strong>not</strong> be used in a nested fashion.
|
||||
* an inner tag in a {@code <property/>} tag). Implementations may return
|
||||
* {@code null} if they will <strong>not</strong> be used in a nested fashion.
|
||||
* @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions}
|
||||
* @param parserContext the object encapsulating the current state of the parsing process;
|
||||
* provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry
|
||||
* BeanDefinitionRegistry}.
|
||||
* provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
|
||||
* @return the primary {@link BeanDefinition}
|
||||
*/
|
||||
BeanDefinition parse(Element element, ParserContext parserContext);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beansProjectDescription>
|
||||
<version>1</version>
|
||||
<pluginVersion><![CDATA[2.5.0.201008272200-CI-R3822-B852]]></pluginVersion>
|
||||
<pluginVersion><![CDATA[2.6.0.201103160035-RELEASE]]></pluginVersion>
|
||||
<configSuffixes>
|
||||
<configSuffix><![CDATA[xml]]></configSuffix>
|
||||
</configSuffixes>
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
<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>
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@
|
|||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import org.springframework.beans.factory.parsing.Location;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
|
||||
/**
|
||||
* Represents a {@link Configuration} class method marked with the {@link Bean} annotation.
|
||||
* Represents a {@link Configuration} class method marked with the
|
||||
* {@link Bean} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
|
|
@ -31,30 +31,13 @@ import org.springframework.core.type.MethodMetadata;
|
|||
* @see ConfigurationClassParser
|
||||
* @see ConfigurationClassBeanDefinitionReader
|
||||
*/
|
||||
final class BeanMethod {
|
||||
|
||||
private final MethodMetadata metadata;
|
||||
|
||||
private final ConfigurationClass configurationClass;
|
||||
|
||||
final class BeanMethod extends ConfigurationMethod {
|
||||
|
||||
public BeanMethod(MethodMetadata metadata, ConfigurationClass configurationClass) {
|
||||
this.metadata = metadata;
|
||||
this.configurationClass = configurationClass;
|
||||
}
|
||||
|
||||
public MethodMetadata getMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
public ConfigurationClass getConfigurationClass() {
|
||||
return this.configurationClass;
|
||||
}
|
||||
|
||||
public Location getResourceLocation() {
|
||||
return new Location(this.configurationClass.getResource(), this.metadata);
|
||||
super(metadata, configurationClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(ProblemReporter problemReporter) {
|
||||
if (this.configurationClass.getMetadata().isAnnotated(Configuration.class.getName())) {
|
||||
if (!getMetadata().isOverridable()) {
|
||||
|
|
@ -68,13 +51,6 @@ final class BeanMethod {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[%s:name=%s,declaringClass=%s]",
|
||||
this.getClass().getSimpleName(), this.getMetadata().getMethodName(), this.getMetadata().getDeclaringClassName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link Bean} methods must be overridable in order to accommodate CGLIB.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* 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-2011 the original author or authors.
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -16,60 +16,263 @@
|
|||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.config.AbstractSpecificationBeanDefinitionParser;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
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 {@code <context:component-scan/>} element. Parsed metadata is
|
||||
* used to populate and execute a {@link ComponentScanSpec} instance.
|
||||
* Parser for the {@code <context:component-scan/>} element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Ramnivas Laddad
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 2.5
|
||||
* @see ComponentScan
|
||||
* @see ComponentScanSpec
|
||||
* @see ComponentScanExecutor
|
||||
*/
|
||||
public class ComponentScanBeanDefinitionParser extends AbstractSpecificationBeanDefinitionParser {
|
||||
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
public FeatureSpecification doParse(Element element, ParserContext parserContext) {
|
||||
ClassLoader classLoader = parserContext.getReaderContext().getResourceLoader().getClassLoader();
|
||||
private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
|
||||
|
||||
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"))
|
||||
.beanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults())
|
||||
.autowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
|
||||
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();
|
||||
|
||||
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.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) {
|
||||
|
||||
// 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);
|
||||
String filterType = ((Element)node).getAttribute("type");
|
||||
String expression = ((Element)node).getAttribute("expression");
|
||||
if ("include-filter".equals(localName)) {
|
||||
spec.addIncludeFilter(filterType, expression, classLoader);
|
||||
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);
|
||||
}
|
||||
}
|
||||
else if ("exclude-filter".equals(localName)) {
|
||||
spec.addExcludeFilter(filterType, expression, classLoader);
|
||||
catch (Exception ex) {
|
||||
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spec;
|
||||
@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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* 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.SpecificationContext;
|
||||
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, SpecificationContext specificationContext) {
|
||||
BeanDefinitionRegistry registry = specificationContext.getRegistry();
|
||||
ResourceLoader resourceLoader = specificationContext.getResourceLoader();
|
||||
Environment environment = specificationContext.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));
|
||||
}
|
||||
}
|
||||
|
||||
specificationContext.getRegistrar().registerComponent(compositeDef);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,460 +0,0 @@
|
|||
/*
|
||||
* 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.ProblemCollector;
|
||||
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(ProblemCollector 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, ProblemCollector 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(ProblemCollector 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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -33,7 +33,6 @@ 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;
|
||||
|
|
@ -45,8 +44,8 @@ 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.FeatureSpecification;
|
||||
import org.springframework.context.config.SpecificationContext;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
|
@ -94,7 +93,7 @@ public class ConfigurationClassBeanDefinitionReader {
|
|||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private SpecificationContext specificationContext;
|
||||
private Environment environment;
|
||||
|
||||
/**
|
||||
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
|
||||
|
|
@ -111,13 +110,7 @@ public class ConfigurationClassBeanDefinitionReader {
|
|||
this.problemReporter = problemReporter;
|
||||
this.metadataReaderFactory = metadataReaderFactory;
|
||||
this.resourceLoader = resourceLoader;
|
||||
// TODO SPR-7420: see about passing in the SpecificationContext created in ConfigurationClassPostProcessor
|
||||
this.specificationContext = new SpecificationContext();
|
||||
this.specificationContext.setRegistry(this.registry);
|
||||
this.specificationContext.setRegistrar(new SimpleComponentRegistrar(this.registry));
|
||||
this.specificationContext.setResourceLoader(this.resourceLoader);
|
||||
this.specificationContext.setEnvironment(environment);
|
||||
this.specificationContext.setProblemReporter(problemReporter);
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -137,7 +130,6 @@ public class ConfigurationClassBeanDefinitionReader {
|
|||
*/
|
||||
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
|
||||
AnnotationMetadata metadata = configClass.getMetadata();
|
||||
processFeatureAnnotations(metadata);
|
||||
doLoadBeanDefinitionForConfigurationClassIfNecessary(configClass);
|
||||
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
|
||||
loadBeanDefinitionsForBeanMethod(beanMethod);
|
||||
|
|
@ -145,27 +137,6 @@ public class ConfigurationClassBeanDefinitionReader {
|
|||
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.specificationContext);
|
||||
}
|
||||
}
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// TODO SPR-7420: what exception to throw?
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the {@link Configuration} class itself as a bean definition.
|
||||
*/
|
||||
|
|
@ -200,7 +171,7 @@ public class ConfigurationClassBeanDefinitionReader {
|
|||
}
|
||||
|
||||
/**
|
||||
* Read a particular {@link BeanMethod}, registering bean definitions
|
||||
* Read the given {@link BeanMethod}, registering bean definitions
|
||||
* with the BeanDefinitionRegistry based on its contents.
|
||||
*/
|
||||
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
|
||||
|
|
@ -423,8 +394,8 @@ public class ConfigurationClassBeanDefinitionReader {
|
|||
*/
|
||||
private static class InvalidConfigurationImportProblem extends Problem {
|
||||
public InvalidConfigurationImportProblem(String className, Resource resource, AnnotationMetadata metadata) {
|
||||
super(String.format("%s was @Import'ed but is not annotated with @FeatureConfiguration or " +
|
||||
"@Configuration nor does it declare any @Bean methods. Update the class to " +
|
||||
super(String.format("%s was @Import'ed but is not annotated with @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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,15 +197,12 @@ 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 enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
|
||||
MethodProxy cglibMethodProxy) throws ProxyCreationException, Throwable {
|
||||
MethodProxy cglibMethodProxy) throws Throwable {
|
||||
|
||||
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,15 +16,9 @@
|
|||
|
||||
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;
|
||||
|
||||
|
|
@ -32,12 +26,10 @@ 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;
|
||||
|
|
@ -47,23 +39,14 @@ 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.FeatureSpecification;
|
||||
import org.springframework.context.config.SourceAwareSpecification;
|
||||
import org.springframework.context.config.SpecificationContext;
|
||||
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
|
||||
|
|
@ -194,175 +177,13 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
}
|
||||
|
||||
/**
|
||||
* Find and process all @Configuration classes with @Feature methods in the given registry.
|
||||
* Find and process all @Configuration classes in the given registry.
|
||||
*/
|
||||
private void processConfigurationClasses(BeanDefinitionRegistry registry) {
|
||||
ConfigurationClassBeanDefinitionReader reader = getConfigurationClassBeanDefinitionReader(registry);
|
||||
processConfigBeanDefinitions(registry, reader);
|
||||
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment);
|
||||
processConfigBeanDefinitions(parser, reader, registry);
|
||||
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 SpecificationContext specificationContext = createSpecificationContext(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, specificationContext, 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,
|
||||
SpecificationContext specificationContext, EarlyBeanReferenceProxyCreator proxyCreator) {
|
||||
try {
|
||||
// get the return type
|
||||
if (!(FeatureSpecification.class.isAssignableFrom(featureMethod.getReturnType()))) {
|
||||
// TODO SPR-7420: raise a Problem instead?
|
||||
throw new IllegalArgumentException(
|
||||
format("Return type for @Feature method %s.%s() must be assignable to FeatureSpecification",
|
||||
featureMethod.getDeclaringClass().getSimpleName(), featureMethod.getName()));
|
||||
}
|
||||
|
||||
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.createProxyIfPossible(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(specificationContext);
|
||||
} catch (Exception ex) {
|
||||
throw new FeatureMethodExecutionException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private SpecificationContext createSpecificationContext(ConfigurableListableBeanFactory beanFactory) {
|
||||
final BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
|
||||
SpecificationContext specificationContext = new SpecificationContext();
|
||||
specificationContext.setEnvironment(this.environment);
|
||||
specificationContext.setResourceLoader(this.resourceLoader);
|
||||
specificationContext.setRegistry(registry);
|
||||
specificationContext.setRegistrar(new SimpleComponentRegistrar(registry));
|
||||
specificationContext.setProblemReporter(this.problemReporter);
|
||||
return specificationContext;
|
||||
}
|
||||
|
||||
private ConfigurationClassBeanDefinitionReader getConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry) {
|
||||
|
|
@ -377,7 +198,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
* Build and validate a configuration model based on the registry of
|
||||
* {@link Configuration} classes.
|
||||
*/
|
||||
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry, ConfigurationClassBeanDefinitionReader reader) {
|
||||
public void processConfigBeanDefinitions(ConfigurationClassParser parser, ConfigurationClassBeanDefinitionReader reader, BeanDefinitionRegistry registry) {
|
||||
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
|
||||
for (String beanName : registry.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
||||
|
|
@ -391,8 +212,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
return;
|
||||
}
|
||||
|
||||
// Populate a new configuration model by parsing each @Configuration classes
|
||||
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment);
|
||||
// Parse each @Configuration class
|
||||
for (BeanDefinitionHolder holder : configCandidates) {
|
||||
BeanDefinition bd = holder.getBeanDefinition();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.parsing.Location;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
|
||||
/**
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
abstract class ConfigurationMethod {
|
||||
|
||||
protected final MethodMetadata metadata;
|
||||
|
||||
protected final ConfigurationClass configurationClass;
|
||||
|
||||
|
||||
public ConfigurationMethod(MethodMetadata metadata, ConfigurationClass configurationClass) {
|
||||
this.metadata = metadata;
|
||||
this.configurationClass = configurationClass;
|
||||
}
|
||||
|
||||
public MethodMetadata getMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
public ConfigurationClass getConfigurationClass() {
|
||||
return this.configurationClass;
|
||||
}
|
||||
|
||||
public Location getResourceLocation() {
|
||||
return new Location(this.configurationClass.getResource(), this.metadata);
|
||||
}
|
||||
|
||||
public void validate(ProblemReporter problemReporter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[%s:name=%s,declaringClass=%s]",
|
||||
this.getClass().getSimpleName(), this.getMetadata().getMethodName(), this.getMetadata().getDeclaringClassName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
|
||||
}
|
||||
|
|
@ -1,254 +0,0 @@
|
|||
/*
|
||||
* 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 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. No proxy is created if the dependency type
|
||||
* is final, rather the dependency is resolved immediately. This is important
|
||||
* especially with regard to supporting @Value injection.
|
||||
*/
|
||||
public Object createProxyIfPossible(DependencyDescriptor descriptor) {
|
||||
if (Modifier.isFinal(descriptor.getDependencyType().getModifiers())) {
|
||||
return beanFactory.resolveDependency(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");
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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 {
|
||||
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* 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 "";
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* 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.ProblemCollector;
|
||||
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) {
|
||||
ProblemCollector collector = new SimpleProblemCollector(this.source());
|
||||
this.doValidate(collector);
|
||||
collector.reportProblems(problemReporter);
|
||||
return collector.hasErrors() ? false : true;
|
||||
}
|
||||
|
||||
protected abstract void doValidate(ProblemCollector problems);
|
||||
|
||||
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(SpecificationContext specificationContext) {
|
||||
FeatureSpecificationExecutor executor =
|
||||
BeanUtils.instantiateClass(this.executorType);
|
||||
executor.execute(this, specificationContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* 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 java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.ComponentRegistrarAdapter;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.beans.factory.parsing.ReaderContext;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.env.DefaultEnvironment;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractSpecificationBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
public final BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
FeatureSpecification spec = doParse(element, parserContext);
|
||||
if (spec instanceof SourceAwareSpecification) {
|
||||
((SourceAwareSpecification)spec).source(parserContext.getReaderContext().extractSource(element));
|
||||
((SourceAwareSpecification)spec).sourceName(element.getTagName());
|
||||
}
|
||||
spec.execute(specificationContextFrom(parserContext));
|
||||
return null;
|
||||
}
|
||||
|
||||
abstract protected FeatureSpecification doParse(Element element, ParserContext parserContext);
|
||||
|
||||
/**
|
||||
* Adapt the given ParserContext into a SpecificationContext.
|
||||
*/
|
||||
private SpecificationContext specificationContextFrom(ParserContext parserContext) {
|
||||
SpecificationContext specContext = new SpecificationContext();
|
||||
specContext.setRegistry(parserContext.getRegistry());
|
||||
specContext.setRegistrar(new ComponentRegistrarAdapter(parserContext));
|
||||
specContext.setResourceLoader(parserContext.getReaderContext().getResourceLoader());
|
||||
try {
|
||||
// again, STS constraints around the addition of the new getEnvironment()
|
||||
// method in 3.1.0 (it's not present in STS current spring version, 3.0.5)
|
||||
// TODO 3.1 GA: remove this block prior to 3.1 GA
|
||||
specContext.setEnvironment(parserContext.getDelegate().getEnvironment());
|
||||
} catch (NoSuchMethodError ex) {
|
||||
specContext.setEnvironment(new DefaultEnvironment());
|
||||
}
|
||||
try {
|
||||
// access the reader context's problem reporter reflectively in order to
|
||||
// compensate for tooling (STS) constraints around introduction of changes
|
||||
// to parser context / reader context classes.
|
||||
// TODO 3.1 GA: remove this block prior to 3.1 GA
|
||||
Field field = ReaderContext.class.getDeclaredField("problemReporter");
|
||||
field.setAccessible(true);
|
||||
ProblemReporter problemReporter = (ProblemReporter)field.get(parserContext.getReaderContext());
|
||||
specContext.setProblemReporter(problemReporter);
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException(
|
||||
"Could not access field 'ReaderContext#problemReporter' on object " +
|
||||
parserContext.getReaderContext(), ex);
|
||||
}
|
||||
return specContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* 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, SpecificationContext specificationContext) {
|
||||
Assert.notNull(spec, "Specification must not be null");
|
||||
Assert.notNull(spec, "SpecificationContext 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(specificationContext.getProblemReporter())) {
|
||||
doExecute((S)spec, specificationContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, SpecificationContext specificationContext);
|
||||
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* 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 to a bean by 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 feature set
|
||||
* 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(FeatureSpecification, SpecificationContext)
|
||||
* @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(SpecificationContext specificationContext);
|
||||
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* 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, SpecificationContext specificationContext);
|
||||
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* 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,81 +0,0 @@
|
|||
/*
|
||||
* 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 SpecificationContext {
|
||||
|
||||
private BeanDefinitionRegistry registry;
|
||||
private ComponentRegistrar registrar;
|
||||
private ResourceLoader resourceLoader;
|
||||
private Environment environment;
|
||||
private ProblemReporter problemReporter;
|
||||
|
||||
public SpecificationContext() { }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* 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.SpecificationContext;
|
||||
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 SpecificationContext specificationContext;
|
||||
private DefaultListableBeanFactory bf;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.bf = new DefaultListableBeanFactory();
|
||||
this.executor = new ComponentScanExecutor();
|
||||
this.specificationContext = new SpecificationContext();
|
||||
this.specificationContext.setRegistry(bf);
|
||||
this.specificationContext.setResourceLoader(new DefaultResourceLoader());
|
||||
this.specificationContext.setEnvironment(new MockEnvironment());
|
||||
this.specificationContext.setRegistrar(new SimpleComponentRegistrar(bf));
|
||||
this.specificationContext.setProblemReporter(new FailFastProblemReporter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validSpec() {
|
||||
this.executor.execute(new ComponentScanSpec("example.scannable"), this.specificationContext);
|
||||
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.specificationContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,411 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,312 +0,0 @@
|
|||
/*
|
||||
* 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.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.createProxyIfPossible(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.createProxyIfPossible(descriptorFor(TestBean.class));
|
||||
|
||||
proxy.getName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyHashCodeAvoidsEagerInstantiation() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
TestBean proxy = (TestBean) pc.createProxyIfPossible(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.createProxyIfPossible(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy.equals(new Object()), is(false));
|
||||
assertThat(proxy.equals(proxy), is(true));
|
||||
|
||||
TestBean proxy2 = (TestBean) pc.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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 attemptToProxyFinalClassReturnsNonProxiedInstance() throws Exception {
|
||||
bf.registerBeanDefinition("finalBean", new RootBeanDefinition(FinalBean.class));
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
Object bean = pc.createProxyIfPossible(descriptorFor(FinalBean.class));
|
||||
assertThat(bean, instanceOf(FinalBean.class));
|
||||
assertThat(bean, not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* 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.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
public class FeatureConfigurationClassTests {
|
||||
|
||||
@Test(expected=FeatureMethodExecutionException.class)
|
||||
public void featureConfigurationClassesMustNotContainBeanAnnotatedMethods() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(FeatureConfigWithBeanAnnotatedMethod.class);
|
||||
ctx.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void featureMethodsMayAcceptResourceLoaderParameter() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.setDisplayName("enclosing app ctx");
|
||||
ctx.setEnvironment(new MockEnvironment().withProperty("foo", "bar"));
|
||||
ctx.register(FeatureMethodWithResourceLoaderParameter.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();
|
||||
}
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
class FeatureMethodWithResourceLoaderParameter {
|
||||
@Feature
|
||||
public FeatureSpecification feature(ResourceLoader rl,
|
||||
Environment e) {
|
||||
// prove that the injected Environment is that of the enclosing app context
|
||||
assertThat(e.getProperty("foo"), is("bar"));
|
||||
// prove that the injected ResourceLoader is actually the enclosing application context
|
||||
Object target = ((EarlyBeanReferenceProxy)rl).dereferenceTargetBean();
|
||||
assertThat(target, instanceOf(AnnotationConfigApplicationContext.class));
|
||||
assertThat(((AnnotationConfigApplicationContext)target).getDisplayName(), is("enclosing app ctx"));
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* 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"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,192 +0,0 @@
|
|||
/*
|
||||
* 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* 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 static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests proving that @Feature methods may reference the product of @Bean methods.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureMethodErrorTests {
|
||||
|
||||
@Test
|
||||
public void incorrectReturnType() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(FeatureConfig.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
fail("expected exception");
|
||||
} catch (FeatureMethodExecutionException ex) {
|
||||
assertThat(ex.getCause().getMessage(),
|
||||
equalTo("Return type for @Feature method FeatureConfig.f() must be " +
|
||||
"assignable to FeatureSpecification"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void voidReturnType() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(VoidFeatureConfig.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
fail("expected exception");
|
||||
} catch (FeatureMethodExecutionException ex) {
|
||||
assertThat(ex.getCause().getMessage(),
|
||||
equalTo("Return type for @Feature method VoidFeatureConfig.f() must be " +
|
||||
"assignable to FeatureSpecification"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void containsBeanMethod() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(FeatureConfigWithBeanMethod.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
fail("expected exception");
|
||||
} catch (FeatureMethodExecutionException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo("@FeatureConfiguration classes must not contain @Bean-annotated methods. " +
|
||||
"FeatureConfigWithBeanMethod.testBean() 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."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class FeatureConfig {
|
||||
@Feature
|
||||
public Object f() {
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class VoidFeatureConfig {
|
||||
@Feature
|
||||
public void f() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class FeatureConfigWithBeanMethod {
|
||||
@Feature
|
||||
public FeatureSpecification f() {
|
||||
return new StubSpecification();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* 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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* 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.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
|
||||
/**
|
||||
* Tests ensuring that @Feature methods can accept @Value-annoted
|
||||
* parameters, particularly String types. SPR-7974 revealed this
|
||||
* was failing due to attempting to proxy objects of type String,
|
||||
* which cannot be done.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class FeatureMethodValueInjectionTests {
|
||||
|
||||
@Test
|
||||
public void control() {
|
||||
System.setProperty("foo", "bar");
|
||||
System.setProperty("num", "2");
|
||||
Config config = new AnnotationConfigApplicationContext(Config.class).getBean(Config.class);
|
||||
System.clearProperty("foo");
|
||||
System.clearProperty("num");
|
||||
assertThat(config.foo, is("bar"));
|
||||
assertThat(config.num, is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spelValueInjection() {
|
||||
System.setProperty("foo", "bar");
|
||||
new AnnotationConfigApplicationContext(SpelValueInjectionFeatureConfig.class);
|
||||
System.clearProperty("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spelIntValueInjection() {
|
||||
System.setProperty("num", "5");
|
||||
new AnnotationConfigApplicationContext(SpelIntValueInjectionFeatureConfig.class);
|
||||
System.clearProperty("num");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringBeanInjection() {
|
||||
new AnnotationConfigApplicationContext(StringBeanConfig.class, StringBeanInjectionByTypeFeatureConfig.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void qualifiedStringBeanInjection() {
|
||||
new AnnotationConfigApplicationContext(StringBeanSubConfig.class, StringBeanInjectionByQualifierFeatureConfig.class);
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class SpelValueInjectionFeatureConfig {
|
||||
@Feature
|
||||
public StubSpecification feature(@Value("#{environment['foo']}") String foo) {
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class SpelIntValueInjectionFeatureConfig {
|
||||
@Feature
|
||||
public StubSpecification feature(@Value("#{environment['num']}") int num) {
|
||||
assertThat(num, is(5));
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class StringBeanConfig {
|
||||
@Bean
|
||||
public String stringBean() {
|
||||
return "sb";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class StringBeanSubConfig extends StringBeanConfig {
|
||||
@Bean
|
||||
public String stringBean2() {
|
||||
return "sb2";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class StringBeanInjectionByTypeFeatureConfig {
|
||||
@Feature
|
||||
public StubSpecification feature(String string) {
|
||||
assertThat(string, is("sb"));
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class StringBeanInjectionByQualifierFeatureConfig {
|
||||
@Feature
|
||||
public StubSpecification feature(@Qualifier("stringBean2") String string) {
|
||||
assertThat(string, is("sb2"));
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
@Value("#{environment['foo']}") String foo;
|
||||
@Value("#{environment['num']}") int num;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* 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.SpecificationContext;
|
||||
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, SpecificationContext specificationContext) {
|
||||
Assert.state(executeMethodWasCalled == false);
|
||||
executeMethodWasCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* 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 {
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* 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.ProblemCollector;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.context.config.SpecificationContext;
|
||||
|
||||
public class StubSpecification extends AbstractFeatureSpecification {
|
||||
|
||||
public StubSpecification() {
|
||||
this(StubSpecificationExecutor.class);
|
||||
}
|
||||
|
||||
public StubSpecification(Class<? extends FeatureSpecificationExecutor> excecutorType) {
|
||||
super(excecutorType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(ProblemCollector problems) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class StubSpecificationExecutor implements FeatureSpecificationExecutor {
|
||||
|
||||
public void execute(FeatureSpecification spec, SpecificationContext specificationContext) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
import org.springframework.transaction.TxNamespaceHandlerTests;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionNamespaceHandlerTests;
|
||||
import org.springframework.transaction.annotation.TxAnnotationDrivenFeatureTests;
|
||||
import org.springframework.transaction.config.AnnotationDrivenTests;
|
||||
import org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParserTests;
|
||||
import org.springframework.web.servlet.config.MvcAnnotationDrivenFeatureTests;
|
||||
import org.springframework.web.servlet.config.MvcDefaultServletHandlerTests;
|
||||
import org.springframework.web.servlet.config.MvcNamespaceTests;
|
||||
import org.springframework.web.servlet.config.MvcResourcesTests;
|
||||
import org.springframework.web.servlet.config.MvcViewControllersTests;
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({
|
||||
EarlyBeanReferenceProxyCreatorTests.class,
|
||||
SimpleFeatureMethodProcessingTests.class,
|
||||
BeanFactoryAwareFeatureConfigurationTests.class,
|
||||
FeatureMethodBeanReferenceTests.class,
|
||||
FeatureMethodQualifiedBeanReferenceTests.class,
|
||||
FeatureMethodErrorTests.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 {
|
||||
|
||||
}
|
||||
|
|
@ -16,7 +16,15 @@
|
|||
|
||||
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.context.config.AbstractSpecificationBeanDefinitionParser;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
|
@ -40,7 +48,7 @@ import org.w3c.dom.Element;
|
|||
* @since 2.0
|
||||
* @see TxAnnotationDriven
|
||||
*/
|
||||
class AnnotationDrivenBeanDefinitionParser extends AbstractSpecificationBeanDefinitionParser {
|
||||
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed transaction advisor (mode="proxy").
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -31,7 +31,7 @@ import org.springframework.util.ClassUtils;
|
|||
* @author Christian Dupuis
|
||||
* @since 2.5
|
||||
*/
|
||||
class JtaTransactionManagerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
public class JtaTransactionManagerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String WEBLOGIC_JTA_TRANSACTION_MANAGER_CLASS_NAME =
|
||||
"org.springframework.transaction.jta.WebLogicJtaTransactionManager";
|
||||
|
|
@ -74,7 +74,7 @@ class JtaTransactionManagerBeanDefinitionParser extends AbstractSingleBeanDefini
|
|||
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
|
||||
return TxAnnotationDriven.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME;
|
||||
return TxNamespaceHandler.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,8 +48,6 @@ import org.springframework.util.xml.DomUtils;
|
|||
*/
|
||||
class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
|
||||
|
||||
private static final String METHOD_ELEMENT = "method";
|
||||
|
||||
private static final String METHOD_NAME_ATTRIBUTE = "name";
|
||||
|
|
@ -76,7 +74,7 @@ class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
|||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
builder.addPropertyReference("transactionManager", element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE));
|
||||
builder.addPropertyReference("transactionManager", TxNamespaceHandler.getTransactionManagerName(element));
|
||||
|
||||
List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);
|
||||
if (txAttributes.size() > 1) {
|
||||
|
|
|
|||
|
|
@ -1,198 +0,0 @@
|
|||
/*
|
||||
* 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.ProblemCollector;
|
||||
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(ProblemCollector problems) {
|
||||
if (this.mode instanceof String) {
|
||||
if (!ObjectUtils.containsConstant(AdviceMode.values(), (String)this.mode)) {
|
||||
problems.error("no such mode name: " + this.mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* 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.SpecificationContext;
|
||||
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, SpecificationContext specificationContext) {
|
||||
BeanDefinitionRegistry registry = specificationContext.getRegistry();
|
||||
ComponentRegistrar registrar = specificationContext.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,17 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
|||
*/
|
||||
public class TxNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
|
||||
|
||||
static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
|
||||
|
||||
|
||||
static String getTransactionManagerName(Element element) {
|
||||
return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?
|
||||
element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
|
||||
}
|
||||
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
|
||||
|
|
|
|||
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.CannotLoadBeanClassException;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Feature;
|
||||
import org.springframework.context.annotation.FeatureConfiguration;
|
||||
import org.springframework.context.config.AdviceMode;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.CallCountingTransactionManager;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionNamespaceHandlerTests.TransactionalTestBean;
|
||||
import org.springframework.transaction.config.TxAnnotationDriven;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link TxAnnotationDriven} support within @Configuration
|
||||
* classes. Adapted from original tx: namespace tests at
|
||||
* {@link AnnotationTransactionNamespaceHandlerTests}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class TxAnnotationDrivenFeatureTests {
|
||||
@Test
|
||||
public void transactionProxyIsCreated() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(TxFeature.class, TxManagerConfig.class);
|
||||
ctx.refresh();
|
||||
TransactionalTestBean bean = ctx.getBean(TransactionalTestBean.class);
|
||||
assertThat("testBean is not a proxy", AopUtils.isAopProxy(bean), is(true));
|
||||
Map<?,?> services = ctx.getBeansWithAnnotation(Service.class);
|
||||
assertThat("Stereotype annotation not visible", services.containsKey("testBean"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void txManagerIsResolvedOnInvocationOfTransactionalMethod() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(TxFeature.class, TxManagerConfig.class);
|
||||
ctx.refresh();
|
||||
TransactionalTestBean bean = ctx.getBean(TransactionalTestBean.class);
|
||||
|
||||
// invoke a transactional method, causing the PlatformTransactionManager bean to be resolved.
|
||||
bean.findAllFoos();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void txManagerIsResolvedCorrectlyWhenMultipleManagersArePresent() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(TxFeature.class, MultiTxManagerConfig.class);
|
||||
ctx.refresh();
|
||||
TransactionalTestBean bean = ctx.getBean(TransactionalTestBean.class);
|
||||
|
||||
// invoke a transactional method, causing the PlatformTransactionManager bean to be resolved.
|
||||
bean.findAllFoos();
|
||||
}
|
||||
|
||||
/**
|
||||
* A cheap test just to prove that in ASPECTJ mode, the AnnotationTransactionAspect does indeed
|
||||
* get loaded -- or in this case, attempted to be loaded at which point the test fails.
|
||||
*/
|
||||
@Test
|
||||
public void proxyTypeAspectJCausesRegistrationOfAnnotationTransactionAspect() {
|
||||
try {
|
||||
new AnnotationConfigApplicationContext(TxWithAspectJFeature.class, TxManagerConfig.class);
|
||||
fail("should have thrown CNFE when trying to load AnnotationTransactionAspect. " +
|
||||
"Do you actually have org.springframework.aspects on the classpath?");
|
||||
} catch (CannotLoadBeanClassException ex) {
|
||||
ClassNotFoundException cause = (ClassNotFoundException) ex.getCause();
|
||||
assertThat(cause.getMessage(), equalTo("org.springframework.transaction.aspectj.AnnotationTransactionAspect"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
class TxFeature {
|
||||
|
||||
@Feature
|
||||
public TxAnnotationDriven tx(TxManagerConfig txManagerConfig) {
|
||||
return new TxAnnotationDriven(txManagerConfig.txManager());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
class TxWithAspectJFeature {
|
||||
|
||||
@Feature
|
||||
public TxAnnotationDriven tx(PlatformTransactionManager txManager) {
|
||||
return new TxAnnotationDriven(txManager).mode(AdviceMode.ASPECTJ);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
class TxManagerConfig {
|
||||
|
||||
@Bean
|
||||
public TransactionalTestBean testBean() {
|
||||
return new TransactionalTestBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager txManager() {
|
||||
return new CallCountingTransactionManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
class MultiTxManagerConfig extends TxManagerConfig {
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager txManager2() {
|
||||
return new CallCountingTransactionManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 org.springframework.web.servlet.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.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link BeanDefinitonParser}s that register an HttpRequestHandler.
|
||||
*
|
||||
* @author Jeremy Grelle
|
||||
* @since 3.0.4
|
||||
*/
|
||||
abstract class AbstractHttpRequestHandlerBeanDefinitionParser implements BeanDefinitionParser{
|
||||
|
||||
private static final String HANDLER_ADAPTER_BEAN_NAME = "org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter";
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
registerHandlerAdapterIfNecessary(parserContext, source);
|
||||
doParse(element, parserContext);
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract void doParse(Element element, ParserContext parserContext);
|
||||
|
||||
private void registerHandlerAdapterIfNecessary(ParserContext parserContext, Object source) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(HANDLER_ADAPTER_BEAN_NAME)) {
|
||||
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(HttpRequestHandlerAdapter.class);
|
||||
handlerAdapterDef.setSource(source);
|
||||
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parserContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -16,65 +16,265 @@
|
|||
|
||||
package org.springframework.web.servlet.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
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.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.config.AbstractSpecificationBeanDefinitionParser;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodMapping;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebArgumentResolverAdapter;
|
||||
import org.w3c.dom.Element;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} that parses the {@code annotation-driven} element
|
||||
* to configure a Spring MVC web application.
|
||||
* {@link BeanDefinitionParser} that parses the {@code annotation-driven} element to configure a Spring MVC web
|
||||
* application.
|
||||
*
|
||||
* <p>Responsible for:
|
||||
* <ol>
|
||||
* <li>Registering a DefaultAnnotationHandlerMapping bean for mapping HTTP Servlet Requests to @Controller methods
|
||||
* using @RequestMapping annotations.
|
||||
* <li>Registering a AnnotationMethodHandlerAdapter bean for invoking annotated @Controller methods.
|
||||
* Will configure the HandlerAdapter's <code>webBindingInitializer</code> property for centrally configuring
|
||||
* {@code @Controller} {@code DataBinder} instances:
|
||||
* <ul>
|
||||
* <li>Configures the conversionService if specified, otherwise defaults to a fresh {@link ConversionService} instance
|
||||
* created by the default {@link FormattingConversionServiceFactoryBean}.
|
||||
* <li>Configures the validator if specified, otherwise defaults to a fresh {@link Validator} instance created by the
|
||||
* default {@link LocalValidatorFactoryBean} <em>if the JSR-303 API is present on the classpath</em>.
|
||||
* <li>Configures standard {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverters},
|
||||
* including the {@link Jaxb2RootElementHttpMessageConverter} <em>if JAXB2 is present on the classpath</em>, and
|
||||
* the {@link MappingJacksonHttpMessageConverter} <em>if Jackson is present on the classpath</em>.
|
||||
* </ul>
|
||||
* </ol>
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see MvcAnnotationDriven
|
||||
* @see MvcAnnotationDrivenExecutor
|
||||
*/
|
||||
class AnnotationDrivenBeanDefinitionParser extends AbstractSpecificationBeanDefinitionParser {
|
||||
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Parses the {@code <mvc:annotation-driven/>} tag.
|
||||
*/
|
||||
@Override
|
||||
protected FeatureSpecification doParse(Element element, ParserContext parserContext) {
|
||||
MvcAnnotationDriven spec = new MvcAnnotationDriven();
|
||||
private static final boolean jsr303Present = ClassUtils.isPresent(
|
||||
"javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
private static final boolean jaxb2Present =
|
||||
ClassUtils.isPresent("javax.xml.bind.Binder", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
private static final boolean jacksonPresent =
|
||||
ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
private static boolean romePresent =
|
||||
ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
|
||||
parserContext.pushContainingComponent(compDefinition);
|
||||
|
||||
RootBeanDefinition methodMappingDef = new RootBeanDefinition(RequestMappingHandlerMethodMapping.class);
|
||||
methodMappingDef.setSource(source);
|
||||
methodMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
methodMappingDef.getPropertyValues().add("order", 0);
|
||||
String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(methodMappingDef);
|
||||
|
||||
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
|
||||
RuntimeBeanReference validator = getValidator(element, source, parserContext);
|
||||
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element, source, parserContext);
|
||||
|
||||
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
|
||||
bindingDef.setSource(source);
|
||||
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
bindingDef.getPropertyValues().add("conversionService", conversionService);
|
||||
bindingDef.getPropertyValues().add("validator", validator);
|
||||
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
|
||||
|
||||
ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
|
||||
ManagedList<?> argumentResolvers = getArgumentResolvers(element, source, parserContext);
|
||||
|
||||
RootBeanDefinition methodAdapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class);
|
||||
methodAdapterDef.setSource(source);
|
||||
methodAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
methodAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
|
||||
methodAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
|
||||
if (argumentResolvers != null) {
|
||||
methodAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
|
||||
}
|
||||
String methodAdapterName = parserContext.getReaderContext().registerWithGeneratedName(methodAdapterDef);
|
||||
|
||||
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
|
||||
csInterceptorDef.setSource(source);
|
||||
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
|
||||
RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
|
||||
mappedCsInterceptorDef.setSource(source);
|
||||
mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
|
||||
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
|
||||
String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef);
|
||||
|
||||
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(RequestMappingHandlerMethodExceptionResolver.class);
|
||||
methodExceptionResolver.setSource(source);
|
||||
methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
|
||||
methodExceptionResolver.getPropertyValues().add("order", 0);
|
||||
String methodExceptionResolverName =
|
||||
parserContext.getReaderContext().registerWithGeneratedName(methodExceptionResolver);
|
||||
|
||||
RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
|
||||
responseStatusExceptionResolver.setSource(source);
|
||||
responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
responseStatusExceptionResolver.getPropertyValues().add("order", 1);
|
||||
String responseStatusExceptionResolverName =
|
||||
parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver);
|
||||
|
||||
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
|
||||
defaultExceptionResolver.setSource(source);
|
||||
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
defaultExceptionResolver.getPropertyValues().add("order", 2);
|
||||
String defaultExceptionResolverName =
|
||||
parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver);
|
||||
|
||||
parserContext.registerComponent(new BeanComponentDefinition(methodMappingDef, methodMappingName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(methodAdapterDef, methodAdapterName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private RuntimeBeanReference getConversionService(Element element, Object source, ParserContext parserContext) {
|
||||
RuntimeBeanReference conversionServiceRef;
|
||||
if (element.hasAttribute("conversion-service")) {
|
||||
String conversionService = element.getAttribute("conversion-service");
|
||||
spec.conversionService(conversionService);
|
||||
conversionServiceRef = new RuntimeBeanReference(element.getAttribute("conversion-service"));
|
||||
}
|
||||
else {
|
||||
RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
|
||||
conversionDef.setSource(source);
|
||||
conversionDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String conversionName = parserContext.getReaderContext().registerWithGeneratedName(conversionDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(conversionDef, conversionName));
|
||||
conversionServiceRef = new RuntimeBeanReference(conversionName);
|
||||
}
|
||||
return conversionServiceRef;
|
||||
}
|
||||
|
||||
private RuntimeBeanReference getValidator(Element element, Object source, ParserContext parserContext) {
|
||||
if (element.hasAttribute("validator")) {
|
||||
spec.validator(element.getAttribute("validator"));
|
||||
return new RuntimeBeanReference(element.getAttribute("validator"));
|
||||
}
|
||||
else if (jsr303Present) {
|
||||
RootBeanDefinition validatorDef = new RootBeanDefinition(LocalValidatorFactoryBean.class);
|
||||
validatorDef.setSource(source);
|
||||
validatorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String validatorName = parserContext.getReaderContext().registerWithGeneratedName(validatorDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(validatorDef, validatorName));
|
||||
return new RuntimeBeanReference(validatorName);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private RuntimeBeanReference getMessageCodesResolver(Element element, Object source, ParserContext parserContext) {
|
||||
if (element.hasAttribute("message-codes-resolver")) {
|
||||
spec.messageCodesResolver(element.getAttribute("message-codes-resolver"));
|
||||
}
|
||||
Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
|
||||
if (convertersElement != null) {
|
||||
if (convertersElement.hasAttribute("register-defaults")) {
|
||||
spec.shouldRegisterDefaultMessageConverters(Boolean.valueOf(convertersElement
|
||||
.getAttribute("register-defaults")));
|
||||
}
|
||||
spec.messageConverters(extractBeanSubElements(convertersElement, parserContext));
|
||||
return new RuntimeBeanReference(element.getAttribute("message-codes-resolver"));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ManagedList<?> getArgumentResolvers(Element element, Object source, ParserContext parserContext) {
|
||||
Element resolversElement = DomUtils.getChildElementByTagName(element, "argument-resolvers");
|
||||
if (resolversElement != null) {
|
||||
ManagedList<BeanDefinitionHolder> beanDefs = extractBeanSubElements(resolversElement, parserContext);
|
||||
spec.argumentResolvers(wrapWebArgumentResolverBeanDefs(beanDefs));
|
||||
ManagedList<BeanDefinitionHolder> argumentResolvers = extractBeanSubElements(resolversElement, parserContext);
|
||||
return wrapWebArgumentResolverBeanDefs(argumentResolvers);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ManagedList<?> getMessageConverters(Element element, Object source, ParserContext parserContext) {
|
||||
Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
|
||||
ManagedList<? super Object> messageConverters = new ManagedList<Object>();
|
||||
if (convertersElement != null) {
|
||||
messageConverters.setSource(source);
|
||||
for (Element converter : DomUtils.getChildElementsByTagName(convertersElement, "bean")) {
|
||||
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(converter);
|
||||
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef);
|
||||
messageConverters.add(beanDef);
|
||||
}
|
||||
}
|
||||
|
||||
return spec;
|
||||
if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) {
|
||||
messageConverters.setSource(source);
|
||||
messageConverters.add(createConverterBeanDefinition(ByteArrayHttpMessageConverter.class, source));
|
||||
|
||||
RootBeanDefinition stringConverterDef = createConverterBeanDefinition(StringHttpMessageConverter.class,
|
||||
source);
|
||||
stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
|
||||
messageConverters.add(stringConverterDef);
|
||||
|
||||
messageConverters.add(createConverterBeanDefinition(ResourceHttpMessageConverter.class, source));
|
||||
messageConverters.add(createConverterBeanDefinition(SourceHttpMessageConverter.class, source));
|
||||
messageConverters.add(createConverterBeanDefinition(XmlAwareFormHttpMessageConverter.class, source));
|
||||
if (jaxb2Present) {
|
||||
messageConverters
|
||||
.add(createConverterBeanDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
|
||||
}
|
||||
if (jacksonPresent) {
|
||||
messageConverters.add(createConverterBeanDefinition(MappingJacksonHttpMessageConverter.class, source));
|
||||
}
|
||||
if (romePresent) {
|
||||
messageConverters.add(createConverterBeanDefinition(AtomFeedHttpMessageConverter.class, source));
|
||||
messageConverters.add(createConverterBeanDefinition(RssChannelHttpMessageConverter.class, source));
|
||||
}
|
||||
}
|
||||
return messageConverters;
|
||||
}
|
||||
|
||||
private RootBeanDefinition createConverterBeanDefinition(Class<? extends HttpMessageConverter> converterClass,
|
||||
Object source) {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(converterClass);
|
||||
beanDefinition.setSource(source);
|
||||
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
private ManagedList<BeanDefinitionHolder> extractBeanSubElements(Element parentElement, ParserContext parserContext) {
|
||||
|
|
@ -99,8 +299,7 @@ class AnnotationDrivenBeanDefinitionParser extends AbstractSpecificationBeanDefi
|
|||
RootBeanDefinition adapter = new RootBeanDefinition(ServletWebArgumentResolverAdapter.class);
|
||||
adapter.getConstructorArgumentValues().addIndexedArgumentValue(0, beanDef);
|
||||
result.add(new BeanDefinitionHolder(adapter, beanDef.getBeanName() + "Adapter"));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
result.add(beanDef);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,15 +16,20 @@
|
|||
|
||||
package org.springframework.web.servlet.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.config.AbstractSpecificationBeanDefinitionParser;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} that parses a {@code default-servlet-handler} element to
|
||||
|
|
@ -32,20 +37,37 @@ import org.w3c.dom.Element;
|
|||
* {@link SimpleUrlHandlerMapping} for mapping resource requests, and a
|
||||
* {@link HttpRequestHandlerAdapter} if necessary.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Chris Beams
|
||||
* @author Jeremy Grelle
|
||||
* @since 3.0.4
|
||||
*/
|
||||
class DefaultServletHandlerBeanDefinitionParser extends AbstractSpecificationBeanDefinitionParser {
|
||||
class DefaultServletHandlerBeanDefinitionParser extends AbstractHttpRequestHandlerBeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Parses the {@code <mvc:default-servlet-handler/>} tag.
|
||||
*/
|
||||
public FeatureSpecification doParse(Element element, ParserContext parserContext) {
|
||||
String defaultServletHandler = element.getAttribute("default-servlet-handler");
|
||||
return StringUtils.hasText(defaultServletHandler) ?
|
||||
new MvcDefaultServletHandler(defaultServletHandler) :
|
||||
new MvcDefaultServletHandler();
|
||||
@Override
|
||||
public void doParse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
String defaultServletName = element.getAttribute("default-servlet-name");
|
||||
RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
|
||||
defaultServletHandlerDef.setSource(source);
|
||||
defaultServletHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
if (StringUtils.hasText(defaultServletName)) {
|
||||
defaultServletHandlerDef.getPropertyValues().add("defaultServletName", defaultServletName);
|
||||
}
|
||||
String defaultServletHandlerName = parserContext.getReaderContext().generateBeanName(defaultServletHandlerDef);
|
||||
parserContext.getRegistry().registerBeanDefinition(defaultServletHandlerName, defaultServletHandlerDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(defaultServletHandlerDef, defaultServletHandlerName));
|
||||
|
||||
Map<String, String> urlMap = new ManagedMap<String, String>();
|
||||
urlMap.put("/**", defaultServletHandlerName);
|
||||
|
||||
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
|
||||
handlerMappingDef.setSource(source);
|
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
|
||||
|
||||
String handlerMappingBeanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
|
||||
parserContext.getRegistry().registerBeanDefinition(handlerMappingBeanName, handlerMappingDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, handlerMappingBeanName));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,54 +18,59 @@ package org.springframework.web.servlet.config;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
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.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.config.AbstractSpecificationBeanDefinitionParser;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses
|
||||
* a {@code interceptors} element to register set of {@link MappedInterceptor}
|
||||
* definitions.
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a {@code interceptors} element to register
|
||||
* a set of {@link MappedInterceptor} definitions.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class InterceptorsBeanDefinitionParser extends AbstractSpecificationBeanDefinitionParser {
|
||||
class InterceptorsBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Parses the {@code <mvc:interceptors/>} tag.
|
||||
*/
|
||||
public FeatureSpecification doParse(Element element, ParserContext parserContext) {
|
||||
MvcInterceptors mvcInterceptors = new MvcInterceptors();
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
|
||||
parserContext.pushContainingComponent(compDefinition);
|
||||
|
||||
List<Element> interceptors = DomUtils.getChildElementsByTagName(element, new String[] { "bean", "interceptor" });
|
||||
for (Element interceptor : interceptors) {
|
||||
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
|
||||
mappedInterceptorDef.setSource(parserContext.extractSource(interceptor));
|
||||
mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String[] pathPatterns;
|
||||
BeanDefinitionHolder interceptorDef;
|
||||
if ("interceptor".equals(interceptor.getLocalName())) {
|
||||
List<Element> paths = DomUtils.getChildElementsByTagName(interceptor, "mapping");
|
||||
String[] pathPatterns = new String[paths.size()];
|
||||
pathPatterns = new String[paths.size()];
|
||||
for (int i = 0; i < paths.size(); i++) {
|
||||
pathPatterns[i] = paths.get(i).getAttribute("path");
|
||||
}
|
||||
Element beanElement = DomUtils.getChildElementByTagName(interceptor, "bean");
|
||||
mvcInterceptors.interceptor(pathPatterns, parseBeanElement(parserContext, beanElement));
|
||||
Element interceptorBean = DomUtils.getChildElementByTagName(interceptor, "bean");
|
||||
interceptorDef = parserContext.getDelegate().parseBeanDefinitionElement(interceptorBean);
|
||||
interceptorDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(interceptorBean, interceptorDef);
|
||||
} else {
|
||||
mvcInterceptors.interceptor(null, parseBeanElement(parserContext, interceptor));
|
||||
pathPatterns = null;
|
||||
interceptorDef = parserContext.getDelegate().parseBeanDefinitionElement(interceptor);
|
||||
interceptorDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(interceptor, interceptorDef);
|
||||
}
|
||||
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, pathPatterns);
|
||||
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, interceptorDef);
|
||||
String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedInterceptorDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));
|
||||
}
|
||||
|
||||
return mvcInterceptors;
|
||||
}
|
||||
|
||||
private BeanDefinitionHolder parseBeanElement(ParserContext parserContext, Element interceptor) {
|
||||
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(interceptor);
|
||||
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(interceptor, beanDef);
|
||||
return beanDef;
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,259 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.parsing.ProblemCollector;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebArgumentResolverAdapter;
|
||||
|
||||
/**
|
||||
* Specifies the Spring MVC "annotation-driven" container feature. The
|
||||
* feature provides the following fine-grained configuration:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code DefaultAnnotationHandlerMapping} bean for mapping HTTP Servlet Requests
|
||||
* to {@code @Controller} methods using {@code @RequestMapping} annotations.
|
||||
* <li>{@code AnnotationMethodHandlerAdapter} bean for invoking annotated
|
||||
* {@code @Controller} methods.
|
||||
* <li>{@code HandlerExceptionResolver} beans for invoking {@code @ExceptionHandler}
|
||||
* controller methods and for mapping Spring exception to HTTP status codes.
|
||||
* </ul>
|
||||
*
|
||||
* <p>The {@code HandlerAdapter} is further configured with the following, which apply
|
||||
* globally (across controllers invoked though the {@code AnnotationMethodHandlerAdapter}):
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link ConversionService} - a custom instance can be provided via
|
||||
* {@link #conversionService(ConversionService)}. Otherwise it defaults to a fresh
|
||||
* {@link ConversionService} instance created by the default
|
||||
* {@link FormattingConversionServiceFactoryBean}.
|
||||
* <li>{@link Validator} - a custom instance can be provided via
|
||||
* {@link #validator(Validator)}. Otherwise it defaults to a fresh {@code Validator}
|
||||
* instance created by the default {@link LocalValidatorFactoryBean} <em>assuming
|
||||
* JSR-303 API is present on the classpath</em>.
|
||||
* <li>{@code HttpMessageConverter} beans including the {@link
|
||||
* Jaxb2RootElementHttpMessageConverter} <em>assuming JAXB2 is present on the
|
||||
* classpath</em>, the {@link MappingJacksonHttpMessageConverter} <em>assuming Jackson
|
||||
* is present on the classpath</em>, and the {@link AtomFeedHttpMessageConverter} and the
|
||||
* {@link RssChannelHttpMessageConverter} converters <em>assuming Rome is present on
|
||||
* the classpath</em>.
|
||||
* <li>Optionally, custom {@code WebArgumentResolver} beans to use for resolving
|
||||
* custom arguments to handler methods. These are typically implemented to detect
|
||||
* special parameter types, resolving well-known argument values for them.
|
||||
* </ul>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class MvcAnnotationDriven extends AbstractFeatureSpecification {
|
||||
|
||||
private static final Class<? extends FeatureSpecificationExecutor> EXECUTOR_TYPE = MvcAnnotationDrivenExecutor.class;
|
||||
|
||||
private Object conversionService;
|
||||
|
||||
private Object validator;
|
||||
|
||||
private Object messageCodesResolver;
|
||||
|
||||
private boolean shouldRegisterDefaultMessageConverters = true;
|
||||
|
||||
private ManagedList<? super Object> messageConverters = new ManagedList<Object>();
|
||||
|
||||
private ManagedList<? super Object> argumentResolvers = new ManagedList<Object>();
|
||||
|
||||
/**
|
||||
* Creates an MvcAnnotationDriven specification.
|
||||
*/
|
||||
public MvcAnnotationDriven() {
|
||||
super(EXECUTOR_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> The ConversionService bean instance to use for type conversion during
|
||||
* field binding. This is not required input. It only needs to be provided
|
||||
* explicitly if custom converters or formatters need to be configured.
|
||||
*
|
||||
* <p> If not provided, a default FormattingConversionService is registered
|
||||
* that contains converters to/from standard JDK types. In addition, full
|
||||
* support for date/time formatting will be installed if the Joda Time
|
||||
* library is present on the classpath.
|
||||
*
|
||||
* @param conversionService the ConversionService instance to use
|
||||
*/
|
||||
public MvcAnnotationDriven conversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> The ConversionService to use for type conversion during field binding.
|
||||
* This is an alternative to {@link #conversionService(ConversionService)}
|
||||
* allowing you to provide a bean name rather than a bean instance.
|
||||
*
|
||||
* @param conversionService the ConversionService bean name
|
||||
*/
|
||||
public MvcAnnotationDriven conversionService(String conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
return this;
|
||||
}
|
||||
|
||||
Object conversionService() {
|
||||
return this.conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HttpMessageConverter types to use for converting @RequestBody method
|
||||
* parameters and @ResponseBody method return values. HttpMessageConverter
|
||||
* registrations provided here will take precedence over HttpMessageConverter
|
||||
* types registered by default.
|
||||
* Also see {@link #shouldRegisterDefaultMessageConverters(boolean)} if
|
||||
* default registrations are to be turned off altogether.
|
||||
*
|
||||
* @param converters the message converters
|
||||
*/
|
||||
public MvcAnnotationDriven messageConverters(HttpMessageConverter<?>... converters) {
|
||||
for (HttpMessageConverter<?> converter : converters) {
|
||||
this.messageConverters.add(converter);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void messageConverters(ManagedList<BeanDefinitionHolder> converterBeanDefinitions) {
|
||||
this.messageConverters.addAll(converterBeanDefinitions);
|
||||
}
|
||||
|
||||
ManagedList<?> messageConverters() {
|
||||
return this.messageConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not default HttpMessageConverter registrations should
|
||||
* be added in addition to the ones provided via
|
||||
* {@link #messageConverters(HttpMessageConverter...)}
|
||||
*
|
||||
* @param shouldRegister true will result in registration of defaults.
|
||||
*/
|
||||
public MvcAnnotationDriven shouldRegisterDefaultMessageConverters(boolean shouldRegister) {
|
||||
this.shouldRegisterDefaultMessageConverters = shouldRegister;
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean shouldRegisterDefaultMessageConverters() {
|
||||
return this.shouldRegisterDefaultMessageConverters;
|
||||
}
|
||||
|
||||
public MvcAnnotationDriven argumentResolvers(HandlerMethodArgumentResolver... resolvers) {
|
||||
for (HandlerMethodArgumentResolver resolver : resolvers) {
|
||||
this.argumentResolvers.add(resolver);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MvcAnnotationDriven argumentResolvers(WebArgumentResolver... resolvers) {
|
||||
for (WebArgumentResolver resolver : resolvers) {
|
||||
this.argumentResolvers.add(new ServletWebArgumentResolverAdapter(resolver));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void argumentResolvers(ManagedList<BeanDefinitionHolder> resolverBeanDefinitions) {
|
||||
this.argumentResolvers.addAll(resolverBeanDefinitions);
|
||||
}
|
||||
|
||||
ManagedList<?> argumentResolvers() {
|
||||
return this.argumentResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Validator bean instance to use to validate Controller model objects.
|
||||
* This is not required input. It only needs to be specified explicitly if
|
||||
* a custom Validator needs to be configured.
|
||||
*
|
||||
* <p> If not specified, JSR-303 validation will be installed if a JSR-303
|
||||
* provider is present on the classpath.
|
||||
*
|
||||
* @param validator the Validator bean instance
|
||||
*/
|
||||
public MvcAnnotationDriven validator(Validator validator) {
|
||||
this.validator = validator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Validator bean instance to use to validate Controller model objects.
|
||||
* This is an alternative to {@link #validator(Validator)} allowing you to
|
||||
* provide a bean name rather than a bean instance.
|
||||
*
|
||||
* @param validator the Validator bean name
|
||||
*/
|
||||
public MvcAnnotationDriven validator(String validator) {
|
||||
this.validator = validator;
|
||||
return this;
|
||||
}
|
||||
|
||||
Object validator() {
|
||||
return this.validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* The MessageCodesResolver to use to build message codes from data binding
|
||||
* and validation error codes. This is not required input. If not specified
|
||||
* the DefaultMessageCodesResolver is used.
|
||||
*
|
||||
* @param messageCodesResolver the MessageCodesResolver bean instance
|
||||
*/
|
||||
public MvcAnnotationDriven messageCodesResolver(MessageCodesResolver messageCodesResolver) {
|
||||
this.messageCodesResolver = messageCodesResolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The MessageCodesResolver to use to build message codes from data binding
|
||||
* and validation error codes. This is an alternative to
|
||||
* {@link #messageCodesResolver(MessageCodesResolver)} allowing you to provide
|
||||
* a bean name rather than a bean instance.
|
||||
*
|
||||
* @param messageCodesResolver the MessageCodesResolver bean name
|
||||
*/
|
||||
public MvcAnnotationDriven messageCodesResolver(String messageCodesResolver) {
|
||||
this.messageCodesResolver = messageCodesResolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
Object messageCodesResolver() {
|
||||
return this.messageCodesResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(ProblemCollector problems) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,236 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
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.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.config.AbstractSpecificationExecutor;
|
||||
import org.springframework.context.config.SpecificationContext;
|
||||
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodMapping;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
|
||||
/**
|
||||
* Executes {@link MvcAnnotationDriven} specifications, creating and registering
|
||||
* bean definitions as appropriate based on the configuration within.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
* @see MvcAnnotationDriven
|
||||
*/
|
||||
final class MvcAnnotationDrivenExecutor extends AbstractSpecificationExecutor<MvcAnnotationDriven> {
|
||||
|
||||
private static final boolean jsr303Present = ClassUtils.isPresent("javax.validation.Validator",
|
||||
AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
private static final boolean jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder",
|
||||
AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
private static final boolean jacksonPresent = ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper",
|
||||
AnnotationDrivenBeanDefinitionParser.class.getClassLoader())
|
||||
&& ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator",
|
||||
AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
private static boolean romePresent = ClassUtils.isPresent("com.sun.syndication.feed.WireFeed",
|
||||
AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
@Override
|
||||
public void doExecute(MvcAnnotationDriven spec, SpecificationContext specContext) {
|
||||
ComponentRegistrar registrar = specContext.getRegistrar();
|
||||
Object source = spec.source();
|
||||
|
||||
RootBeanDefinition methodMappingDef = new RootBeanDefinition(RequestMappingHandlerMethodMapping.class);
|
||||
methodMappingDef.setSource(source);
|
||||
methodMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
methodMappingDef.getPropertyValues().add("order", 0);
|
||||
String methodMappingName = registrar.registerWithGeneratedName(methodMappingDef);
|
||||
|
||||
Object conversionService = getConversionService(spec, registrar);
|
||||
Object validator = getValidator(spec, registrar);
|
||||
Object messageCodesResolver = getMessageCodesResolver(spec, registrar);
|
||||
|
||||
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
|
||||
bindingDef.setSource(source);
|
||||
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
bindingDef.getPropertyValues().add("conversionService", conversionService);
|
||||
bindingDef.getPropertyValues().add("validator", validator);
|
||||
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
|
||||
|
||||
ManagedList<? super Object> messageConverters = getMessageConverters(spec, registrar);
|
||||
|
||||
RootBeanDefinition methodAdapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class);
|
||||
methodAdapterDef.setSource(source);
|
||||
methodAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
methodAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
|
||||
methodAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
|
||||
if (!spec.argumentResolvers().isEmpty()) {
|
||||
methodAdapterDef.getPropertyValues().add("customArgumentResolvers", spec.argumentResolvers());
|
||||
}
|
||||
String methodAdapterName = registrar.registerWithGeneratedName(methodAdapterDef);
|
||||
|
||||
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
|
||||
csInterceptorDef.setSource(source);
|
||||
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
|
||||
RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
|
||||
mappedCsInterceptorDef.setSource(source);
|
||||
mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
|
||||
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
|
||||
String mappedInterceptorName = registrar.registerWithGeneratedName(mappedCsInterceptorDef);
|
||||
|
||||
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(
|
||||
ExceptionHandlerExceptionResolver.class);
|
||||
methodExceptionResolver.setSource(source);
|
||||
methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
|
||||
methodExceptionResolver.getPropertyValues().add("order", 0);
|
||||
String methodExceptionResolverName = registrar.registerWithGeneratedName(methodExceptionResolver);
|
||||
|
||||
RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(
|
||||
ResponseStatusExceptionResolver.class);
|
||||
responseStatusExceptionResolver.setSource(source);
|
||||
responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
responseStatusExceptionResolver.getPropertyValues().add("order", 1);
|
||||
String responseStatusExceptionResolverName = registrar
|
||||
.registerWithGeneratedName(responseStatusExceptionResolver);
|
||||
|
||||
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
|
||||
defaultExceptionResolver.setSource(source);
|
||||
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
defaultExceptionResolver.getPropertyValues().add("order", 2);
|
||||
String defaultExceptionResolverName = registrar.registerWithGeneratedName(defaultExceptionResolver);
|
||||
|
||||
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(spec.sourceName(), source);
|
||||
compDefinition.addNestedComponent(new BeanComponentDefinition(methodMappingDef, methodMappingName));
|
||||
compDefinition.addNestedComponent(new BeanComponentDefinition(methodAdapterDef, methodAdapterName));
|
||||
compDefinition.addNestedComponent(new BeanComponentDefinition(methodExceptionResolver, methodExceptionResolverName));
|
||||
compDefinition.addNestedComponent(new BeanComponentDefinition(responseStatusExceptionResolver,
|
||||
responseStatusExceptionResolverName));
|
||||
compDefinition.addNestedComponent(new BeanComponentDefinition(defaultExceptionResolver,
|
||||
defaultExceptionResolverName));
|
||||
compDefinition.addNestedComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
|
||||
registrar.registerComponent(compDefinition);
|
||||
}
|
||||
|
||||
private Object getConversionService(MvcAnnotationDriven spec, ComponentRegistrar registrar) {
|
||||
if (spec.conversionService() != null) {
|
||||
return getBeanOrReference(spec.conversionService());
|
||||
} else {
|
||||
RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
|
||||
conversionDef.setSource(spec.source());
|
||||
conversionDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String conversionName = registrar.registerWithGeneratedName(conversionDef);
|
||||
registrar.registerComponent(new BeanComponentDefinition(conversionDef, conversionName));
|
||||
return new RuntimeBeanReference(conversionName);
|
||||
}
|
||||
}
|
||||
|
||||
private Object getValidator(MvcAnnotationDriven spec, ComponentRegistrar registrar) {
|
||||
if (spec.validator() != null) {
|
||||
return getBeanOrReference(spec.validator());
|
||||
} else if (jsr303Present) {
|
||||
RootBeanDefinition validatorDef = new RootBeanDefinition(LocalValidatorFactoryBean.class);
|
||||
validatorDef.setSource(spec.source());
|
||||
validatorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String validatorName = registrar.registerWithGeneratedName(validatorDef);
|
||||
registrar.registerComponent(new BeanComponentDefinition(validatorDef, validatorName));
|
||||
return new RuntimeBeanReference(validatorName);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Object getMessageCodesResolver(MvcAnnotationDriven spec, ComponentRegistrar registrar) {
|
||||
if (spec.messageCodesResolver() != null) {
|
||||
return getBeanOrReference(spec.messageCodesResolver());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ManagedList<? super Object> getMessageConverters(MvcAnnotationDriven spec, ComponentRegistrar registrar) {
|
||||
ManagedList<? super Object> messageConverters = new ManagedList<Object>();
|
||||
Object source = spec.source();
|
||||
messageConverters.setSource(source);
|
||||
messageConverters.addAll(spec.messageConverters());
|
||||
if (spec.shouldRegisterDefaultMessageConverters()) {
|
||||
messageConverters.add(createConverterBeanDefinition(ByteArrayHttpMessageConverter.class, source));
|
||||
RootBeanDefinition stringConverterDef = createConverterBeanDefinition(StringHttpMessageConverter.class,
|
||||
source);
|
||||
stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
|
||||
messageConverters.add(stringConverterDef);
|
||||
messageConverters.add(createConverterBeanDefinition(ResourceHttpMessageConverter.class, source));
|
||||
messageConverters.add(createConverterBeanDefinition(SourceHttpMessageConverter.class, source));
|
||||
messageConverters.add(createConverterBeanDefinition(XmlAwareFormHttpMessageConverter.class, source));
|
||||
if (jaxb2Present) {
|
||||
messageConverters
|
||||
.add(createConverterBeanDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
|
||||
}
|
||||
if (jacksonPresent) {
|
||||
messageConverters.add(createConverterBeanDefinition(MappingJacksonHttpMessageConverter.class, source));
|
||||
}
|
||||
if (romePresent) {
|
||||
messageConverters.add(createConverterBeanDefinition(AtomFeedHttpMessageConverter.class, source));
|
||||
messageConverters.add(createConverterBeanDefinition(RssChannelHttpMessageConverter.class, source));
|
||||
}
|
||||
}
|
||||
return messageConverters;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private RootBeanDefinition createConverterBeanDefinition(Class<? extends HttpMessageConverter> converterClass,
|
||||
Object source) {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(converterClass);
|
||||
beanDefinition.setSource(source);
|
||||
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
private Object getBeanOrReference(Object bean) {
|
||||
if (bean != null && bean instanceof String) {
|
||||
return new RuntimeBeanReference((String) bean);
|
||||
} else {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import org.springframework.beans.factory.parsing.ProblemCollector;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Specifies the Spring MVC "default-servlet-handler" container feature. The
|
||||
* feature provides the following fine-grained configuration:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link DefaultServletHttpRequestHandler} for serving static files by
|
||||
* forwarding to the Servlet container's "default" Servlet.
|
||||
* <li>{@link SimpleUrlHandlerMapping} to map the above request handler to "/**"
|
||||
* <li>{@link HttpRequestHandlerAdapter} to enable the DispatcherServlet to be
|
||||
* able to invoke the above request handler.
|
||||
* </ul>
|
||||
*
|
||||
* This handler will forward all requests to the default Servlet. Therefore
|
||||
* it is important that it remains last in the order of all other URL
|
||||
* HandlerMappings. That will be the case if you use the {@link MvcAnnotationDriven}
|
||||
* feature or alternatively if you are setting up your customized HandlerMapping
|
||||
* instance be sure to set its "order" property to a value lower than that of
|
||||
* the DefaultServletHttpRequestHandler, which is Integer.MAX_VALUE.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class MvcDefaultServletHandler extends AbstractFeatureSpecification {
|
||||
|
||||
private static final Class<? extends FeatureSpecificationExecutor> EXECUTOR_TYPE = MvcDefaultServletHandlerExecutor.class;
|
||||
|
||||
private String defaultServletName;
|
||||
|
||||
/**
|
||||
* <p>Creates an instance of MvcDefaultServletHandler without.
|
||||
* If this constructor is used the {@link DefaultServletHttpRequestHandler}
|
||||
* will try to auto-detect the container's default Servlet at startup time
|
||||
* using a list of known names.
|
||||
*
|
||||
* <p>If the default Servlet cannot be detected because of using an
|
||||
* unknown container or because it has been manually configured, an
|
||||
* alternate constructor provided here can be used to specify the
|
||||
* servlet name explicitly.
|
||||
*/
|
||||
public MvcDefaultServletHandler() {
|
||||
super(EXECUTOR_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the default Servlet to forward to for static resource requests.
|
||||
* The {@link DefaultServletHttpRequestHandler} will try to auto-detect the
|
||||
* container's default Servlet at startup time using a list of known names.
|
||||
* However if the default Servlet cannot be detected because of using an unknown
|
||||
* container or because it has been manually configured, you can use this
|
||||
* constructor to set the servlet name explicitly.
|
||||
*
|
||||
* @param defaultServletName the name of the default servlet
|
||||
*/
|
||||
public MvcDefaultServletHandler(String defaultServletName) {
|
||||
this();
|
||||
this.defaultServletName = defaultServletName;
|
||||
}
|
||||
|
||||
String defaultServletName() {
|
||||
return this.defaultServletName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(ProblemCollector problems) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.config.AbstractSpecificationExecutor;
|
||||
import org.springframework.context.config.SpecificationContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Executes {@link MvcDefaultServletHandler} specifications, creating and
|
||||
* registering bean definitions as appropriate based on the configuration
|
||||
* within.
|
||||
*
|
||||
* @author Jeremy Grelle
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
final class MvcDefaultServletHandlerExecutor extends AbstractSpecificationExecutor<MvcDefaultServletHandler> {
|
||||
|
||||
private static final String HANDLER_ADAPTER_BEAN_NAME = "org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter";
|
||||
|
||||
@Override
|
||||
protected void doExecute(MvcDefaultServletHandler spec, SpecificationContext specContext) {
|
||||
BeanDefinitionRegistry registry = specContext.getRegistry();
|
||||
ComponentRegistrar registrar = specContext.getRegistrar();
|
||||
Object source = spec.source();
|
||||
|
||||
if (!registry.containsBeanDefinition(HANDLER_ADAPTER_BEAN_NAME)) {
|
||||
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(HttpRequestHandlerAdapter.class);
|
||||
handlerAdapterDef.setSource(source);
|
||||
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
|
||||
registrar.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
|
||||
}
|
||||
|
||||
RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
|
||||
defaultServletHandlerDef.setSource(source);
|
||||
defaultServletHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
if (StringUtils.hasText(spec.defaultServletName())) {
|
||||
defaultServletHandlerDef.getPropertyValues().add("defaultServletName", spec.defaultServletName());
|
||||
}
|
||||
String defaultServletHandlerName = registrar.registerWithGeneratedName(defaultServletHandlerDef);
|
||||
registry.registerBeanDefinition(defaultServletHandlerName, defaultServletHandlerDef);
|
||||
registrar.registerComponent(new BeanComponentDefinition(defaultServletHandlerDef, defaultServletHandlerName));
|
||||
|
||||
Map<String, String> urlMap = new ManagedMap<String, String>();
|
||||
urlMap.put("/**", defaultServletHandlerName);
|
||||
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
|
||||
handlerMappingDef.setSource(source);
|
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
|
||||
String handlerMappingBeanName = registrar.registerWithGeneratedName(handlerMappingDef);
|
||||
registry.registerBeanDefinition(handlerMappingBeanName, handlerMappingDef);
|
||||
registrar.registerComponent(new BeanComponentDefinition(handlerMappingDef, handlerMappingBeanName));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.parsing.ProblemCollector;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
|
||||
/**
|
||||
* Specifies the Spring MVC "interceptors" container feature. The feature
|
||||
* registers one or more {@link MappedInterceptor} bean definitions. A
|
||||
* MappedInterceptor encapsulates an interceptor and one or more (optional)
|
||||
* path patterns to which the interceptor is mapped. The interceptor can be
|
||||
* of type {@link HandlerInterceptor} or {@link WebRequestInterceptor}.
|
||||
* An interceptor can also be provided without path patterns in which case
|
||||
* it applies globally to all handler invocations.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class MvcInterceptors extends AbstractFeatureSpecification {
|
||||
|
||||
private static final Class<? extends FeatureSpecificationExecutor> EXECUTOR_TYPE = MvcInterceptorsExecutor.class;
|
||||
|
||||
private Map<Object, String[]> interceptorMappings = new LinkedHashMap<Object, String[]>();
|
||||
|
||||
/**
|
||||
* Creates an MvcInterceptors instance.
|
||||
*/
|
||||
public MvcInterceptors() {
|
||||
super(EXECUTOR_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more {@link HandlerInterceptor HandlerInterceptors} that should
|
||||
* intercept all handler invocations.
|
||||
*
|
||||
* @param interceptors one or more interceptors
|
||||
*/
|
||||
public MvcInterceptors globalInterceptors(HandlerInterceptor... interceptors) {
|
||||
addInterceptorMappings(null, interceptors);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more {@link WebRequestInterceptor WebRequestInterceptors} that should
|
||||
* intercept all handler invocations.
|
||||
*
|
||||
* @param interceptors one or more interceptors
|
||||
*/
|
||||
public MvcInterceptors globalInterceptors(WebRequestInterceptor... interceptors) {
|
||||
addInterceptorMappings(null, interceptors);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more interceptors by bean name that should intercept all handler
|
||||
* invocations.
|
||||
*
|
||||
* @param interceptors interceptor bean names
|
||||
*/
|
||||
public MvcInterceptors globalInterceptors(String... interceptors) {
|
||||
addInterceptorMappings(null, interceptors);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more {@link HandlerInterceptor HandlerInterceptors} and map
|
||||
* them to the specified path patterns.
|
||||
*
|
||||
* @param pathPatterns the pathPatterns to map the interceptor to
|
||||
* @param interceptors the interceptors
|
||||
*/
|
||||
public MvcInterceptors mappedInterceptors(String[] pathPatterns, HandlerInterceptor... interceptors) {
|
||||
addInterceptorMappings(pathPatterns, interceptors);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more {@link WebRequestInterceptor WebRequestInterceptors} and
|
||||
* map them to the specified path patterns.
|
||||
*
|
||||
* @param pathPatterns the pathPatterns to map the interceptor to
|
||||
* @param interceptors the interceptors
|
||||
*/
|
||||
public MvcInterceptors mappedInterceptors(String[] pathPatterns, WebRequestInterceptor... interceptors) {
|
||||
addInterceptorMappings(pathPatterns, interceptors);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more interceptors by bean name and map them to the specified
|
||||
* path patterns.
|
||||
*
|
||||
* @param pathPatterns the pathPatterns to map to
|
||||
* @param interceptors the interceptors
|
||||
*/
|
||||
public MvcInterceptors mappedInterceptors(String[] pathPatterns, String... interceptors) {
|
||||
addInterceptorMappings(pathPatterns, interceptors);
|
||||
return this;
|
||||
}
|
||||
|
||||
void interceptor(String[] pathPatterns, BeanDefinitionHolder interceptor) {
|
||||
addInterceptorMappings(pathPatterns, new Object[] { interceptor });
|
||||
}
|
||||
|
||||
Map<Object, String[]> interceptorMappings() {
|
||||
return Collections.unmodifiableMap(interceptorMappings);
|
||||
}
|
||||
|
||||
private void addInterceptorMappings(String[] pathPatterns, Object[] interceptors) {
|
||||
for (Object interceptor : interceptors) {
|
||||
interceptorMappings.put(interceptor, pathPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(ProblemCollector problems) {
|
||||
if (interceptorMappings.size() == 0) {
|
||||
problems.error("No interceptors defined.");
|
||||
}
|
||||
for (Object interceptor : interceptorMappings.keySet()) {
|
||||
if (interceptor == null) {
|
||||
problems.error("Null interceptor provided.");
|
||||
}
|
||||
if (interceptorMappings.get(interceptor) != null) {
|
||||
for (String pattern : interceptorMappings.get(interceptor)) {
|
||||
if (!StringUtils.hasText(pattern)) {
|
||||
problems.error("Empty path pattern specified for " + interceptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
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.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.config.AbstractSpecificationExecutor;
|
||||
import org.springframework.context.config.SpecificationContext;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
|
||||
/**
|
||||
* Executes {@link MvcInterceptors} specification, creating and registering
|
||||
* bean definitions as appropriate based on the configuration within.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
final class MvcInterceptorsExecutor extends AbstractSpecificationExecutor<MvcInterceptors> {
|
||||
|
||||
@Override
|
||||
protected void doExecute(MvcInterceptors spec, SpecificationContext specContext) {
|
||||
ComponentRegistrar registrar = specContext.getRegistrar();
|
||||
Object source = spec.source();
|
||||
|
||||
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(spec.sourceName(), source);
|
||||
|
||||
for (Object interceptor : spec.interceptorMappings().keySet()) {
|
||||
RootBeanDefinition beanDef = new RootBeanDefinition(MappedInterceptor.class);
|
||||
beanDef.setSource(source);
|
||||
beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
beanDef.getConstructorArgumentValues().addIndexedArgumentValue(0,
|
||||
spec.interceptorMappings().get(interceptor));
|
||||
beanDef.getConstructorArgumentValues().addIndexedArgumentValue(1, interceptor);
|
||||
|
||||
String beanName = registrar.registerWithGeneratedName(beanDef);
|
||||
compDefinition.addNestedComponent(new BeanComponentDefinition(beanDef, beanName));
|
||||
}
|
||||
|
||||
registrar.registerComponent(compDefinition);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import org.springframework.beans.factory.parsing.ProblemCollector;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Specifies the Spring MVC "resources" container feature. The
|
||||
* feature provides the following fine-grained configuration:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link ResourceHttpRequestHandler} to serve static resources from a
|
||||
* list of web-root relative, classpath, or other locations.
|
||||
* <li>{@link SimpleUrlHandlerMapping} to map the above request handler to a
|
||||
* a specific path pattern (e.g. "/resources/**").
|
||||
* <li>{@link HttpRequestHandlerAdapter} to enable the DispatcherServlet to be
|
||||
* able to invoke the above request handler.
|
||||
* </ul>
|
||||
*
|
||||
* @author Rossen Stoynchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class MvcResources extends AbstractFeatureSpecification {
|
||||
|
||||
private static final Class<? extends FeatureSpecificationExecutor> EXECUTOR_TYPE = MvcResourcesExecutor.class;
|
||||
|
||||
private Object[] locations;
|
||||
|
||||
private String mapping;
|
||||
|
||||
private Object cachePeriod;
|
||||
|
||||
private Object order = Ordered.LOWEST_PRECEDENCE - 1;
|
||||
|
||||
/**
|
||||
* Create an MvcResources specification instance. See alternate constructor
|
||||
* you prefer to use {@link Resource} instances instead of {@code String}-based
|
||||
* resource locations.
|
||||
*
|
||||
* @param mapping - the URL path pattern within the current Servlet context to
|
||||
* use to identify resource requests (e.g. "/resources/**").
|
||||
* @param locations - locations of resources containing static content to be
|
||||
* served. Each location must point to a valid directory. Locations will be
|
||||
* checked in the order specified. For example if "/" and
|
||||
* "classpath:/META-INF/public-web-resources/" are configured resources will
|
||||
* be served from the Web root and from any JAR on the classpath that contains
|
||||
* a /META-INF/public-web-resources/ directory, with resources under the Web root
|
||||
* taking precedence.
|
||||
*/
|
||||
public MvcResources(String mapping, String... locations) {
|
||||
super(EXECUTOR_TYPE);
|
||||
this.locations = locations;
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an MvcResources specification instance. See alternate constructor
|
||||
* defined here if you prefer to use String-based path patterns.
|
||||
*
|
||||
* @param mapping - the URL path pattern within the current Servlet context to
|
||||
* use to identify resource requests (e.g. "/resources/**").
|
||||
* @param resources - Spring {@link Resource} objects containing static
|
||||
* content to be served. Resources will be checked in the order specified.
|
||||
*/
|
||||
public MvcResources(String mapping, Resource... resources) {
|
||||
super(EXECUTOR_TYPE);
|
||||
this.locations = resources;
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* The period of time resources should be cached for in seconds.
|
||||
* The default is to not send any cache headers but rather to rely on
|
||||
* last-modified timestamps only.
|
||||
* <p>Set this to 0 in order to send cache headers that prevent caching,
|
||||
* or to a positive number of seconds in order to send cache headers
|
||||
* with the given max-age value.
|
||||
*
|
||||
* @param cachePeriod the cache period in seconds
|
||||
*/
|
||||
public MvcResources cachePeriod(Integer cachePeriod) {
|
||||
this.cachePeriod = cachePeriod;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a cachePeriod as a String. An alternative to {@link #cachePeriod(Integer)}.
|
||||
* The String must represent an Integer after placeholder and SpEL expression
|
||||
* resolution.
|
||||
*
|
||||
* @param cachePeriod the cache period in seconds
|
||||
*/
|
||||
public MvcResources cachePeriod(String cachePeriod) {
|
||||
this.cachePeriod = cachePeriod;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a cachePeriod as a String. An alternative to {@link #cachePeriod(Integer)}.
|
||||
* The String must represent an Integer after placeholder and SpEL expression
|
||||
* resolution.
|
||||
* <p>Sets the order for the SimpleUrlHandlerMapping used to match resource
|
||||
* requests relative to order value for other HandlerMapping instances
|
||||
* such as the {@link DefaultAnnotationHandlerMapping} used to match
|
||||
* controller requests.
|
||||
*
|
||||
* @param order the order to use. The default value is
|
||||
* {@link Ordered#LOWEST_PRECEDENCE} - 1.
|
||||
*/
|
||||
public MvcResources order(Integer order) {
|
||||
this.order = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify an order as a String. An alternative to {@link #order(Integer)}.
|
||||
* The String must represent an Integer after placeholder and SpEL expression
|
||||
* resolution.
|
||||
*
|
||||
* @param order the order to use. The default value is
|
||||
* {@link Ordered#LOWEST_PRECEDENCE} - 1.
|
||||
*/
|
||||
public MvcResources order(String order) {
|
||||
this.order = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Package private accessors
|
||||
|
||||
Object cachePeriod() {
|
||||
return cachePeriod;
|
||||
}
|
||||
|
||||
Object[] locations() {
|
||||
return this.locations;
|
||||
}
|
||||
|
||||
String mapping() {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
Object order() {
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(ProblemCollector problems) {
|
||||
if (!StringUtils.hasText(mapping)) {
|
||||
problems.error("Mapping is required");
|
||||
}
|
||||
if (locations == null || locations.length == 0) {
|
||||
problems.error("At least one location is required");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.config.AbstractSpecificationExecutor;
|
||||
import org.springframework.context.config.SpecificationContext;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Executes {@link MvcResources} specifications, creating and registering
|
||||
* bean definitions as appropriate based on the configuration within.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Jeremy Grelle
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
final class MvcResourcesExecutor extends AbstractSpecificationExecutor<MvcResources> {
|
||||
|
||||
private static final String HANDLER_ADAPTER_BEAN_NAME = "org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter";
|
||||
|
||||
@Override
|
||||
protected void doExecute(MvcResources spec, SpecificationContext specContext) {
|
||||
BeanDefinitionRegistry registry = specContext.getRegistry();
|
||||
ComponentRegistrar registrar = specContext.getRegistrar();
|
||||
Object source = spec.source();
|
||||
|
||||
if (!registry.containsBeanDefinition(HANDLER_ADAPTER_BEAN_NAME)) {
|
||||
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(HttpRequestHandlerAdapter.class);
|
||||
handlerAdapterDef.setSource(source);
|
||||
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
|
||||
registrar.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
|
||||
}
|
||||
|
||||
RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
|
||||
resourceHandlerDef.setSource(source);
|
||||
resourceHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
resourceHandlerDef.getPropertyValues().add("locations", spec.locations());
|
||||
if (spec.cachePeriod() != null) {
|
||||
resourceHandlerDef.getPropertyValues().add("cacheSeconds", spec.cachePeriod());
|
||||
}
|
||||
String resourceHandlerBeanName = registrar.registerWithGeneratedName(resourceHandlerDef);
|
||||
registry.registerBeanDefinition(resourceHandlerBeanName, resourceHandlerDef);
|
||||
registrar.registerComponent(new BeanComponentDefinition(resourceHandlerDef, resourceHandlerBeanName));
|
||||
|
||||
Map<String, String> urlMap = new ManagedMap<String, String>();
|
||||
urlMap.put(spec.mapping(), resourceHandlerBeanName);
|
||||
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
|
||||
handlerMappingDef.setSource(source);
|
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
|
||||
if (spec.order() != null) {
|
||||
handlerMappingDef.getPropertyValues().add("order", spec.order());
|
||||
}
|
||||
String beanName = registrar.registerWithGeneratedName(handlerMappingDef);
|
||||
registry.registerBeanDefinition(beanName, handlerMappingDef);
|
||||
registrar.registerComponent(new BeanComponentDefinition(handlerMappingDef, beanName));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.parsing.ProblemCollector;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
|
||||
|
||||
/**
|
||||
* Specifies the Spring MVC "View Controllers" container feature. The
|
||||
* feature allows specifying one or more path to view name mappings.
|
||||
* It sets up the following fine-grained configuration:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link ParameterizableViewController} for each path/view name pair.
|
||||
* <li>{@link SimpleUrlHandlerMapping} mapping each view controller to its path.
|
||||
* <li>{@link SimpleControllerHandlerAdapter} to enable the DispatcherServlet
|
||||
* to invoke the view controllers.
|
||||
* </ul>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class MvcViewControllers extends AbstractFeatureSpecification {
|
||||
|
||||
private static final Class<? extends FeatureSpecificationExecutor> EXECUTOR_TYPE = MvcViewControllersExecutor.class;
|
||||
|
||||
private Map<String, String> mappings = new HashMap<String, String>();
|
||||
|
||||
public MvcViewControllers(String path) {
|
||||
this(path, null);
|
||||
}
|
||||
|
||||
public MvcViewControllers(String path, String viewName) {
|
||||
super(EXECUTOR_TYPE);
|
||||
this.mappings.put(path, viewName);
|
||||
}
|
||||
|
||||
public MvcViewControllers viewController(String path) {
|
||||
return this.viewController(path, null);
|
||||
}
|
||||
|
||||
public MvcViewControllers viewController(String path, String viewName) {
|
||||
this.mappings.put(path, viewName);
|
||||
return this;
|
||||
}
|
||||
|
||||
Map<String, String> mappings() {
|
||||
return Collections.unmodifiableMap(mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(ProblemCollector problems) {
|
||||
if (mappings.size() == 0) {
|
||||
problems.error("At least one ViewController must be defined");
|
||||
}
|
||||
for (String path : mappings.keySet()) {
|
||||
if (!StringUtils.hasText(path)) {
|
||||
problems.error("The path attribute in a ViewController is required");
|
||||
}
|
||||
String viewName = mappings.get(path);
|
||||
if (viewName != null && viewName.isEmpty()) {
|
||||
problems.error("The view name in a ViewController may be null but not empty.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.config.AbstractSpecificationExecutor;
|
||||
import org.springframework.context.config.SpecificationContext;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
|
||||
|
||||
/**
|
||||
* Executes {@link MvcViewControllers} specification, creating and registering
|
||||
* bean definitions as appropriate based on the configuration within.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Christian Dupuis
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
final class MvcViewControllersExecutor extends AbstractSpecificationExecutor<MvcViewControllers> {
|
||||
|
||||
private static final String HANDLER_ADAPTER_BEAN_NAME = "org.springframework.web.servlet.config.viewControllerHandlerAdapter";
|
||||
|
||||
private static final String HANDLER_MAPPING_BEAN_NAME = "org.springframework.web.servlet.config.viewControllerHandlerMapping";
|
||||
|
||||
@Override
|
||||
protected void doExecute(MvcViewControllers spec, SpecificationContext specContext) {
|
||||
BeanDefinitionRegistry registry = specContext.getRegistry();
|
||||
ComponentRegistrar registrar = specContext.getRegistrar();
|
||||
Object source = spec.source();
|
||||
|
||||
if (!registry.containsBeanDefinition(HANDLER_ADAPTER_BEAN_NAME)) {
|
||||
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(SimpleControllerHandlerAdapter.class);
|
||||
handlerAdapterDef.setSource(source);
|
||||
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
|
||||
registrar.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
|
||||
}
|
||||
|
||||
BeanDefinition handlerMappingBeanDef = null;
|
||||
if (!registry.containsBeanDefinition(HANDLER_MAPPING_BEAN_NAME)) {
|
||||
RootBeanDefinition beanDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
|
||||
beanDef.setSource(source);
|
||||
beanDef.getPropertyValues().add("order", "1");
|
||||
beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, beanDef);
|
||||
registrar.registerComponent(new BeanComponentDefinition(beanDef, HANDLER_MAPPING_BEAN_NAME));
|
||||
handlerMappingBeanDef = beanDef;
|
||||
} else {
|
||||
handlerMappingBeanDef = registry.getBeanDefinition(HANDLER_MAPPING_BEAN_NAME);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : spec.mappings().entrySet()) {
|
||||
RootBeanDefinition viewControllerDef = new RootBeanDefinition(ParameterizableViewController.class);
|
||||
viewControllerDef.setSource(source);
|
||||
if (entry.getValue() != null) {
|
||||
viewControllerDef.getPropertyValues().add("viewName", entry.getValue());
|
||||
}
|
||||
if (!handlerMappingBeanDef.getPropertyValues().contains("urlMap")) {
|
||||
handlerMappingBeanDef.getPropertyValues().add("urlMap", new ManagedMap<String, BeanDefinition>());
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, BeanDefinition> urlMap = (Map<String, BeanDefinition>) handlerMappingBeanDef
|
||||
.getPropertyValues().getPropertyValue("urlMap").getValue();
|
||||
urlMap.put(entry.getKey(), viewControllerDef);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -16,40 +16,89 @@
|
|||
|
||||
package org.springframework.web.servlet.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.config.AbstractSpecificationBeanDefinitionParser;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.util.StringUtils;
|
||||
import java.util.Map;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a
|
||||
* {@code resources} element.
|
||||
* {@code resources} element to register a {@link ResourceHttpRequestHandler}.
|
||||
* Will also register a {@link SimpleUrlHandlerMapping} for mapping resource requests,
|
||||
* and a {@link HttpRequestHandlerAdapter} if necessary.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Keith Donald
|
||||
* @author Jeremy Grelle
|
||||
* @since 3.0.4
|
||||
* @see MvcResources
|
||||
* @see MvcResourcesExecutor
|
||||
*/
|
||||
class ResourcesBeanDefinitionParser extends AbstractSpecificationBeanDefinitionParser {
|
||||
class ResourcesBeanDefinitionParser extends AbstractHttpRequestHandlerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Parses the {@code <mvc:resources/>} tag
|
||||
*/
|
||||
public FeatureSpecification doParse(Element element, ParserContext parserContext) {
|
||||
String mapping = element.getAttribute("mapping");
|
||||
String[] locations =
|
||||
StringUtils.commaDelimitedListToStringArray(element.getAttribute("location"));
|
||||
@Override
|
||||
public void doParse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
registerResourceMappings(parserContext, element, source);
|
||||
}
|
||||
|
||||
MvcResources spec = new MvcResources(mapping, locations);
|
||||
if (element.hasAttribute("cache-period")) {
|
||||
spec.cachePeriod(element.getAttribute("cache-period"));
|
||||
}
|
||||
if (element.hasAttribute("order")) {
|
||||
spec.order(element.getAttribute("order"));
|
||||
private void registerResourceMappings(ParserContext parserContext, Element element, Object source) {
|
||||
String resourceHandlerName = registerResourceHandler(parserContext, element, source);
|
||||
if (resourceHandlerName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return spec;
|
||||
Map<String, String> urlMap = new ManagedMap<String, String>();
|
||||
String resourceRequestPath = element.getAttribute("mapping");
|
||||
if (!StringUtils.hasText(resourceRequestPath)) {
|
||||
parserContext.getReaderContext().error("The 'mapping' attribute is required.", parserContext.extractSource(element));
|
||||
return;
|
||||
}
|
||||
urlMap.put(resourceRequestPath, resourceHandlerName);
|
||||
|
||||
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
|
||||
handlerMappingDef.setSource(source);
|
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
|
||||
|
||||
String order = element.getAttribute("order");
|
||||
// use a default of near-lowest precedence, still allowing for even lower precedence in other mappings
|
||||
handlerMappingDef.getPropertyValues().add("order", StringUtils.hasText(order) ? order : Ordered.LOWEST_PRECEDENCE - 1);
|
||||
|
||||
String beanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
|
||||
parserContext.getRegistry().registerBeanDefinition(beanName, handlerMappingDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, beanName));
|
||||
}
|
||||
|
||||
private String registerResourceHandler(ParserContext parserContext, Element element, Object source) {
|
||||
String locationAttr = element.getAttribute("location");
|
||||
if (!StringUtils.hasText(locationAttr)) {
|
||||
parserContext.getReaderContext().error("The 'location' attribute is required.", parserContext.extractSource(element));
|
||||
return null;
|
||||
}
|
||||
|
||||
RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
|
||||
resourceHandlerDef.setSource(source);
|
||||
resourceHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
resourceHandlerDef.getPropertyValues().add("locations", StringUtils.commaDelimitedListToStringArray(locationAttr));
|
||||
|
||||
String cacheSeconds = element.getAttribute("cache-period");
|
||||
if (StringUtils.hasText(cacheSeconds)) {
|
||||
resourceHandlerDef.getPropertyValues().add("cacheSeconds", cacheSeconds);
|
||||
}
|
||||
|
||||
String beanName = parserContext.getReaderContext().generateBeanName(resourceHandlerDef);
|
||||
parserContext.getRegistry().registerBeanDefinition(beanName, resourceHandlerDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(resourceHandlerDef, beanName));
|
||||
return beanName;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,30 +16,88 @@
|
|||
|
||||
package org.springframework.web.servlet.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.config.AbstractSpecificationBeanDefinitionParser;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import java.util.Map;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a
|
||||
* {@code view-controller} element to register a {@link ParameterizableViewController}.
|
||||
* Will also register a {@link SimpleUrlHandlerMapping} for view controllers.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Keith Donald
|
||||
* @author Christian Dupuis
|
||||
* @since 3.0
|
||||
* @see MvcViewControllers
|
||||
* @see MvcViewControllersExecutor
|
||||
*/
|
||||
class ViewControllerBeanDefinitionParser extends AbstractSpecificationBeanDefinitionParser {
|
||||
class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Parses the {@code <mvc:view-controller/>} tag.
|
||||
*/
|
||||
public FeatureSpecification doParse(Element element, ParserContext parserContext) {
|
||||
String path = element.getAttribute("path");
|
||||
String viewName = element.getAttribute("view-name");
|
||||
return new MvcViewControllers(path, viewName.isEmpty() ? null : viewName);
|
||||
private static final String HANDLER_ADAPTER_BEAN_NAME =
|
||||
"org.springframework.web.servlet.config.viewControllerHandlerAdapter";
|
||||
|
||||
private static final String HANDLER_MAPPING_BEAN_NAME =
|
||||
"org.springframework.web.servlet.config.viewControllerHandlerMapping";
|
||||
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
// Register handler adapter
|
||||
registerHanderAdapter(parserContext, source);
|
||||
|
||||
// Register handler mapping
|
||||
BeanDefinition handlerMappingDef = registerHandlerMapping(parserContext, source);
|
||||
|
||||
// Create view controller bean definition
|
||||
RootBeanDefinition viewControllerDef = new RootBeanDefinition(ParameterizableViewController.class);
|
||||
viewControllerDef.setSource(source);
|
||||
if (element.hasAttribute("view-name")) {
|
||||
viewControllerDef.getPropertyValues().add("viewName", element.getAttribute("view-name"));
|
||||
}
|
||||
Map<String, BeanDefinition> urlMap;
|
||||
if (handlerMappingDef.getPropertyValues().contains("urlMap")) {
|
||||
urlMap = (Map<String, BeanDefinition>) handlerMappingDef.getPropertyValues().getPropertyValue("urlMap").getValue();
|
||||
}
|
||||
else {
|
||||
urlMap = new ManagedMap<String, BeanDefinition>();
|
||||
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
|
||||
}
|
||||
urlMap.put(element.getAttribute("path"), viewControllerDef);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void registerHanderAdapter(ParserContext parserContext, Object source) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(HANDLER_ADAPTER_BEAN_NAME)) {
|
||||
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(SimpleControllerHandlerAdapter.class);
|
||||
handlerAdapterDef.setSource(source);
|
||||
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parserContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private BeanDefinition registerHandlerMapping(ParserContext parserContext, Object source) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(HANDLER_MAPPING_BEAN_NAME)) {
|
||||
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
|
||||
handlerMappingDef.setSource(source);
|
||||
handlerMappingDef.getPropertyValues().add("order", "1");
|
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parserContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
|
||||
return handlerMappingDef;
|
||||
}
|
||||
else {
|
||||
return parserContext.getRegistry().getBeanDefinition(HANDLER_MAPPING_BEAN_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Feature;
|
||||
import org.springframework.context.annotation.FeatureConfiguration;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebArgumentResolverAdapter;
|
||||
|
||||
/**
|
||||
* Integration tests for the {@link MvcAnnotationDriven} feature specification.
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class MvcAnnotationDrivenFeatureTests {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testMessageCodesResolver() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(MvcFeature.class, MvcBeans.class);
|
||||
ctx.refresh();
|
||||
RequestMappingHandlerMethodAdapter adapter = ctx.getBean(RequestMappingHandlerMethodAdapter.class);
|
||||
assertNotNull(adapter);
|
||||
Object initializer = new DirectFieldAccessor(adapter).getPropertyValue("webBindingInitializer");
|
||||
assertNotNull(initializer);
|
||||
MessageCodesResolver resolver = ((ConfigurableWebBindingInitializer) initializer).getMessageCodesResolver();
|
||||
assertNotNull(resolver);
|
||||
assertEquals("test.foo.bar", resolver.resolveMessageCodes("foo", "bar")[0]);
|
||||
Object value = new DirectFieldAccessor(adapter).getPropertyValue("customArgumentResolvers");
|
||||
assertNotNull(value);
|
||||
List<HandlerMethodArgumentResolver> resolvers = (List<HandlerMethodArgumentResolver>) value;
|
||||
assertEquals(2, resolvers.size());
|
||||
assertTrue(resolvers.get(0) instanceof ServletWebArgumentResolverAdapter);
|
||||
assertTrue(resolvers.get(1) instanceof TestHandlerMethodArgumentResolver);
|
||||
Object converters = new DirectFieldAccessor(adapter).getPropertyValue("messageConverters");
|
||||
assertNotNull(converters);
|
||||
List<HttpMessageConverter<?>> convertersArray = (List<HttpMessageConverter<?>>) converters;
|
||||
assertTrue("Default converters are registered in addition to the custom one", convertersArray.size() > 1);
|
||||
assertTrue(convertersArray.get(0) instanceof StringHttpMessageConverter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
class MvcFeature {
|
||||
@Feature
|
||||
public MvcAnnotationDriven annotationDriven(MvcBeans mvcBeans) {
|
||||
return new MvcAnnotationDriven()
|
||||
.conversionService(mvcBeans.conversionService())
|
||||
.messageCodesResolver(mvcBeans.messageCodesResolver())
|
||||
.validator(mvcBeans.validator())
|
||||
.messageConverters(new StringHttpMessageConverter())
|
||||
.argumentResolvers(new TestWebArgumentResolver())
|
||||
.argumentResolvers(new TestHandlerMethodArgumentResolver());
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class MvcBeans {
|
||||
@Bean
|
||||
public FormattingConversionService conversionService() {
|
||||
return new DefaultFormattingConversionService();
|
||||
}
|
||||
@Bean
|
||||
public Validator validator() {
|
||||
return new LocalValidatorFactoryBean();
|
||||
}
|
||||
@Bean MessageCodesResolver messageCodesResolver() {
|
||||
return new TestMessageCodesResolver();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Feature;
|
||||
import org.springframework.context.annotation.FeatureConfiguration;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link MvcDefaultServletHandler} feature specification.
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class MvcDefaultServletHandlerTests {
|
||||
|
||||
@Test
|
||||
public void testDefaultServletHandler() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(MvcDefaultServletHandlerFeature.class);
|
||||
ctx.refresh();
|
||||
HttpRequestHandlerAdapter adapter = ctx.getBean(HttpRequestHandlerAdapter.class);
|
||||
assertNotNull(adapter);
|
||||
DefaultServletHttpRequestHandler handler = ctx.getBean(DefaultServletHttpRequestHandler.class);
|
||||
assertNotNull(handler);
|
||||
String defaultServletHandlerName = (String) new DirectFieldAccessor(handler)
|
||||
.getPropertyValue("defaultServletName");
|
||||
assertEquals("foo", defaultServletHandlerName);
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
private static class MvcDefaultServletHandlerFeature {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Feature
|
||||
public MvcDefaultServletHandler defaultServletHandler() {
|
||||
return new MvcDefaultServletHandler("foo");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import static org.easymock.EasyMock.capture;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.easymock.Capture;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Feature;
|
||||
import org.springframework.context.annotation.FeatureConfiguration;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
import org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor;
|
||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||
import org.springframework.web.servlet.theme.ThemeChangeInterceptor;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link MvcInterceptors}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class MvcInterceptorsTests {
|
||||
|
||||
@Test
|
||||
public void testInterceptors() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(MvcInterceptorsFeature.class);
|
||||
ctx.refresh();
|
||||
|
||||
Iterator<MappedInterceptor> itr = ctx.getBeansOfType(MappedInterceptor.class).values().iterator();
|
||||
|
||||
MappedInterceptor interceptor = itr.next();
|
||||
assertTrue(interceptor.getInterceptor() instanceof UserRoleAuthorizationInterceptor);
|
||||
assertNull(interceptor.getPathPatterns());
|
||||
|
||||
interceptor = itr.next();
|
||||
assertTrue(interceptor.getInterceptor() instanceof LocaleChangeInterceptor);
|
||||
assertArrayEquals(new String[] { "/locale", "/locale/**" }, interceptor.getPathPatterns());
|
||||
|
||||
interceptor = itr.next();
|
||||
assertTrue(interceptor.getInterceptor() instanceof ThemeChangeInterceptor);
|
||||
assertArrayEquals(new String[] { "/theme", "/theme/**" }, interceptor.getPathPatterns());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateNoInterceptors() {
|
||||
ProblemReporter reporter = createMock(ProblemReporter.class);
|
||||
Capture<Problem> captured = new Capture<Problem>();
|
||||
reporter.error(capture(captured));
|
||||
replay(reporter);
|
||||
|
||||
boolean result = new MvcInterceptors().validate(reporter);
|
||||
|
||||
assertFalse(result);
|
||||
assertEquals("No interceptors defined.", captured.getValue().getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateNullHandler() {
|
||||
ProblemReporter reporter = createMock(ProblemReporter.class);
|
||||
Capture<Problem> captured = new Capture<Problem>();
|
||||
reporter.error(capture(captured));
|
||||
replay(reporter);
|
||||
|
||||
HandlerInterceptor[] interceptors = new HandlerInterceptor[] { null };
|
||||
boolean result = new MvcInterceptors().globalInterceptors(interceptors).validate(reporter);
|
||||
|
||||
assertFalse(result);
|
||||
assertTrue(captured.getValue().getMessage().contains("Null interceptor"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateEmptyPath() {
|
||||
ProblemReporter reporter = createMock(ProblemReporter.class);
|
||||
Capture<Problem> captured = new Capture<Problem>();
|
||||
reporter.error(capture(captured));
|
||||
replay(reporter);
|
||||
|
||||
HandlerInterceptor[] interceptors = new HandlerInterceptor[] { new LocaleChangeInterceptor() };
|
||||
String[] patterns = new String[] { "" };
|
||||
boolean result = new MvcInterceptors().mappedInterceptors(patterns, interceptors).validate(reporter);
|
||||
|
||||
assertFalse(result);
|
||||
assertTrue(captured.getValue().getMessage().startsWith("Empty path pattern specified for "));
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
private static class MvcInterceptorsFeature {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Feature
|
||||
public MvcInterceptors interceptors() {
|
||||
return new MvcInterceptors()
|
||||
.globalInterceptors(new UserRoleAuthorizationInterceptor())
|
||||
.mappedInterceptors(new String[] { "/locale", "/locale/**" }, new LocaleChangeInterceptor())
|
||||
.mappedInterceptors(new String[] { "/theme", "/theme/**"}, new ThemeChangeInterceptor());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Feature;
|
||||
import org.springframework.context.annotation.FeatureConfiguration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link MvcResources} feature specification.
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class MvcResourcesTests {
|
||||
|
||||
@Test
|
||||
public void testResources() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(MvcResourcesFeature.class);
|
||||
ctx.refresh();
|
||||
HttpRequestHandlerAdapter adapter = ctx.getBean(HttpRequestHandlerAdapter.class);
|
||||
assertNotNull(adapter);
|
||||
ResourceHttpRequestHandler handler = ctx.getBean(ResourceHttpRequestHandler.class);
|
||||
assertNotNull(handler);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Resource> locations = (List<Resource>) new DirectFieldAccessor(handler).getPropertyValue("locations");
|
||||
assertNotNull(locations);
|
||||
assertEquals(2, locations.size());
|
||||
assertEquals("foo", locations.get(0).getFilename());
|
||||
assertEquals("bar", locations.get(1).getFilename());
|
||||
SimpleUrlHandlerMapping mapping = ctx.getBean(SimpleUrlHandlerMapping.class);
|
||||
assertEquals(1, mapping.getOrder());
|
||||
assertSame(handler, mapping.getHandlerMap().get("/resources/**"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidResources() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(InvalidMvcResourcesFeature.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
fail("Invalid feature spec should not validate");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getCause().getMessage().contains("Mapping is required"));
|
||||
// TODO : should all problems be in the message ?
|
||||
}
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
private static class MvcResourcesFeature {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Feature
|
||||
public MvcResources resources() {
|
||||
return new MvcResources("/resources/**", new String[] { "/foo", "/bar" }).cachePeriod(86400).order(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
private static class InvalidMvcResourcesFeature {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Feature
|
||||
public MvcResources resources() {
|
||||
return new MvcResources(" ", new String[] {});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* 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.web.servlet.config;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Feature;
|
||||
import org.springframework.context.annotation.FeatureConfiguration;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link MvcViewControllers} feature specification.
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class MvcViewControllersTests {
|
||||
|
||||
@Test
|
||||
public void testMvcViewControllers() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(MvcViewControllersFeature.class);
|
||||
ctx.refresh();
|
||||
SimpleControllerHandlerAdapter adapter = ctx.getBean(SimpleControllerHandlerAdapter.class);
|
||||
assertNotNull(adapter);
|
||||
SimpleUrlHandlerMapping handler = ctx.getBean(SimpleUrlHandlerMapping.class);
|
||||
assertNotNull(handler);
|
||||
Map<String, ?> urlMap = handler.getUrlMap();
|
||||
assertNotNull(urlMap);
|
||||
assertEquals(2, urlMap.size());
|
||||
ParameterizableViewController controller = (ParameterizableViewController) urlMap.get("/");
|
||||
assertNotNull(controller);
|
||||
assertEquals("home", controller.getViewName());
|
||||
controller = (ParameterizableViewController) urlMap.get("/account");
|
||||
assertNotNull(controller);
|
||||
assertNull(controller.getViewName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyPath() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(EmptyPathViewControllersFeature.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
fail("expected exception");
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex.getCause().getMessage().contains("path attribute"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyViewName() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(EmptyViewNameViewControllersFeature.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
fail("expected exception");
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex.getCause().getMessage().contains("not empty"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullViewName() {
|
||||
FailFastProblemReporter problemReporter = new FailFastProblemReporter();
|
||||
assertThat(new MvcViewControllers("/some/path").validate(problemReporter), is(true));
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
private static class MvcViewControllersFeature {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Feature
|
||||
public MvcViewControllers mvcViewControllers() {
|
||||
return new MvcViewControllers("/", "home").viewController("/account");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
private static class EmptyViewNameViewControllersFeature {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Feature
|
||||
public MvcViewControllers mvcViewControllers() {
|
||||
return new MvcViewControllers("/some/path", "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
private static class EmptyPathViewControllersFeature {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Feature
|
||||
public MvcViewControllers mvcViewControllers() {
|
||||
return new MvcViewControllers("", "someViewName");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue