Add target caching to BindableProxyFactory

- Prevent new reflection calls to channels
  for Sources, Sinks and Processors, cache bound target
  if it's found and try to use it next time as target
  should not change.
- One test for checking caching.
- Fixes #759
This commit is contained in:
Janne Valkealahti
2017-01-12 08:37:10 +00:00
committed by Marius Bogoevici
parent 5cff7ced90
commit 9983453b77
2 changed files with 58 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 the original author or authors.
* Copyright 2015-2017 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.
@@ -74,6 +74,8 @@ public class BindableProxyFactory implements MethodInterceptor, FactoryBean<Obje
private Map<String, BoundTargetHolder> outputHolders = new HashMap<>();
private final Map<Method, Object> targetCache = new HashMap<>(2);
public BindableProxyFactory(Class<?> type) {
this.type = type;
}
@@ -81,16 +83,27 @@ public class BindableProxyFactory implements MethodInterceptor, FactoryBean<Obje
@Override
public synchronized Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
// try to use cached target
Object boundTarget = targetCache.get(method);
if (boundTarget != null) {
return boundTarget;
}
Input input = AnnotationUtils.findAnnotation(method, Input.class);
if (input != null) {
String name = BindingBeanDefinitionRegistryUtils.getBindingTargetName(input, method);
return this.inputHolders.get(name).getBoundTarget();
boundTarget = this.inputHolders.get(name).getBoundTarget();
targetCache.put(method, boundTarget);
return boundTarget;
}
else {
Output output = AnnotationUtils.findAnnotation(method, Output.class);
if (output != null) {
String name = BindingBeanDefinitionRegistryUtils.getBindingTargetName(output, method);
return this.outputHolders.get(name).getBoundTarget();
boundTarget = this.outputHolders.get(name).getBoundTarget();
targetCache.put(method, boundTarget);
return boundTarget;
}
}
return null;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2017 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,11 +16,14 @@
package org.springframework.cloud.stream.aggregation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
@@ -30,13 +33,16 @@ import org.springframework.cloud.stream.aggregate.AggregateApplicationBuilder.So
import org.springframework.cloud.stream.aggregate.SharedBindingTargetRegistry;
import org.springframework.cloud.stream.aggregate.SharedChannelRegistry;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.binding.BindableProxyFactory;
import org.springframework.cloud.stream.binding.BindingTargetFactory;
import org.springframework.cloud.stream.messaging.Processor;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.MessageChannel;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertTrue;
@@ -331,6 +337,41 @@ public class AggregationTest {
aggregatedApplicationContext.close();
}
@Test
public void testBindableProxyFactoryCaching() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MockBinderRegistryConfiguration.class,
TestSource.class, TestProcessor.class);
Map<String, BindableProxyFactory> factories = context.getBeansOfType(BindableProxyFactory.class);
assertThat(factories).hasSize(2);
Map<String, Source> sources = context.getBeansOfType(Source.class);
assertThat(sources).hasSize(2);
for (Source source : sources.values()) {
source.output();
}
Map<String, Processor> processors = context.getBeansOfType(Processor.class);
assertThat(processors).hasSize(1);
for (Processor processor : processors.values()) {
processor.input();
processor.output();
}
for (BindableProxyFactory factory : factories.values()) {
Field field = ReflectionUtils.findField(BindableProxyFactory.class, "targetCache");
ReflectionUtils.makeAccessible(field);
Map<?, ?> targetCache = (Map<?, ?>) ReflectionUtils.getField(field, factory);
if (factory.getObjectType() == Source.class) {
assertThat(targetCache).hasSize(1);
} else if (factory.getObjectType() == Processor.class) {
assertThat(targetCache).hasSize(2);
} else {
Assert.fail("Found unexpected type");
}
}
context.close();
}
@EnableBinding(Source.class)
@EnableAutoConfiguration
public static class TestSource {