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:
Artem Bilan
2018-01-11 20:04:46 -05:00
committed by Oleg Zhurakousky
parent b0356bdea9
commit 8513e80c2a
3 changed files with 83 additions and 30 deletions

View File

@@ -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 {
}
};
}
}

View File

@@ -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

View File

@@ -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);
}
}