Inspect bean definition more thoroughly for factory arguments

Fixes gh-153
This commit is contained in:
Dave Syer
2018-03-02 11:28:33 +00:00
parent dc008cc24d
commit 5528659b4b
4 changed files with 117 additions and 5 deletions

View File

@@ -16,7 +16,7 @@ The sample app creates the shaded jar file, with an `azure` classifier for deplo
=== JSON Configuration
The Azure tooling needs to find some JSON configuration files to tell it how to deploy and intergrate the function (e.g. which Java class to use as the entry point, and which triggers to use). Those files can be created with the Maven plugin for a non-Spring function, but the tooling doesn't work yet with the adapter in its current form. There is an example `function.json` in the sample which hooks the function up as an HTTP endpoint:
The Azure tooling needs to find some JSON configuration files to tell it how to deploy and integrate the function (e.g. which Java class to use as the entry point, and which triggers to use). Those files can be created with the Maven plugin for a non-Spring function, but the tooling doesn't work yet with the adapter in its current form. There is an example `function.json` in the sample which hooks the function up as an HTTP endpoint:
```
{

View File

@@ -20,6 +20,8 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -42,6 +44,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.function.context.FunctionCatalog;
import org.springframework.cloud.function.context.FunctionRegistration;
@@ -553,15 +556,48 @@ public class ContextFunctionCatalogAutoConfiguration {
MethodMetadataReadingVisitor visitor) {
Class<?> factory = ClassUtils
.resolveClassName(visitor.getDeclaringClassName(), null);
Class<?>[] params = getParamTypes(factory, definition);
Method method = ReflectionUtils.findMethod(factory, visitor.getMethodName(),
params);
Type type = method.getGenericReturnType();
return type;
}
private Method[] getCandidateMethods(final Class<?> factoryClass,
final RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
@Override
public Method[] run() {
return (mbd.isNonPublicAccessAllowed()
? ReflectionUtils.getAllDeclaredMethods(factoryClass)
: factoryClass.getMethods());
}
});
}
else {
return (mbd.isNonPublicAccessAllowed()
? ReflectionUtils.getAllDeclaredMethods(factoryClass)
: factoryClass.getMethods());
}
}
private Class<?>[] getParamTypes(Class<?> factory,
AbstractBeanDefinition definition) {
if (definition instanceof RootBeanDefinition) {
RootBeanDefinition root = (RootBeanDefinition) definition;
for (Method method : getCandidateMethods(factory, root)) {
if (root.isFactoryMethod(method)) {
return method.getParameterTypes();
}
}
}
List<Class<?>> params = new ArrayList<>();
for (ValueHolder holder : definition.getConstructorArgumentValues()
.getIndexedArgumentValues().values()) {
params.add(ClassUtils.resolveClassName(holder.getType(), null));
}
Method method = ReflectionUtils.findMethod(factory, visitor.getMethodName(),
params.toArray(new Class<?>[0]));
Type type = method.getGenericReturnType();
return type;
return params.toArray(new Class<?>[0]);
}
private Object getField(Object target, String name) {

View File

@@ -46,6 +46,7 @@ import org.springframework.cloud.function.context.FunctionCatalog;
import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.context.FunctionScan;
import org.springframework.cloud.function.context.catalog.FunctionInspector;
import org.springframework.cloud.function.inject.FooConfiguration;
import org.springframework.cloud.function.scan.ScannedFunction;
import org.springframework.cloud.function.test.GenericFunction;
import org.springframework.context.ConfigurableApplicationContext;
@@ -120,7 +121,26 @@ public class ContextFunctionCatalogAutoConfigurationTests {
.isEqualTo(String.class);
assertThat(inspector.getInputType(catalog.lookup(Consumer.class, "foos")))
.isEqualTo(Foo.class);
}
@Test
public void dependencyInjection() {
create(DependencyInjectionConfiguration.class);
assertThat(context.getBean("foos")).isInstanceOf(Function.class);
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "foos"))
.isInstanceOf(Function.class);
assertThat(inspector.getInputType(catalog.lookup(Function.class, "foos")))
.isEqualTo(String.class);
}
@Test
public void externalDependencyInjection() {
create(ExternalDependencyConfiguration.class);
assertThat(context.getBean("foos")).isInstanceOf(Function.class);
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "foos"))
.isInstanceOf(Function.class);
assertThat(inspector.getInputType(catalog.lookup(Function.class, "foos")))
.isEqualTo(String.class);
}
@Test
@@ -523,6 +543,32 @@ public class ContextFunctionCatalogAutoConfigurationTests {
}
}
@EnableAutoConfiguration
@Configuration
protected static class DependencyInjectionConfiguration {
@Bean
public Function<String, Foo> foos(String foo) {
return value -> new Foo(foo + ": " + value.toUpperCase());
}
@Bean
public String value() {
return "Hello";
}
}
@EnableAutoConfiguration
@Configuration
@ComponentScan(basePackageClasses = FooConfiguration.class)
protected static class ExternalDependencyConfiguration {
@Bean
public String value() {
return "Hello";
}
}
@EnableAutoConfiguration
@Configuration
protected static class AmbiguousConfiguration {

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2016-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.
* 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.cloud.function.inject;
import java.util.function.Function;
import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfigurationTests.Foo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FooConfiguration {
@Bean
public Function<String, Foo> foos(String foo) {
return value -> new Foo(foo + ": " + value.toUpperCase());
}
}