Replace propAccessorInjector with the SpelPAReg
After changing the `propertyAccessorInjector` `BeanPostProcessor` just to a simple `InitializingBean` there is no guarantee which bean will be accessed from the autowire phase. There are some cases like `@PostConstruct` on the `@Configuration` wich may be called before this new `propertyAccessorInjector` bean and we end up with `EvaluationContext` not supplied with additional `PropertyAccessors` * For proper `IntegrationEvaluationContextFactoryBean` initialization add `SpelPropertyAccessorRegistrar` for custom `PA` configuration * Remove `propertyAccessorInjector` bean altogether * Resolve deprecation in the `BindingServiceConfiguration` * Modify `SpelExpressionConverterConfigurationTests` to confirm that even `@PostConstruct` on `@Configuration` works as expected Resolves #1178
This commit is contained in:
committed by
Oleg Zhurakousky
parent
b0356bdea9
commit
8513e80c2a
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.beans.Introspector;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -32,7 +31,6 @@ import org.springframework.cloud.stream.binder.BinderFactory;
|
||||
import org.springframework.cloud.stream.binding.AbstractBindingTargetFactory;
|
||||
import org.springframework.cloud.stream.binding.Bindable;
|
||||
import org.springframework.cloud.stream.binding.BinderAwareChannelResolver;
|
||||
import org.springframework.cloud.stream.binding.BinderAwareRouterBeanPostProcessor;
|
||||
import org.springframework.cloud.stream.binding.BindingService;
|
||||
import org.springframework.cloud.stream.binding.CompositeMessageChannelConfigurer;
|
||||
import org.springframework.cloud.stream.binding.ContextStartAfterRefreshListener;
|
||||
@@ -51,15 +49,12 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.PublishSubscribeChannel;
|
||||
import org.springframework.integration.config.HandlerMethodArgumentResolversHolder;
|
||||
import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.BridgeHandler;
|
||||
import org.springframework.integration.json.JsonPropertyAccessor;
|
||||
import org.springframework.integration.router.AbstractMappingMessageRouter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -68,7 +63,6 @@ import org.springframework.messaging.core.DestinationResolver;
|
||||
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
|
||||
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.tuple.spel.TuplePropertyAccessor;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
@@ -83,7 +77,6 @@ import org.springframework.util.CollectionUtils;
|
||||
* @author Artem Bilan
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({ BindingServiceProperties.class, SpringIntegrationProperties.class })
|
||||
@Import(ContentTypeConfiguration.class)
|
||||
@@ -216,27 +209,12 @@ public class BindingServiceConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public BinderAwareRouterBeanPostProcessor binderAwareRouterBeanPostProcessor(@Autowired(required=false) AbstractMappingMessageRouter[] routers,
|
||||
@SuppressWarnings("deprecation")
|
||||
public org.springframework.cloud.stream.binding.BinderAwareRouterBeanPostProcessor binderAwareRouterBeanPostProcessor(
|
||||
@Autowired(required=false) AbstractMappingMessageRouter[] routers,
|
||||
@Autowired(required=false)DestinationResolver<MessageChannel> channelResolver) {
|
||||
return new BinderAwareRouterBeanPostProcessor(routers, channelResolver);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public InitializingBean propertyAccessorInjector(IntegrationEvaluationContextFactoryBean[] iecfbs) {
|
||||
return new InitializingBean() {
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
TuplePropertyAccessor tpa = new TuplePropertyAccessor();
|
||||
JsonPropertyAccessor jpa = new JsonPropertyAccessor();
|
||||
if (iecfbs != null) {
|
||||
for (IntegrationEvaluationContextFactoryBean iecfb : iecfbs) {
|
||||
Map<String, PropertyAccessor> factoryBeanAccessors = iecfb.getPropertyAccessors();
|
||||
factoryBeanAccessors.put(Introspector.decapitalize(tpa.getClass().getSimpleName()), tpa);
|
||||
factoryBeanAccessors.put(Introspector.decapitalize(jpa.getClass().getSimpleName()), jpa);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return new org.springframework.cloud.stream.binding.BinderAwareRouterBeanPostProcessor(routers, channelResolver);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -254,4 +232,5 @@ public class BindingServiceConfiguration {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.beans.Introspector;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
@@ -32,16 +34,36 @@ import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.config.IntegrationConverter;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.expression.SpelPropertyAccessorRegistrar;
|
||||
import org.springframework.integration.json.JsonPropertyAccessor;
|
||||
import org.springframework.tuple.spel.TuplePropertyAccessor;
|
||||
|
||||
/**
|
||||
* Adds a Converter from String to SpEL Expression in the context.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@Configuration
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public class SpelExpressionConverterConfiguration {
|
||||
|
||||
/**
|
||||
* Provide a {@link SpelPropertyAccessorRegistrar} supplied
|
||||
* with the {@link JsonPropertyAccessor} and {@link TuplePropertyAccessor}.
|
||||
* This bean is used to customize an
|
||||
* {@link org.springframework.integration.config.IntegrationEvaluationContextFactoryBean}.
|
||||
* for additional {@link org.springframework.expression.PropertyAccessor}s.
|
||||
* @return the SpelPropertyAccessorRegistrar bean
|
||||
* @see org.springframework.integration.config.IntegrationEvaluationContextFactoryBean
|
||||
*/
|
||||
@Bean
|
||||
public static SpelPropertyAccessorRegistrar spelPropertyAccessorRegistrar() {
|
||||
return new SpelPropertyAccessorRegistrar()
|
||||
.add(Introspector.decapitalize(JsonPropertyAccessor.class.getSimpleName()), new JsonPropertyAccessor())
|
||||
.add(Introspector.decapitalize(TuplePropertyAccessor.class.getSimpleName()), new TuplePropertyAccessor());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationPropertiesBinding
|
||||
@IntegrationConverter
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,16 @@
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@@ -26,9 +33,15 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.integration.json.JsonPropertyAccessor;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -37,6 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Tests for SpelExpressionConverterConfiguration.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = SpelExpressionConverterConfigurationTests.Config.class, properties = "expression: a.b")
|
||||
@@ -45,9 +59,28 @@ public class SpelExpressionConverterConfigurationTests {
|
||||
@Autowired
|
||||
private Pojo pojo;
|
||||
|
||||
@Autowired
|
||||
private EvaluationContext evaluationContext;
|
||||
|
||||
@Autowired
|
||||
private Config config;
|
||||
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void converterCorrectlyInstalled() {
|
||||
assertThat(pojo.getExpression().getValue("{\"a\": {\"b\": 5}}").toString()).isEqualTo("5");
|
||||
Expression expression = pojo.getExpression();
|
||||
assertThat(expression.getValue("{\"a\": {\"b\": 5}}").toString()).isEqualTo("5");
|
||||
|
||||
List<PropertyAccessor> propertyAccessors =
|
||||
TestUtils.getPropertyValue(this.evaluationContext, "propertyAccessors", List.class);
|
||||
|
||||
assertThat(propertyAccessors).hasAtLeastOneElementOfType(JsonPropertyAccessor.class);
|
||||
|
||||
propertyAccessors =
|
||||
TestUtils.getPropertyValue(this.config.evaluationContext, "propertyAccessors", List.class);
|
||||
|
||||
assertThat(propertyAccessors).hasAtLeastOneElementOfType(JsonPropertyAccessor.class);
|
||||
}
|
||||
|
||||
@ConfigurationProperties
|
||||
@@ -69,7 +102,26 @@ public class SpelExpressionConverterConfigurationTests {
|
||||
@EnableAutoConfiguration
|
||||
@Import(MockBinderRegistryConfiguration.class)
|
||||
@EnableConfigurationProperties(Pojo.class)
|
||||
public static class Config {
|
||||
public static class Config implements BeanFactoryAware {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private EvaluationContext evaluationContext;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EvaluationContext evaluationContext() {
|
||||
return ExpressionUtils.createStandardEvaluationContext(this.beanFactory);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void setup() {
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.beanFactory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user