Avoid using @Order on @Configuration classes as Spring now honours it

In Spring Framework 4.1, @Order on a @Configuration class had no effect.
This allowed us to use it on auto-configuration classes to control
the ordering of auto-configuration classes without it having any
broader implications for configuration class ordering.

Spring Framework 4.2 now honours @Order on @Configuration classes. This
breaks a number of tests where we were relying on the order that the
classes were passed to register when evaluating various bean conditions.

This commit replaces the use of @Order on auto-configuration classes
with a new annotation, @AutoConfigureOrder. The new annotation is
handled by AutoConfigurationSorter where it’s used to order
auto-configuration classes. This allows us to order auto-configuration
classes without the unwanted side-effect of this also affecting the
general ordering of configuration classes.

See gh-2575
This commit is contained in:
Andy Wilkinson 2015-03-24 13:51:49 +00:00
parent 04c5fc8856
commit 7a73c5883f
10 changed files with 62 additions and 18 deletions

View File

@ -28,7 +28,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
@ -142,7 +141,7 @@ class AutoConfigurationSorter {
public int getOrder() { public int getOrder() {
Map<String, Object> orderedAnnotation = this.metadata Map<String, Object> orderedAnnotation = this.metadata
.getAnnotationAttributes(Order.class.getName()); .getAnnotationAttributes(AutoConfigureOrder.class.getName());
return (orderedAnnotation == null ? Ordered.LOWEST_PRECEDENCE return (orderedAnnotation == null ? Ordered.LOWEST_PRECEDENCE
: (Integer) orderedAnnotation.get("value")); : (Integer) orderedAnnotation.get("value"));
} }

View File

@ -0,0 +1,47 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.boot.autoconfigure;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* Auto-configuration specific variant of Spring Framework's {@link Order} annotation.
* Allows auto-configuration classes to be ordered among themselves without affecting the
* order of configuration classes passed to
* {@link AnnotationConfigApplicationContext#register(Class...)}.
*
* @author Andy Wilkinson
* @since 1.3.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
public @interface AutoConfigureOrder {
/**
* The order value. Default is {@link Ordered#LOWEST_PRECEDENCE}.
* @see Ordered#getOrder()
*/
int value() default Ordered.LOWEST_PRECEDENCE;
}

View File

@ -33,7 +33,6 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
@ -51,7 +50,7 @@ import static org.springframework.util.StringUtils.trimAllWhitespace;
*/ */
@Configuration @Configuration
@ConditionalOnMissingBean(MessageSource.class) @ConditionalOnMissingBean(MessageSource.class)
@Order(Ordered.HIGHEST_PRECEDENCE) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class) @Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties @EnableConfigurationProperties
@ConfigurationProperties(prefix = "spring.messages") @ConfigurationProperties(prefix = "spring.messages")

View File

@ -22,7 +22,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for * {@link EnableAutoConfiguration Auto-configuration} for
@ -32,7 +31,7 @@ import org.springframework.core.annotation.Order;
* @author Dave Syer * @author Dave Syer
*/ */
@Configuration @Configuration
@Order(Ordered.HIGHEST_PRECEDENCE) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class PropertyPlaceholderAutoConfiguration { public class PropertyPlaceholderAutoConfiguration {
@Bean @Bean

View File

@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.cloud; package org.springframework.boot.autoconfigure.cloud;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -28,7 +29,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Cloud. * {@link EnableAutoConfiguration Auto-configuration} for Spring Cloud.
@ -47,7 +47,7 @@ import org.springframework.core.annotation.Order;
*/ */
@Configuration @Configuration
@Profile("cloud") @Profile("cloud")
@Order(CloudAutoConfiguration.ORDER) @AutoConfigureOrder(CloudAutoConfiguration.ORDER)
@ConditionalOnClass(CloudScanConfiguration.class) @ConditionalOnClass(CloudScanConfiguration.class)
@ConditionalOnMissingBean(Cloud.class) @ConditionalOnMissingBean(Cloud.class)
@ConditionalOnProperty(prefix = "spring.cloud", name = "enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnProperty(prefix = "spring.cloud", name = "enabled", havingValue = "true", matchIfMissing = true)

View File

@ -33,6 +33,7 @@ import org.glassfish.jersey.servlet.ServletProperties;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -48,7 +49,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.web.WebApplicationInitializer; import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.filter.RequestContextFilter; import org.springframework.web.filter.RequestContextFilter;
@ -64,7 +64,7 @@ import org.springframework.web.filter.RequestContextFilter;
"javax.servlet.ServletRegistration" }) "javax.servlet.ServletRegistration" })
@ConditionalOnBean(type = "org.glassfish.jersey.server.ResourceConfig") @ConditionalOnBean(type = "org.glassfish.jersey.server.ResourceConfig")
@ConditionalOnWebApplication @ConditionalOnWebApplication
@Order(Ordered.HIGHEST_PRECEDENCE) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@AutoConfigureBefore(DispatcherServletAutoConfiguration.class) @AutoConfigureBefore(DispatcherServletAutoConfiguration.class)
@EnableConfigurationProperties(JerseyProperties.class) @EnableConfigurationProperties(JerseyProperties.class)
public class JerseyAutoConfiguration implements WebApplicationInitializer { public class JerseyAutoConfiguration implements WebApplicationInitializer {

View File

@ -25,6 +25,7 @@ import javax.servlet.ServletRegistration;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@ -53,7 +54,7 @@ import org.springframework.web.servlet.DispatcherServlet;
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer * @author Dave Syer
*/ */
@Order(Ordered.HIGHEST_PRECEDENCE) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration @Configuration
@ConditionalOnWebApplication @ConditionalOnWebApplication
@ConditionalOnClass(DispatcherServlet.class) @ConditionalOnClass(DispatcherServlet.class)

View File

@ -29,6 +29,7 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -45,7 +46,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.xnio.SslClientAuthMode; import org.xnio.SslClientAuthMode;
@ -57,7 +57,7 @@ import org.xnio.SslClientAuthMode;
* @author Dave Syer * @author Dave Syer
* @author Ivan Sopov * @author Ivan Sopov
*/ */
@Order(Ordered.HIGHEST_PRECEDENCE) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration @Configuration
@ConditionalOnWebApplication @ConditionalOnWebApplication
@Import(EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar.class) @Import(EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar.class)

View File

@ -32,6 +32,7 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -45,7 +46,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
@ -92,7 +92,7 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver;
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, @ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
WebMvcConfigurerAdapter.class }) WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@Order(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter(DispatcherServletAutoConfiguration.class) @AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
public class WebMvcAutoConfiguration { public class WebMvcAutoConfiguration {

View File

@ -28,7 +28,6 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.DefaultResourceLoader;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -157,11 +156,11 @@ public class AutoConfigurationSorterTests {
} }
@Order(Ordered.LOWEST_PRECEDENCE) @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public static class OrderLowest { public static class OrderLowest {
} }
@Order(Ordered.HIGHEST_PRECEDENCE) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public static class OrderHighest { public static class OrderHighest {
} }