diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java index 5f4ee7f555..f0fd4c5cac 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.springframework.beans.factory.annotation; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.util.Assert; /** * Extension of the {@link org.springframework.beans.factory.support.GenericBeanDefinition} @@ -32,27 +33,46 @@ import org.springframework.core.type.StandardAnnotationMetadata; * which also implements the AnnotatedBeanDefinition interface). * * @author Juergen Hoeller + * @author Chris Beams * @since 2.5 * @see AnnotatedBeanDefinition#getMetadata() * @see org.springframework.core.type.StandardAnnotationMetadata */ +@SuppressWarnings("serial") public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition { - private final AnnotationMetadata annotationMetadata; + private final AnnotationMetadata metadata; /** * Create a new AnnotatedGenericBeanDefinition for the given bean class. * @param beanClass the loaded bean class */ - public AnnotatedGenericBeanDefinition(Class beanClass) { + public AnnotatedGenericBeanDefinition(Class beanClass) { setBeanClass(beanClass); - this.annotationMetadata = new StandardAnnotationMetadata(beanClass, true); + this.metadata = new StandardAnnotationMetadata(beanClass, true); + } + + /** + * Create a new AnnotatedGenericBeanDefinition for the given annotation metadata, + * allowing for ASM-based processing and avoidance of early loading of the bean class. + * Note that this constructor is functionally equivalent to + * {@link org.springframework.context.annotation.ScannedGenericBeanDefinition + * ScannedGenericBeanDefinition}, however the semantics of the latter indicate that + * a bean was discovered specifically via component-scanning as opposed to other + * means. + * @param metadata the annotation metadata for the bean class in question + * @since 3.1.1 + */ + public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata) { + Assert.notNull(metadata, "AnnotationMetadata must not be null"); + setBeanClassName(metadata.getClassName()); + this.metadata = metadata; } public final AnnotationMetadata getMetadata() { - return this.annotationMetadata; + return this.metadata; } } diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java index dd71eef9a1..80ce225457 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -17,6 +17,7 @@ package org.springframework.scheduling.quartz; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -40,6 +41,7 @@ import org.springframework.beans.support.ArgumentConvertingMethodInvoker; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.MethodInvoker; +import org.springframework.util.ReflectionUtils; /** * {@link org.springframework.beans.factory.FactoryBean} that exposes a @@ -80,6 +82,8 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod private static Class jobDetailImplClass; + private static Method setResultMethod; + static { try { jobDetailImplClass = Class.forName("org.quartz.impl.JobDetailImpl"); @@ -87,6 +91,14 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod catch (ClassNotFoundException ex) { jobDetailImplClass = null; } + try { + Class jobExecutionContextClass = + QuartzJobBean.class.getClassLoader().loadClass("org.quartz.JobExecutionContext"); + setResultMethod = jobExecutionContextClass.getMethod("setResult", Object.class); + } + catch (Exception ex) { + throw new IllegalStateException("Incompatible Quartz API: " + ex); + } } @@ -296,7 +308,7 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { try { - context.setResult(this.methodInvoker.invoke()); + ReflectionUtils.invokeMethod(setResultMethod, context, this.methodInvoker.invoke()); } catch (InvocationTargetException ex) { if (ex.getTargetException() instanceof JobExecutionException) { diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AdviceModeImportSelector.java b/spring-context/src/main/java/org/springframework/context/annotation/AdviceModeImportSelector.java index 8c4651bc37..eae51ec251 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AdviceModeImportSelector.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AdviceModeImportSelector.java @@ -16,8 +16,6 @@ package org.springframework.context.annotation; -import static org.springframework.context.annotation.MetadataUtils.attributesFor; - import java.lang.annotation.Annotation; import org.springframework.core.GenericTypeResolver; @@ -25,6 +23,8 @@ import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; +import static org.springframework.context.annotation.MetadataUtils.*; + /** * Convenient base class for {@link ImportSelector} implementations that select imports * based on an {@link AdviceMode} value from an annotation (such as the {@code @Enable*} diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java index 914b9d9ce7..d147a50656 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -109,6 +109,8 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { this.reader.setBeanNameGenerator(beanNameGenerator); this.scanner.setBeanNameGenerator(beanNameGenerator); + this.getBeanFactory().registerSingleton( + AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator); } /** diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java index d184aa9b00..793ba314b4 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java @@ -16,8 +16,6 @@ package org.springframework.context.annotation; -import static org.springframework.context.annotation.MetadataUtils.attributesFor; - import java.util.LinkedHashSet; import java.util.Set; @@ -32,6 +30,8 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.ClassUtils; +import static org.springframework.context.annotation.MetadataUtils.*; + /** * Utility class that allows for convenient registration of common * {@link org.springframework.beans.factory.config.BeanPostProcessor} and @@ -56,6 +56,17 @@ public class AnnotationConfigUtils { public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"; + /** + * The bean name of the internally managed BeanNameGenerator for use when processing + * {@link Configuration} classes. Set by {@link AnnotationConfigApplicationContext} + * and {@code AnnotationConfigWebApplicationContext} during bootstrap in order to make + * any custom name generation strategy available to the underlying + * {@link ConfigurationClassPostProcessor}. + * @since 3.1.1 + */ + public static final String CONFIGURATION_BEAN_NAME_GENERATOR = + "org.springframework.context.annotation.internalConfigurationBeanNameGenerator"; + /** * The bean name of the internally managed Autowired annotation processor. */ @@ -249,4 +260,5 @@ public class AnnotationConfigUtils { return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass); } + } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java index 0d655830b3..493745bd82 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java @@ -16,8 +16,6 @@ package org.springframework.context.annotation; -import static org.springframework.context.annotation.MetadataUtils.attributesFor; - import java.lang.annotation.Annotation; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; @@ -25,6 +23,8 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.util.Assert; +import static org.springframework.context.annotation.MetadataUtils.*; + /** * A {@link ScopeMetadataResolver} implementation that by default checks for * the presence of Spring's {@link Scope} annotation on the bean class. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AspectJAutoProxyRegistrar.java b/spring-context/src/main/java/org/springframework/context/annotation/AspectJAutoProxyRegistrar.java index b41fa7da25..aedbbbe28e 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AspectJAutoProxyRegistrar.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AspectJAutoProxyRegistrar.java @@ -16,13 +16,13 @@ package org.springframework.context.annotation; -import static org.springframework.context.annotation.MetadataUtils.attributesFor; - import org.springframework.aop.config.AopConfigUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; +import static org.springframework.context.annotation.MetadataUtils.*; + /** * Registers an {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator * AnnotationAwareAspectJAutoProxyCreator} against the current {@link BeanDefinitionRegistry} diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java index 04a249566d..2afeba197e 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java @@ -30,9 +30,9 @@ import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.annotation.AnnotationAttributes; -import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.EnvironmentCapable; +import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java index 20729b0c5b..f0826a62eb 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -77,8 +77,14 @@ public @interface ComponentScan { /** * The {@link BeanNameGenerator} class to be used for naming detected components * within the Spring container. + *

The default value of the {@link BeanNameGenerator} interface itself indicates + * that the scanner used to process this {@code @ComponentScan} annotation should + * use its inherited bean name generator, e.g. the default + * {@link AnnotationBeanNameGenerator} or any custom instance supplied to the + * application context at bootstrap time. + * @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator) */ - Class nameGenerator() default AnnotationBeanNameGenerator.class; + Class nameGenerator() default BeanNameGenerator.class; /** * The {@link ScopeMetadataResolver} to be used for resolving the scope of detected components. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java index 9fa82e40c7..a549b8d0af 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java @@ -51,11 +51,16 @@ class ComponentScanAnnotationParser { private final BeanDefinitionRegistry registry; + private final BeanNameGenerator beanNameGenerator; + public ComponentScanAnnotationParser( - ResourceLoader resourceLoader, Environment environment, BeanDefinitionRegistry registry) { + ResourceLoader resourceLoader, Environment environment, + BeanNameGenerator beanNameGenerator, BeanDefinitionRegistry registry) { + this.resourceLoader = resourceLoader; this.environment = environment; + this.beanNameGenerator = beanNameGenerator; this.registry = registry; } @@ -71,7 +76,10 @@ class ComponentScanAnnotationParser { scanner.setResourceLoader(this.resourceLoader); Class generatorClass = componentScan.getClass("nameGenerator"); - scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass)); + boolean useInheritedGenerator = BeanNameGenerator.class.equals(generatorClass); + scanner.setBeanNameGenerator(useInheritedGenerator + ? this.beanNameGenerator + : BeanUtils.instantiateClass(generatorClass)); ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); if (scopedProxyMode != ScopedProxyMode.DEFAULT) { diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index ffa0e08d82..02b4125e61 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -16,7 +16,6 @@ package org.springframework.context.annotation; -import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -29,6 +28,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.BeanDefinition; @@ -37,12 +37,10 @@ import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.parsing.SourceExtractor; -import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionReader; -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.BeanNameGenerator; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; @@ -50,8 +48,8 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.MethodMetadata; -import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; import static org.springframework.context.annotation.MetadataUtils.*; @@ -85,6 +83,8 @@ class ConfigurationClassBeanDefinitionReader { private final Environment environment; + private final BeanNameGenerator beanNameGenerator; + /** * Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used @@ -92,16 +92,20 @@ class ConfigurationClassBeanDefinitionReader { * @param problemReporter * @param metadataReaderFactory */ - public ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor, + public ConfigurationClassBeanDefinitionReader( + BeanDefinitionRegistry registry, SourceExtractor sourceExtractor, ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory, - ResourceLoader resourceLoader, Environment environment) { + ResourceLoader resourceLoader, Environment environment, + BeanNameGenerator beanNameGenerator) { + Assert.notNull(beanNameGenerator, "BeanNameGenerator must not be null"); this.registry = registry; this.sourceExtractor = sourceExtractor; this.problemReporter = problemReporter; this.metadataReaderFactory = metadataReaderFactory; this.resourceLoader = resourceLoader; this.environment = environment; + this.beanNameGenerator = beanNameGenerator; } @@ -135,39 +139,21 @@ class ConfigurationClassBeanDefinitionReader { return; } - BeanDefinition configBeanDef = new GenericBeanDefinition(); - String className = configClass.getMetadata().getClassName(); + AnnotationMetadata metadata = configClass.getMetadata(); + BeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata); + String className = metadata.getClassName(); configBeanDef.setBeanClassName(className); - MetadataReader reader; - try { - reader = this.metadataReaderFactory.getMetadataReader(className); - } - catch (IOException ex) { - throw new IllegalStateException("Could not create MetadataReader for class " + className); - } if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) { - Map configAttributes = - reader.getAnnotationMetadata().getAnnotationAttributes(Configuration.class.getName()); - - // has the 'value' attribute of @Configuration been set? - String configBeanName = (String) configAttributes.get("value"); - if (StringUtils.hasText(configBeanName)) { - // yes -> register the configuration class bean with this name - this.registry.registerBeanDefinition(configBeanName, configBeanDef); - } - else { - // no -> register the configuration class bean with a generated name - configBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName((AbstractBeanDefinition)configBeanDef, this.registry); - } + String configBeanName = this.beanNameGenerator.generateBeanName(configBeanDef, this.registry); + this.registry.registerBeanDefinition(configBeanName, configBeanDef); configClass.setBeanName(configBeanName); if (logger.isDebugEnabled()) { logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName)); } } else { - AnnotationMetadata metadata = reader.getAnnotationMetadata(); this.problemReporter.error( - new InvalidConfigurationImportProblem(className, reader.getResource(), metadata)); + new InvalidConfigurationImportProblem(className, configClass.getResource(), metadata)); } } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index a6533c9111..1ad4838ed8 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -16,8 +16,6 @@ package org.springframework.context.annotation; -import static org.springframework.context.annotation.MetadataUtils.attributesFor; - import java.io.IOException; import java.lang.annotation.Annotation; import java.util.ArrayList; @@ -38,6 +36,7 @@ import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; @@ -51,6 +50,8 @@ import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.util.StringUtils; +import static org.springframework.context.annotation.MetadataUtils.*; + /** * Parses a {@link Configuration} class definition, populating a collection of * {@link ConfigurationClass} objects (parsing a single Configuration class may result in @@ -77,6 +78,8 @@ class ConfigurationClassParser { private final ImportStack importStack = new ImportStack(); + private final Set knownSuperclasses = new LinkedHashSet(); + private final Set configurationClasses = new LinkedHashSet(); @@ -98,14 +101,16 @@ class ConfigurationClassParser { */ public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory, ProblemReporter problemReporter, Environment environment, - ResourceLoader resourceLoader, BeanDefinitionRegistry registry) { + ResourceLoader resourceLoader, BeanNameGenerator beanNameGenerator, + BeanDefinitionRegistry registry) { + this.metadataReaderFactory = metadataReaderFactory; this.problemReporter = problemReporter; this.environment = environment; this.resourceLoader = resourceLoader; this.registry = registry; - this.componentScanParser = - new ComponentScanAnnotationParser(this.resourceLoader, this.environment, this.registry); + this.componentScanParser = new ComponentScanAnnotationParser( + resourceLoader, environment, beanNameGenerator, registry); } @@ -138,23 +143,12 @@ class ConfigurationClassParser { } } - while (metadata != null) { - doProcessConfigurationClass(configClass, metadata); - String superClassName = metadata.getSuperClassName(); - if (superClassName != null && !Object.class.getName().equals(superClassName)) { - if (metadata instanceof StandardAnnotationMetadata) { - Class clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass(); - metadata = new StandardAnnotationMetadata(clazz.getSuperclass(), true); - } - else { - MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superClassName); - metadata = reader.getAnnotationMetadata(); - } - } - else { - metadata = null; - } + // recursively process the configuration class and its superclass hierarchy + do { + metadata = doProcessConfigurationClass(configClass, metadata); } + while (metadata != null); + if (this.configurationClasses.contains(configClass) && configClass.getBeanName() != null) { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. @@ -164,7 +158,11 @@ class ConfigurationClassParser { this.configurationClasses.add(configClass); } - protected void doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException { + /** + * @return annotation metadata of superclass, null if none found or previously processed + */ + protected AnnotationMetadata doProcessConfigurationClass( + ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException { // recursively process any member (nested) classes first for (String memberClassName : metadata.getMemberClassNames()) { @@ -227,8 +225,26 @@ class ConfigurationClassParser { for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } - } + // process superclass, if any + if (metadata.hasSuperClass()) { + String superclass = metadata.getSuperClassName(); + if (this.knownSuperclasses.add(superclass)) { + // superclass found, return its annotation metadata and recurse + if (metadata instanceof StandardAnnotationMetadata) { + Class clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass(); + return new StandardAnnotationMetadata(clazz.getSuperclass(), true); + } + else { + MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superclass); + return reader.getAnnotationMetadata(); + } + } + } + + // no superclass, processing is complete + return null; + } /** * Return a list of attribute maps for all declarations of the given annotation diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java index 8e724106de..9736099c6f 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java @@ -46,6 +46,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; @@ -65,6 +66,8 @@ import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import static org.springframework.context.annotation.AnnotationConfigUtils.*; + /** * {@link BeanFactoryPostProcessor} used for bootstrapping processing of * {@link Configuration @Configuration} classes. @@ -112,6 +115,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo private ConfigurationClassBeanDefinitionReader reader; + private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); + /** * Set the {@link SourceExtractor} to use for generated bean definitions @@ -142,6 +147,26 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo this.setMetadataReaderFactoryCalled = true; } + /** + * Set the {@link BeanNameGenerator} to be used when registering imported and nested + * {@link Configuration} classes. The default is {@link AnnotationBeanNameGenerator}. + *

Note that this strategy does not apply to {@link Bean} methods. + *

This setter is typically only appropriate when configuring the post-processor as + * a standalone bean definition in XML, e.g. not using the dedicated + * {@code AnnotationConfig*} application contexts or the {@code + * } element. Any bean name generator specified against + * the application context will take precedence over any value set here. + * @param beanNameGenerator the strategy to use when generating configuration class + * bean names + * @since 3.1.1 + * @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator) + * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR + */ + public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { + Assert.notNull(beanNameGenerator, "BeanNameGenerator must not be null"); + this.beanNameGenerator = beanNameGenerator; + } + public void setEnvironment(Environment environment) { Assert.notNull(environment, "Environment must not be null"); this.environment = environment; @@ -197,14 +222,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo enhanceConfigurationClasses(beanFactory); } - private ConfigurationClassBeanDefinitionReader getConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry) { - if (this.reader == null) { - this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, - this.problemReporter, this.metadataReaderFactory, this.resourceLoader, this.environment); - } - return this.reader; - } - /** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. @@ -223,9 +240,19 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo return; } + // Detect any custom bean name generation strategy supplied through the enclosing application context + SingletonBeanRegistry singletonRegistry = null; + if (registry instanceof SingletonBeanRegistry) { + singletonRegistry = (SingletonBeanRegistry) registry; + if (singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) { + this.beanNameGenerator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); + } + } + // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( - this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, registry); + this.metadataReaderFactory, this.problemReporter, this.environment, + this.resourceLoader, this.beanNameGenerator, registry); for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { @@ -258,12 +285,18 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo } // Read the model and create bean definitions based on its content - this.getConfigurationClassBeanDefinitionReader(registry).loadBeanDefinitions(parser.getConfigurationClasses()); + if (this.reader == null) { + this.reader = new ConfigurationClassBeanDefinitionReader( + registry, this.sourceExtractor, this.problemReporter, + this.metadataReaderFactory, this.resourceLoader, this.environment, + this.beanNameGenerator); + } + this.reader.loadBeanDefinitions(parser.getConfigurationClasses()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes - if (registry instanceof SingletonBeanRegistry) { - if (!((SingletonBeanRegistry) registry).containsSingleton("importRegistry")) { - ((SingletonBeanRegistry) registry).registerSingleton("importRegistry", parser.getImportRegistry()); + if (singletonRegistry != null) { + if (!singletonRegistry.containsSingleton("importRegistry")) { + singletonRegistry.registerSingleton("importRegistry", parser.getImportRegistry()); } } } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/LoadTimeWeavingConfiguration.java b/spring-context/src/main/java/org/springframework/context/annotation/LoadTimeWeavingConfiguration.java index 3f74a3d779..d81821b703 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/LoadTimeWeavingConfiguration.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/LoadTimeWeavingConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.context.annotation; -import static org.springframework.context.weaving.AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE; - import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; @@ -30,6 +28,8 @@ import org.springframework.core.type.AnnotationMetadata; import org.springframework.instrument.classloading.LoadTimeWeaver; import org.springframework.util.Assert; +import static org.springframework.context.weaving.AspectJWeavingEnabler.*; + /** * {@code @Configuration} class that registers a {@link LoadTimeWeaver} bean. * diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ScannedGenericBeanDefinition.java b/spring-context/src/main/java/org/springframework/context/annotation/ScannedGenericBeanDefinition.java index 22d918c887..b5afe9f6de 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ScannedGenericBeanDefinition.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ScannedGenericBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2012 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. @@ -17,6 +17,7 @@ package org.springframework.context.annotation; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.classreading.MetadataReader; @@ -27,16 +28,22 @@ import org.springframework.util.Assert; * class, based on an ASM ClassReader, with support for annotation metadata exposed * through the {@link AnnotatedBeanDefinition} interface. * - *

This class does not load the bean Class early. + *

This class does not load the bean {@code Class} early. * It rather retrieves all relevant metadata from the ".class" file itself, - * parsed with the ASM ClassReader. + * parsed with the ASM ClassReader. It is functionally equivalent to + * {@link AnnotatedGenericBeanDefinition#AnnotatedGenericBeanDefinition(AnnotationMetadata)} + * but distinguishes by type beans that have been scanned vs those that have + * been otherwise registered or detected by other means. * * @author Juergen Hoeller + * @author Chris Beams * @since 2.5 * @see #getMetadata() * @see #getBeanClassName() * @see org.springframework.core.type.classreading.MetadataReaderFactory + * @see AnnotatedGenericBeanDefinition */ +@SuppressWarnings("serial") public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition { private final AnnotationMetadata metadata; diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AsmCircularImportDetectionTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AsmCircularImportDetectionTests.java index 17b232da59..36a9b1eed8 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/AsmCircularImportDetectionTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/AsmCircularImportDetectionTests.java @@ -40,6 +40,7 @@ public class AsmCircularImportDetectionTests extends AbstractCircularImportDetec new FailFastProblemReporter(), new StandardEnvironment(), new DefaultResourceLoader(), + new AnnotationBeanNameGenerator(), new DefaultListableBeanFactory()); } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ImportAwareTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ImportAwareTests.java index 180176452b..822923d9c1 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ImportAwareTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ImportAwareTests.java @@ -16,11 +16,6 @@ package org.springframework.context.annotation; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; -import static org.springframework.context.annotation.MetadataUtils.attributesFor; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -35,6 +30,10 @@ import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.springframework.context.annotation.MetadataUtils.*; + /** * Tests that an ImportAware @Configuration classes gets injected with the * annotation metadata of the @Configuration class that imported it. diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationBeanNameTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationBeanNameTests.java new file mode 100644 index 0000000000..445397c384 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationBeanNameTests.java @@ -0,0 +1,94 @@ +/* + * Copyright 2002-2012 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.Test; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.AnnotationBeanNameGenerator; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.stereotype.Component; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +/** + * Unit tests ensuring that configuration class bean names as expressed via @Configuration + * or @Component 'value' attributes are indeed respected, and that customization of bean + * naming through a BeanNameGenerator strategy works as expected. + * + * @author Chris Beams + * @since 3.1.1 + */ +public class ConfigurationBeanNameTests { + + @Test + public void registerOuterConfig() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(A.class); + ctx.refresh(); + assertThat(ctx.containsBean("outer"), is(true)); + assertThat(ctx.containsBean("imported"), is(true)); + assertThat(ctx.containsBean("nested"), is(true)); + assertThat(ctx.containsBean("nestedBean"), is(true)); + } + + @Test + public void registerNestedConfig() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(A.B.class); + ctx.refresh(); + assertThat(ctx.containsBean("outer"), is(false)); + assertThat(ctx.containsBean("imported"), is(false)); + assertThat(ctx.containsBean("nested"), is(true)); + assertThat(ctx.containsBean("nestedBean"), is(true)); + } + + @Test + public void registerOuterConfig_withBeanNameGenerator() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setBeanNameGenerator(new AnnotationBeanNameGenerator() { + public String generateBeanName( + BeanDefinition definition, BeanDefinitionRegistry registry) { + return "custom-" + super.generateBeanName(definition, registry); + } + }); + ctx.register(A.class); + ctx.refresh(); + assertThat(ctx.containsBean("custom-outer"), is(true)); + assertThat(ctx.containsBean("custom-imported"), is(true)); + assertThat(ctx.containsBean("custom-nested"), is(true)); + assertThat(ctx.containsBean("nestedBean"), is(true)); + } + + @Configuration("outer") + @Import(C.class) + static class A { + @Component("nested") + static class B { + @Bean public String nestedBean() { return ""; } + } + } + + @Configuration("imported") + static class C { + @Bean public String s() { return "s"; } + } +} diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationMetaAnnotationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationMetaAnnotationTests.java new file mode 100644 index 0000000000..c556e86898 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationMetaAnnotationTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2012 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 java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import test.beans.TestBean; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +/** + * Ensures that @Configuration is supported properly as a meta-annotation. + * + * @author Chris Beams + */ +public class ConfigurationMetaAnnotationTests { + + @Test + public void customConfigurationStereotype() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(Config.class); + ctx.refresh(); + assertThat(ctx.containsBean("customName"), is(true)); + TestBean a = ctx.getBean("a", TestBean.class); + TestBean b = ctx.getBean("b", TestBean.class); + assertThat(b, sameInstance(a.getSpouse())); + } + + + @TestConfiguration("customName") + static class Config { + @Bean + public TestBean a() { + TestBean a = new TestBean(); + a.setSpouse(b()); + return a; + } + + @Bean + public TestBean b() { + return new TestBean(); + } + } + + + @Configuration + @Retention(RetentionPolicy.RUNTIME) + public @interface TestConfiguration { + String value() default ""; + } +} diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/spr8955/Spr8955Parent.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/spr8955/Spr8955Parent.java new file mode 100644 index 0000000000..21a1b007df --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/spr8955/Spr8955Parent.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2012 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.spr8955; + +import org.springframework.stereotype.Component; + +/** + * @author Chris Beams + * @author Willem Dekker + */ +abstract class Spr8955Parent { + + @Component + static class Spr8955Child extends Spr8955Parent { + + } + +} diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/spr8955/Spr8955Tests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/spr8955/Spr8955Tests.java new file mode 100644 index 0000000000..c8b6fa4dea --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/spr8955/Spr8955Tests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2012 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.spr8955; + +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + + +/** + * @author Chris Beams + * @author Willem Dekker + */ +public class Spr8955Tests { + + @Test + public void repro() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.scan("org.springframework.context.annotation.configuration.spr8955"); + ctx.refresh(); + } + +} diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java index f5c5f18034..e1f796847a 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java @@ -95,7 +95,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { else { // Try a URL connection content-length header... URLConnection con = url.openConnection(); - con.setUseCaches(false); + ResourceUtils.useCachesIfNecessary(con); HttpURLConnection httpCon = (con instanceof HttpURLConnection ? (HttpURLConnection) con : null); if (httpCon != null) { @@ -152,12 +152,12 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { URL url = getURL(); if (ResourceUtils.isFileURL(url)) { // Proceed with file system resolution... - return super.contentLength(); + return getFile().length(); } else { // Try a URL connection content-length header... URLConnection con = url.openConnection(); - con.setUseCaches(false); + ResourceUtils.useCachesIfNecessary(con); if (con instanceof HttpURLConnection) { ((HttpURLConnection) con).setRequestMethod("HEAD"); } @@ -175,7 +175,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { else { // Try a URL connection last-modified header... URLConnection con = url.openConnection(); - con.setUseCaches(false); + ResourceUtils.useCachesIfNecessary(con); if (con instanceof HttpURLConnection) { ((HttpURLConnection) con).setRequestMethod("HEAD"); } diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java index 2f6c8e7d7d..1d48f7fb33 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java @@ -108,12 +108,28 @@ public abstract class AbstractResource implements Resource { } /** - * This implementation checks the length of the underlying File, - * if available. - * @see #getFile() + * This implementation reads the entire InputStream to calculate the + * content length. Subclasses will almost always be able to provide + * a more optimal version of this, e.g. checking a File length. + * @see #getInputStream() */ public long contentLength() throws IOException { - return getFile().length(); + InputStream is = getInputStream(); + try { + long size = 0; + byte[] buf = new byte[255]; + for (int read = is.read(buf); read != -1;) { + size += read; + } + return size; + } + finally { + try { + is.close(); + } + catch (IOException ex) { + } + } } /** diff --git a/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java b/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java index d96ba7c75b..3d0805c345 100644 --- a/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -139,6 +139,14 @@ public class FileSystemResource extends AbstractResource implements WritableReso return this.file; } + /** + * This implementation returns the underlying File's length. + */ + @Override + public long contentLength() throws IOException { + return this.file.length(); + } + /** * This implementation creates a FileSystemResource, applying the given path * relative to the path of the underlying file of this resource descriptor. diff --git a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java index a68e919c66..2f9f162d78 100644 --- a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2012 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. @@ -26,6 +26,7 @@ import java.net.URL; import java.net.URLConnection; import org.springframework.util.Assert; +import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; /** @@ -119,7 +120,7 @@ public class UrlResource extends AbstractFileResolvingResource { */ public InputStream getInputStream() throws IOException { URLConnection con = this.url.openConnection(); - con.setUseCaches(false); + ResourceUtils.useCachesIfNecessary(con); try { return con.getInputStream(); } diff --git a/spring-core/src/main/java/org/springframework/core/io/VfsResource.java b/spring-core/src/main/java/org/springframework/core/io/VfsResource.java index c161858ad6..88925f6b78 100644 --- a/spring-core/src/main/java/org/springframework/core/io/VfsResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/VfsResource.java @@ -86,6 +86,11 @@ public class VfsResource extends AbstractResource { return VfsUtils.getFile(this.resource); } + @Override + public long contentLength() throws IOException { + return VfsUtils.getSize(this.resource); + } + @Override public long lastModified() throws IOException { return VfsUtils.getLastModified(this.resource); diff --git a/spring-core/src/main/java/org/springframework/core/io/VfsUtils.java b/spring-core/src/main/java/org/springframework/core/io/VfsUtils.java index a0e268ce70..71c2ceb097 100644 --- a/spring-core/src/main/java/org/springframework/core/io/VfsUtils.java +++ b/spring-core/src/main/java/org/springframework/core/io/VfsUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import java.net.URL; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.core.NestedIOException; import org.springframework.util.ReflectionUtils; @@ -58,14 +59,15 @@ public abstract class VfsUtils { private static Method VFS_METHOD_GET_ROOT_URI = null; private static Method VIRTUAL_FILE_METHOD_EXISTS = null; + private static Method VIRTUAL_FILE_METHOD_GET_INPUT_STREAM; private static Method VIRTUAL_FILE_METHOD_GET_SIZE; private static Method VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED; - private static Method VIRTUAL_FILE_METHOD_GET_CHILD; - private static Method VIRTUAL_FILE_METHOD_GET_INPUT_STREAM; private static Method VIRTUAL_FILE_METHOD_TO_URL; private static Method VIRTUAL_FILE_METHOD_TO_URI; private static Method VIRTUAL_FILE_METHOD_GET_NAME; private static Method VIRTUAL_FILE_METHOD_GET_PATH_NAME; + private static Method VIRTUAL_FILE_METHOD_GET_CHILD; + protected static Class VIRTUAL_FILE_VISITOR_INTERFACE; protected static Method VIRTUAL_FILE_METHOD_VISIT; @@ -101,9 +103,10 @@ public abstract class VfsUtils { if (logger.isDebugEnabled()) logger.debug("JBoss VFS packages for JBoss AS 5 found"); - } catch (ClassNotFoundException ex1) { + } + catch (ClassNotFoundException ex2) { logger.error("JBoss VFS packages (for both JBoss AS 5 and 6) were not found - JBoss VFS support disabled"); - throw new IllegalStateException("Cannot detect JBoss VFS packages", ex1); + throw new IllegalStateException("Cannot detect JBoss VFS packages", ex2); } } @@ -117,8 +120,8 @@ public abstract class VfsUtils { Class virtualFile = loader.loadClass(pkg + "VirtualFile"); VIRTUAL_FILE_METHOD_EXISTS = ReflectionUtils.findMethod(virtualFile, "exists"); - VIRTUAL_FILE_METHOD_GET_SIZE = ReflectionUtils.findMethod(virtualFile, "getSize"); VIRTUAL_FILE_METHOD_GET_INPUT_STREAM = ReflectionUtils.findMethod(virtualFile, "openStream"); + VIRTUAL_FILE_METHOD_GET_SIZE = ReflectionUtils.findMethod(virtualFile, "getSize"); VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED = ReflectionUtils.findMethod(virtualFile, "getLastModified"); VIRTUAL_FILE_METHOD_TO_URI = ReflectionUtils.findMethod(virtualFile, "toURI"); VIRTUAL_FILE_METHOD_TO_URL = ReflectionUtils.findMethod(virtualFile, "toURL"); @@ -183,6 +186,10 @@ public abstract class VfsUtils { } } + static long getSize(Object vfsResource) throws IOException { + return (Long) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_SIZE, vfsResource); + } + static long getLastModified(Object vfsResource) throws IOException { return (Long) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED, vfsResource); } diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java index a9d473da2a..f9a3645a3e 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -433,7 +433,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol if (con instanceof JarURLConnection) { // Should usually be the case for traditional JAR files. JarURLConnection jarCon = (JarURLConnection) con; - jarCon.setUseCaches(false); + ResourceUtils.useCachesIfNecessary(jarCon); jarFile = jarCon.getJarFile(); jarFileUrl = jarCon.getJarFileURL().toExternalForm(); JarEntry jarEntry = jarCon.getJarEntry(); diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java index f52c0f04f7..20b74d017a 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2012 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. @@ -26,6 +26,7 @@ import java.util.Properties; import org.springframework.core.io.Resource; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.ResourceUtils; /** * Convenient utility methods for loading of java.util.Properties, @@ -106,7 +107,7 @@ public abstract class PropertiesLoaderUtils { InputStream is = null; try { URLConnection con = url.openConnection(); - con.setUseCaches(false); + ResourceUtils.useCachesIfNecessary(con); is = con.getInputStream(); properties.load(is); } diff --git a/spring-core/src/main/java/org/springframework/util/ResourceUtils.java b/spring-core/src/main/java/org/springframework/util/ResourceUtils.java index e309719029..b9dc3bd855 100644 --- a/spring-core/src/main/java/org/springframework/util/ResourceUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ResourceUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLConnection; /** * Utility methods for resolving resource locations to files in the @@ -328,4 +329,14 @@ public abstract class ResourceUtils { return new URI(StringUtils.replace(location, " ", "%20")); } + /** + * Set the {@link URLConnection#setUseCaches "useCaches"} flag on the + * given connection, preferring false but leaving the + * flag at true for JNLP based resources. + * @param con the URLConnection to set the flag on + */ + public static void useCachesIfNecessary(URLConnection con) { + con.setUseCaches(con.getClass().getName().startsWith("JNLP")); + } + } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/CustomSQLExceptionTranslatorRegistrar.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/CustomSQLExceptionTranslatorRegistrar.java index e9806b8c0b..0d15a87025 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/CustomSQLExceptionTranslatorRegistrar.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/CustomSQLExceptionTranslatorRegistrar.java @@ -16,53 +16,42 @@ package org.springframework.jdbc.support; -import java.util.Collections; import java.util.HashMap; import java.util.Map; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.springframework.beans.factory.InitializingBean; /** - * Registry for registering custom {@link org.springframework.jdbc.support.SQLExceptionTranslator}. + * Registry for registering custom {@link org.springframework.jdbc.support.SQLExceptionTranslator} + * instances for specific databases. * * @author Thomas Risberg - * @since 3.1 + * @since 3.1.1 */ public class CustomSQLExceptionTranslatorRegistrar implements InitializingBean { - private static final Log logger = LogFactory.getLog(CustomSQLExceptionTranslatorRegistrar.class); - /** * Map registry to hold custom translators specific databases. * Key is the database product name as defined in the * {@link org.springframework.jdbc.support.SQLErrorCodesFactory}. */ - private final Map sqlExceptionTranslators = - new HashMap(); + private final Map translators = new HashMap(); + /** - * Setter for a Map of translators where the key must be the database name as defined in the - * sql-error-codes.xml file. This method is used when this registry is used in an application context. - *

Note that any existing translators will remain unless there is a match in the database name at which - * point the new translator will replace the existing one. - * - * @param sqlExceptionTranslators + * Setter for a Map of {@link SQLExceptionTranslator} references where the key must + * be the database name as defined in the sql-error-codes.xml file. + *

Note that any existing translators will remain unless there is a match in the + * database name, at which point the new translator will replace the existing one. */ - public void setSqlExceptionTranslators(Map sqlExceptionTranslators) { - this.sqlExceptionTranslators.putAll(sqlExceptionTranslators); + public void setTranslators(Map translators) { + this.translators.putAll(translators); } - public void afterPropertiesSet() throws Exception { - if (logger.isDebugEnabled()) { - logger.debug("Registering custom SQL exception translators for database(s): " + - sqlExceptionTranslators.keySet()); - } - for (String dbName : sqlExceptionTranslators.keySet()) { - CustomSQLExceptionTranslatorRegistry.getInstance() - .registerSqlExceptionTranslator(dbName, sqlExceptionTranslators.get(dbName)); + public void afterPropertiesSet() { + for (String dbName : this.translators.keySet()) { + CustomSQLExceptionTranslatorRegistry.getInstance().registerTranslator(dbName, this.translators.get(dbName)); } } + } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/CustomSQLExceptionTranslatorRegistry.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/CustomSQLExceptionTranslatorRegistry.java index 89db6b8902..ccbeb4d723 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/CustomSQLExceptionTranslatorRegistry.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/CustomSQLExceptionTranslatorRegistry.java @@ -16,45 +16,25 @@ package org.springframework.jdbc.support; -import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.WeakHashMap; -import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.util.Assert; -import org.springframework.util.PatternMatchUtils; - /** * Registry for custom {@link org.springframework.jdbc.support.SQLExceptionTranslator} instances associated with * specific databases allowing for overriding translation based on values contained in the configuration file * named "sql-error-codes.xml". * * @author Thomas Risberg - * @since 3.1 + * @since 3.1.1 * @see SQLErrorCodesFactory */ public class CustomSQLExceptionTranslatorRegistry { private static final Log logger = LogFactory.getLog(CustomSQLExceptionTranslatorRegistry.class); - /** - * Map registry to hold custom translators specific databases. - * Key is the database product name as defined in the - * {@link org.springframework.jdbc.support.SQLErrorCodesFactory}. - */ - private final Map sqlExceptionTranslatorRegistry = - new HashMap(); - - /** * Keep track of a single instance so we can return it to classes that request it. */ @@ -70,7 +50,15 @@ public class CustomSQLExceptionTranslatorRegistry { /** - * Create a new instance of the {@link org.springframework.jdbc.support.CustomSQLExceptionTranslatorRegistry} class. + * Map registry to hold custom translators specific databases. + * Key is the database product name as defined in the + * {@link org.springframework.jdbc.support.SQLErrorCodesFactory}. + */ + private final Map translatorMap = new HashMap(); + + + /** + * Create a new instance of the {@link CustomSQLExceptionTranslatorRegistry} class. *

Not public to enforce Singleton design pattern. */ private CustomSQLExceptionTranslatorRegistry() { @@ -78,25 +66,28 @@ public class CustomSQLExceptionTranslatorRegistry { /** * Register a new custom translator for the specified database name. - * * @param dbName the database name - * @param sqlExceptionTranslator the custom translator + * @param translator the custom translator */ - public void registerSqlExceptionTranslator(String dbName, SQLExceptionTranslator sqlExceptionTranslator) { - SQLExceptionTranslator replaced = sqlExceptionTranslatorRegistry.put(dbName, sqlExceptionTranslator); + public void registerTranslator(String dbName, SQLExceptionTranslator translator) { + SQLExceptionTranslator replaced = translatorMap.put(dbName, translator); if (replaced != null) { - logger.warn("Replacing custom translator '" + replaced + - "' for database " + dbName + - " with '" + sqlExceptionTranslator + "'"); + logger.warn("Replacing custom translator [" + replaced + "] for database '" + dbName + + "' with [" + translator + "]"); } else { - logger.info("Adding custom translator '" + sqlExceptionTranslator.getClass().getSimpleName() + - "' for database " + dbName); + logger.info("Adding custom translator of type [" + translator.getClass().getName() + + "] for database '" + dbName + "'"); } } - public SQLExceptionTranslator findSqlExceptionTranslatorForDatabase(String dbName) { - return sqlExceptionTranslatorRegistry.get(dbName); + /** + * Find a custom translator for the specified database. + * @param dbName the database name + * @return the custom translator, or null if none found + */ + public SQLExceptionTranslator findTranslatorForDatabase(String dbName) { + return this.translatorMap.get(dbName); } } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodes.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodes.java index 8dde372bec..c82bbad5bf 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodes.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodes.java @@ -17,7 +17,6 @@ package org.springframework.jdbc.support; import org.springframework.util.StringUtils; -import org.springframework.dao.InvalidDataAccessResourceUsageException; /** * JavaBean for holding JDBC error codes for a particular database. @@ -38,8 +37,6 @@ public class SQLErrorCodes { private boolean useSqlStateForTranslation = false; - private SQLExceptionTranslator customSqlExceptionTranslator = null; - private String[] badSqlGrammarCodes = new String[0]; private String[] invalidResultSetAccessCodes = new String[0]; @@ -62,6 +59,8 @@ public class SQLErrorCodes { private CustomSQLErrorCodesTranslation[] customTranslations; + private SQLExceptionTranslator customSqlExceptionTranslator; + /** * Set this property if the database name contains spaces, @@ -100,31 +99,6 @@ public class SQLErrorCodes { return this.useSqlStateForTranslation; } - public SQLExceptionTranslator getCustomSqlExceptionTranslator() { - return customSqlExceptionTranslator; - } - - public void setCustomSqlExceptionTranslator(SQLExceptionTranslator customSqlExceptionTranslator) { - this.customSqlExceptionTranslator = customSqlExceptionTranslator; - } - - public void setCustomSqlExceptionTranslatorClass(Class customSqlExceptionTranslatorClass) { - if (customSqlExceptionTranslatorClass != null) { - try { - this.customSqlExceptionTranslator = - (SQLExceptionTranslator) customSqlExceptionTranslatorClass.newInstance(); - } - catch (InstantiationException e) { - throw new InvalidDataAccessResourceUsageException( - "Unable to instantiate " + customSqlExceptionTranslatorClass.getName(), e); - } - catch (IllegalAccessException e) { - throw new InvalidDataAccessResourceUsageException( - "Unable to instantiate " + customSqlExceptionTranslatorClass.getName(), e); - } - } - } - public void setBadSqlGrammarCodes(String[] badSqlGrammarCodes) { this.badSqlGrammarCodes = StringUtils.sortStringArray(badSqlGrammarCodes); } @@ -213,4 +187,26 @@ public class SQLErrorCodes { return this.customTranslations; } + public void setCustomSqlExceptionTranslatorClass(Class customTranslatorClass) { + if (customTranslatorClass != null) { + try { + this.customSqlExceptionTranslator = customTranslatorClass.newInstance(); + } + catch (Exception ex) { + throw new IllegalStateException("Unable to instantiate custom translator", ex); + } + } + else { + this.customSqlExceptionTranslator = null; + } + } + + public void setCustomSqlExceptionTranslator(SQLExceptionTranslator customSqlExceptionTranslator) { + this.customSqlExceptionTranslator = customSqlExceptionTranslator; + } + + public SQLExceptionTranslator getCustomSqlExceptionTranslator() { + return this.customSqlExceptionTranslator; + } + } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodesFactory.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodesFactory.java index bdaf16c0e1..a127e82937 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodesFactory.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodesFactory.java @@ -17,7 +17,6 @@ package org.springframework.jdbc.support; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import javax.sql.DataSource; @@ -171,7 +170,7 @@ public class SQLErrorCodesFactory { } } if (sec != null) { - checkSqlExceptionTranslatorRegistry(dbName, sec); + checkCustomTranslatorRegistry(dbName, sec); if (logger.isDebugEnabled()) { logger.debug("SQL error codes for '" + dbName + "' found"); } @@ -248,10 +247,12 @@ public class SQLErrorCodesFactory { } } - private void checkSqlExceptionTranslatorRegistry(String dbName, SQLErrorCodes dbCodes) { - // Check the custom sql exception translator registry for any entries + /** + * Check the {@link CustomSQLExceptionTranslatorRegistry} for any entries. + */ + private void checkCustomTranslatorRegistry(String dbName, SQLErrorCodes dbCodes) { SQLExceptionTranslator customTranslator = - CustomSQLExceptionTranslatorRegistry.getInstance().findSqlExceptionTranslatorForDatabase(dbName); + CustomSQLExceptionTranslatorRegistry.getInstance().findTranslatorForDatabase(dbName); if (customTranslator != null) { if (dbCodes.getCustomSqlExceptionTranslator() != null) { logger.warn("Overriding already defined custom translator '" + @@ -265,7 +266,6 @@ public class SQLErrorCodesFactory { } dbCodes.setCustomSqlExceptionTranslator(customTranslator); } - } } diff --git a/spring-jdbc/src/test/resources/org/springframework/jdbc/support/test-custom-translators-context.xml b/spring-jdbc/src/test/resources/org/springframework/jdbc/support/test-custom-translators-context.xml index df3a6258da..5603f4bd0a 100644 --- a/spring-jdbc/src/test/resources/org/springframework/jdbc/support/test-custom-translators-context.xml +++ b/spring-jdbc/src/test/resources/org/springframework/jdbc/support/test-custom-translators-context.xml @@ -8,7 +8,7 @@ - + diff --git a/spring-jms/src/main/java/org/springframework/jms/connection/JmsTransactionManager.java b/spring-jms/src/main/java/org/springframework/jms/connection/JmsTransactionManager.java index 711e62ac47..aa6e235651 100644 --- a/spring-jms/src/main/java/org/springframework/jms/connection/JmsTransactionManager.java +++ b/spring-jms/src/main/java/org/springframework/jms/connection/JmsTransactionManager.java @@ -87,6 +87,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager * @see TransactionAwareConnectionFactoryProxy * @see org.springframework.jms.core.JmsTemplate */ +@SuppressWarnings("serial") public class JmsTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean { @@ -191,14 +192,6 @@ public class JmsTransactionManager extends AbstractPlatformTransactionManager getConnectionFactory(), txObject.getResourceHolder()); } catch (JMSException ex) { - if (session != null) { - try { - session.close(); - } - catch (Throwable ex2) { - // ignore - } - } if (con != null) { try { con.close(); diff --git a/spring-web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java index 24b35ea763..02a46b68f8 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java @@ -19,6 +19,7 @@ package org.springframework.web.context.support; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; +import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.context.annotation.ScopeMetadataResolver; import org.springframework.util.Assert; @@ -196,6 +197,8 @@ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWe if (beanNameGenerator != null) { reader.setBeanNameGenerator(beanNameGenerator); scanner.setBeanNameGenerator(beanNameGenerator); + beanFactory.registerSingleton( + AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator); } if (scopeMetadataResolver != null) { reader.setScopeMetadataResolver(scopeMetadataResolver); diff --git a/spring-web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java b/spring-web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java index 7f0b27a0d3..d427f7f381 100644 --- a/spring-web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java @@ -19,9 +19,16 @@ package org.springframework.web.context.support; import static org.junit.Assert.assertNotNull; import org.junit.Test; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import static org.hamcrest.CoreMatchers.*; + +import static org.junit.Assert.*; + /** * @author Chris Beams */ @@ -49,8 +56,24 @@ public class AnnotationConfigWebApplicationContextTests { assertNotNull(bean); } + @Test + public void withBeanNameGenerator() { + AnnotationConfigWebApplicationContext ctx = + new AnnotationConfigWebApplicationContext(); + ctx.setBeanNameGenerator(new AnnotationBeanNameGenerator() { + @Override + public String generateBeanName(BeanDefinition definition, + BeanDefinitionRegistry registry) { + return "custom-" + super.generateBeanName(definition, registry); + } + }); + ctx.setConfigLocation(Config.class.getName()); + ctx.refresh(); + assertThat(ctx.containsBean("custom-myConfig"), is(true)); + } - @Configuration + + @Configuration("myConfig") static class Config { @Bean public TestBean myTestBean() { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolver.java index e9344b0027..f1eaa6607d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolver.java @@ -61,8 +61,10 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws IOException { - - mavContainer.setRequestHandled(true); + + if (mavContainer != null) { + mavContainer.setRequestHandled(true); + } HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); Class paramType = parameter.getParameterType(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolverTests.java index 5ea3c893e6..14c37c7ecd 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolverTests.java @@ -70,6 +70,16 @@ public class ServletResponseMethodArgumentResolverTests { assertTrue(mavContainer.isRequestHandled()); } + // SPR-8983 + + public void servletResponseNoMavContainer() throws Exception { + MethodParameter servletResponseParameter = new MethodParameter(method, 0); + assertTrue("ServletResponse not supported", resolver.supportsParameter(servletResponseParameter)); + + Object result = resolver.resolveArgument(servletResponseParameter, null, webRequest, null); + assertSame("Invalid result", servletResponse, result); + } + @Test public void outputStream() throws Exception { MethodParameter outputStreamParameter = new MethodParameter(method, 1); diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 704787cc04..fa14a36ddc 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -3,7 +3,7 @@ SPRING FRAMEWORK CHANGELOG http://www.springsource.org -Changes in version 3.1.1 (2012-02-13) +Changes in version 3.1.1 (2012-02-15) ------------------------------------- * official support for Hibernate 4.0 GA as well as Hibernate 4.1 @@ -13,9 +13,12 @@ Changes in version 3.1.1 (2012-02-13) * @ActiveProfiles mechanism in test context framework works with @ImportResource as well * context:property-placeholder's "file-encoding" attribute value is being applied correctly * clarified Resource's "getFilename" method to return null if resource type does not have a filename +* Resource "contentLength()" implementations work with OSGi bundle resources and JBoss VFS resources +* PathMatchingResourcePatternResolver preserves caching for JNLP (Java Web Start) jar connections * optimized converter lookup in GenericConversionService to avoid contention in JDK proxy check * DataBinder correctly handles ParseException from Formatter for String->String case * CacheNamespaceHandler actually parses cache:annotation-driven's "key-generator" attribute +* introduced CustomSQLExceptionTranslatorRegistry/Registrar for JDBC error code translation * officially deprecated TopLinkJpaDialect in favor of EclipseLink and Spring's EclipseLinkJpaDialect * fixed LocalContainerEntityManagerFactoryBean's "packagesToScan" to avoid additional provider scan * LocalContainerEntityManagerFactoryBean's "persistenceUnitName" applies to "packagesToScan" as well @@ -28,7 +31,7 @@ Changes in version 3.1.1 (2012-02-13) * added "entityInterceptor" property to Hibernate 4 LocalSessionFactoryBean * added "getConfiguration" accessor to Hibernate 4 LocalSessionFactoryBean * added "durability" and "description" properties to JobDetailFactoryBean -* corrected fix for QuartzJobBean to work with Quartz 2.0/2.1 +* fixed QuartzJobBean and MethodInvokingJobDetailFactoryBean for compatibility with Quartz 2.0/2.1 * JMS CachingConnectionFactory never caches consumers for temporary queues and topics * JMS SimpleMessageListenerContainer silently falls back to lazy registration of consumers * Servlet/PortletContextResource's "isReadable()" implementation returns false for directories