Migrate type computation logic to FunctionType

This commit is contained in:
Dave Syer
2018-02-26 10:20:14 +00:00
parent 38f6caf4dc
commit d95ab8f173
2 changed files with 82 additions and 171 deletions

View File

@@ -17,8 +17,10 @@ package org.springframework.cloud.function.context;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.function.Function;
import org.springframework.cloud.function.context.catalog.FunctionInspector;
import org.springframework.core.ResolvableType;
import org.springframework.messaging.Message;
import reactor.core.publisher.Flux;
@@ -29,6 +31,9 @@ import reactor.core.publisher.Flux;
*/
public class FunctionType {
public static FunctionType UNCLASSIFIED = new FunctionType(ResolvableType
.forClassWithGenerics(Function.class, Object.class, Object.class).getType());
private Type type;
public FunctionType(Type type) {
@@ -60,6 +65,36 @@ public class FunctionType {
|| Message.class.isAssignableFrom(outputType);
}
public static FunctionType compose(FunctionType input, FunctionType output) {
ResolvableType inputGeneric;
ResolvableType inputType = ResolvableType.forClass(input.getInputType());
if (input.isMessage()) {
inputType = ResolvableType.forClassWithGenerics(Message.class, inputType);
}
ResolvableType outputGeneric;
ResolvableType outputType = ResolvableType.forClass(output.getOutputType());
if (output.isMessage()) {
outputType = ResolvableType.forClassWithGenerics(Message.class, outputType);
}
if (FunctionInspector.isWrapper(input.getInputWrapper())) {
inputGeneric = ResolvableType.forClassWithGenerics(input.getInputWrapper(),
inputType);
}
else {
inputGeneric = inputType;
}
if (FunctionInspector.isWrapper(output.getInputWrapper())) {
outputGeneric = ResolvableType.forClassWithGenerics(output.getInputWrapper(),
outputType);
}
else {
outputGeneric = outputType;
}
return new FunctionType(ResolvableType
.forClassWithGenerics(Function.class, inputGeneric, outputGeneric)
.getType());
}
private Class<?> findType(ParamType paramType) {
int index = paramType.isOutput() ? 1 : 0;
Type type = this.type;

View File

@@ -46,6 +46,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.context.FunctionRegistry;
import org.springframework.cloud.function.context.FunctionScan;
import org.springframework.cloud.function.context.FunctionType;
import org.springframework.cloud.function.context.catalog.FunctionInspector;
import org.springframework.cloud.function.context.catalog.FunctionRegistrationEvent;
import org.springframework.cloud.function.context.catalog.FunctionUnregistrationEvent;
@@ -68,14 +69,11 @@ import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.io.Resource;
import org.springframework.core.type.StandardMethodMetadata;
import org.springframework.core.type.classreading.MethodMetadataReadingVisitor;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
/**
* @author Dave Syer
* @author Mark Fisher
@@ -176,22 +174,22 @@ public class ContextFunctionCatalogAutoConfiguration {
@Override
public Class<?> getInputWrapper(Object function) {
return processor.findType(function, ParamType.INPUT_WRAPPER);
return processor.findType(function).getInputWrapper();
}
@Override
public Class<?> getOutputWrapper(Object function) {
return processor.findType(function, ParamType.OUTPUT_WRAPPER);
return processor.findType(function).getOutputWrapper();
}
@Override
public Class<?> getInputType(Object function) {
return processor.findType(function, ParamType.INPUT);
return processor.findType(function).getInputType();
}
@Override
public Class<?> getOutputType(Object function) {
return processor.findType(function, ParamType.OUTPUT);
return processor.findType(function).getOutputType();
}
@Override
@@ -217,6 +215,7 @@ public class ContextFunctionCatalogAutoConfiguration {
@Autowired(required = false)
private ApplicationEventPublisher publisher;
@Autowired
private ConfigurableListableBeanFactory registry;
@@ -224,7 +223,7 @@ public class ContextFunctionCatalogAutoConfiguration {
private Map<Object, String> registrations = new HashMap<>();
private Map<String, Map<ParamType, Class<?>>> types = new HashMap<>();
private Map<String, FunctionType> types = new HashMap<>();
public Set<String> getSuppliers() {
return this.suppliers.keySet();
@@ -295,16 +294,12 @@ public class ContextFunctionCatalogAutoConfiguration {
}
final Object value = function;
lookup.computeIfAbsent(name, key -> value);
Map<ParamType, Class<?>> values = types.computeIfAbsent(name,
key -> new HashMap<>());
for (ParamType type : ParamType.values()) {
if (!values.containsKey(type)) {
if (type.isInput()) {
values.put(type, types.get(stages[0]).get(type));
}
else {
values.put(type, types.get(stages[stages.length - 1]).get(type));
}
if (!types.containsKey(name)) {
if (types.containsKey(stages[0])
&& types.containsKey(stages[stages.length - 1])) {
FunctionType input = types.get(stages[0]);
FunctionType output = types.get(stages[stages.length - 1]);
types.put(name, FunctionType.compose(input, output));
}
}
registrations.put(function, name);
@@ -314,13 +309,7 @@ public class ContextFunctionCatalogAutoConfiguration {
private Object lookup(String name, Map<String, Object> lookup) {
Object result = lookup.get(name);
if (result != null) {
Map<ParamType, Class<?>> values = types.computeIfAbsent(name,
key -> new HashMap<>());
for (ParamType type : ParamType.values()) {
if (!values.containsKey(type)) {
values.put(type, findType(result, type));
}
}
findType(result);
}
return result;
}
@@ -431,7 +420,7 @@ public class ContextFunctionCatalogAutoConfiguration {
this.conversionService = conversionService != null ? conversionService
: new DefaultConversionService();
}
Class<?> type = findType(function, ParamType.INPUT);
Class<?> type = findType(function).getInputType();
return conversionService.canConvert(String.class, type)
? conversionService.convert(value, type)
: value;
@@ -453,9 +442,7 @@ public class ContextFunctionCatalogAutoConfiguration {
Class<?> type;
if (target instanceof Supplier) {
type = Supplier.class;
findType(target, ParamType.OUTPUT);
findType(target, ParamType.OUTPUT_WRAPPER);
isMessage(target);
findType(target);
registration.target(target((Supplier<?>) target, key));
for (String name : registration.getNames()) {
this.suppliers.put(name, registration.getTarget());
@@ -463,9 +450,7 @@ public class ContextFunctionCatalogAutoConfiguration {
}
else if (target instanceof Consumer) {
type = Consumer.class;
findType(target, ParamType.INPUT);
findType(target, ParamType.INPUT_WRAPPER);
isMessage(target); // cache wrapper types
findType(target);
registration.target(target((Consumer<?>) target, key));
for (String name : registration.getNames()) {
this.consumers.put(name, registration.getTarget());
@@ -473,11 +458,7 @@ public class ContextFunctionCatalogAutoConfiguration {
}
else if (target instanceof Function) {
type = Function.class;
findType(target, ParamType.INPUT);
findType(target, ParamType.OUTPUT);
findType(target, ParamType.INPUT_WRAPPER);
findType(target, ParamType.OUTPUT_WRAPPER);
isMessage(target); // cache wrapper types
findType(target);
registration.target(target((Function<?, ?>) target, key));
for (String name : registration.getNames()) {
this.functions.put(name, registration.getTarget());
@@ -560,92 +541,55 @@ public class ContextFunctionCatalogAutoConfiguration {
}
private boolean hasFluxTypes(Object function) {
return FunctionInspector
.isWrapper(findType(function, ParamType.INPUT_WRAPPER))
|| FunctionInspector
.isWrapper(findType(function, ParamType.OUTPUT_WRAPPER));
return FunctionInspector.isWrapper(findType(function).getInputWrapper())
|| FunctionInspector.isWrapper(findType(function).getOutputWrapper());
}
private Class<?> findType(String name, AbstractBeanDefinition definition,
ParamType paramType) {
private FunctionType findType(String name, AbstractBeanDefinition definition) {
Object source = definition.getSource();
Type param = null;
FunctionType param = null;
// Start by assuming output -> Function
int index = paramType.isOutput() ? 1 : 0;
if (source instanceof StandardMethodMetadata) {
// Standard @Bean metadata
Type beanType = ((StandardMethodMetadata) source).getIntrospectedMethod()
.getGenericReturnType();
if (beanType instanceof ParameterizedType) {
ParameterizedType type = (ParameterizedType) beanType;
param = extractType(type, paramType, index);
param = new FunctionType(type);
}
else {
param = findTypeFromBeanClass((Class<?>) beanType, paramType);
param = new FunctionType(beanType);
}
}
else if (source instanceof MethodMetadataReadingVisitor) {
// A component scan with @Beans
MethodMetadataReadingVisitor visitor = (MethodMetadataReadingVisitor) source;
Type type = findBeanType(definition, visitor);
param = extractType(type, paramType, index);
param = new FunctionType(type);
}
else if (source instanceof Resource) {
Class<?> beanType = this.registry.getType(name);
param = findTypeFromBeanClass(beanType, paramType);
if (param == null) {
return Object.class;
}
param = new FunctionType(beanType);
}
else {
ResolvableType resolvable = (ResolvableType) getField(definition,
"targetType");
if (resolvable != null) {
param = resolvable.getGeneric(index).getGeneric(0).getType();
param = new FunctionType(resolvable.getType());
}
else {
Object bean = this.registry.getBean(name);
if (bean instanceof FunctionFactoryMetadata) {
FunctionFactoryMetadata<?> factory = (FunctionFactoryMetadata<?>) bean;
Type type = factory.getFactoryMethod().getGenericReturnType();
param = extractType(type, paramType, index);
param = new FunctionType(type);
}
else {
param = new FunctionType(bean.getClass());
}
}
}
return extractClass(name, param, paramType);
}
private Class<?> extractClass(String name, Type param, ParamType paramType) {
if (param instanceof ParameterizedType) {
ParameterizedType concrete = (ParameterizedType) param;
param = concrete.getRawType();
}
if (param == null) {
// Last ditch attempt to guess: Flux<String>
if (paramType.isWrapper()) {
param = Flux.class;
}
else {
param = String.class;
}
}
Class<?> result = param instanceof Class ? (Class<?>) param : null;
if (result != null) {
Map<ParamType, Class<?>> values = types.computeIfAbsent(name,
key -> new HashMap<>());
values.put(paramType, result);
}
return result;
}
private Type findTypeFromBeanClass(Class<?> beanType, ParamType paramType) {
int index = paramType.isOutput() ? 1 : 0;
for (Type type : beanType.getGenericInterfaces()) {
if (type.getTypeName().startsWith("java.util.function")) {
return extractType(type, paramType, index);
}
}
return null;
return param;
}
private Type findBeanType(AbstractBeanDefinition definition,
@@ -663,48 +607,6 @@ public class ContextFunctionCatalogAutoConfiguration {
return type;
}
private Type extractType(Type type, ParamType paramType, int index) {
Type param;
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
if (parameterizedType.getActualTypeArguments().length == 1) {
// There's only one
index = 0;
}
Type typeArgumentAtIndex = parameterizedType
.getActualTypeArguments()[index];
if (typeArgumentAtIndex instanceof ParameterizedType
&& !paramType.isWrapper()) {
if (FunctionInspector.isWrapper(
((ParameterizedType) typeArgumentAtIndex).getRawType())) {
param = ((ParameterizedType) typeArgumentAtIndex)
.getActualTypeArguments()[0];
param = extractNestedType(paramType, param);
}
else {
param = extractNestedType(paramType, typeArgumentAtIndex);
}
}
else {
param = extractNestedType(paramType, typeArgumentAtIndex);
}
}
else {
param = Object.class;
}
return param;
}
private Type extractNestedType(ParamType paramType, Type param) {
if (!paramType.isInnerWrapper()
&& param.getTypeName().startsWith(Message.class.getName())) {
if (param instanceof ParameterizedType) {
param = ((ParameterizedType) param).getActualTypeArguments()[0];
}
}
return param;
}
private Object getField(Object target, String name) {
Field field = ReflectionUtils.findField(target.getClass(), name);
if (field == null) {
@@ -715,58 +617,32 @@ public class ContextFunctionCatalogAutoConfiguration {
}
private boolean isMessage(Object function) {
Class<?> inputType = findType(function, ParamType.INPUT_INNER_WRAPPER);
Class<?> outputType = findType(function, ParamType.OUTPUT_INNER_WRAPPER);
return inputType.getName().startsWith(Message.class.getName())
|| Message.class.isAssignableFrom(inputType)
|| outputType.getName().startsWith(Message.class.getName())
|| Message.class.isAssignableFrom(outputType);
return findType(function).isMessage();
}
private Class<?> findType(Object function, ParamType type) {
private FunctionType findType(Object function) {
String name = registrations.get(function);
if (types.containsKey(name)) {
Map<ParamType, Class<?>> values = types.get(name);
if (values.containsKey(type)) {
return values.get(type);
}
return types.get(name);
}
FunctionType param;
if (name == null || registry == null
|| !registry.containsBeanDefinition(name)) {
if (function != null) {
Type param = findTypeFromBeanClass(function.getClass(), type);
if (param != null) {
Class<?> result = extractClass(name, param, type);
if (result != null) {
return result;
}
}
param = new FunctionType(function.getClass());
}
else {
param = FunctionType.UNCLASSIFIED;
}
return Object.class;
}
return findType(name,
(AbstractBeanDefinition) registry.getBeanDefinition(name), type);
else {
param = findType(name,
(AbstractBeanDefinition) registry.getBeanDefinition(name));
}
types.computeIfAbsent(name, str -> param);
return param;
}
}
enum ParamType {
INPUT, OUTPUT, INPUT_WRAPPER, OUTPUT_WRAPPER, INPUT_INNER_WRAPPER, OUTPUT_INNER_WRAPPER;
public boolean isOutput() {
return this == OUTPUT || this == OUTPUT_WRAPPER
|| this == OUTPUT_INNER_WRAPPER;
}
public boolean isInput() {
return this == INPUT || this == INPUT_WRAPPER || this == INPUT_INNER_WRAPPER;
}
public boolean isWrapper() {
return this == OUTPUT_WRAPPER || this == INPUT_WRAPPER;
}
public boolean isInnerWrapper() {
return this == OUTPUT_INNER_WRAPPER || this == INPUT_INNER_WRAPPER;
}
}
}