[3.0] Optimize Converter to cache (#9861)

* [3.0] Optimized Converter to cache

* add asf header

* fix compile
This commit is contained in:
Albumen Kevin 2022-03-31 09:49:13 +08:00 committed by GitHub
parent 807b10e56e
commit 225c2089a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 183 additions and 57 deletions

View File

@ -18,6 +18,7 @@ package org.apache.dubbo.common;
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
import org.apache.dubbo.common.config.ConfigurationCache;
import org.apache.dubbo.common.convert.ConverterUtil;
import org.apache.dubbo.common.lang.ShutdownHookCallbacks;
import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
@ -31,6 +32,7 @@ public class CommonScopeModelInitializer implements ScopeModelInitializer {
public void initializeFrameworkModel(FrameworkModel frameworkModel) {
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
beanFactory.registerBean(FrameworkExecutorRepository.class);
beanFactory.registerBean(ConverterUtil.class);
}
@Override

View File

@ -20,6 +20,7 @@ import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.config.InmemoryConfiguration;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.constants.RemotingConstants;
import org.apache.dubbo.common.convert.ConverterUtil;
import org.apache.dubbo.common.url.component.PathURLAddress;
import org.apache.dubbo.common.url.component.ServiceConfigURL;
import org.apache.dubbo.common.url.component.URLAddress;
@ -76,7 +77,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
import static org.apache.dubbo.common.convert.Converter.convertIfPossible;
import static org.apache.dubbo.common.utils.StringUtils.isBlank;
/**
@ -590,7 +590,7 @@ class URL implements Serializable {
String value = getParameter(key);
T result = null;
if (!isBlank(value)) {
result = convertIfPossible(value, valueType);
result = getOrDefaultFrameworkModel().getBeanFactory().getBean(ConverterUtil.class).convertIfPossible(value, valueType);
}
if (result == null) {
result = defaultValue;

View File

@ -16,12 +16,10 @@
*/
package org.apache.dubbo.common.convert;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.extension.ExtensionScope;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.common.lang.Prioritized;
import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom;
import static org.apache.dubbo.common.utils.TypeUtils.findActualTypeArgument;
@ -72,38 +70,4 @@ public interface Converter<S, T> extends Prioritized {
default Class<T> getTargetType() {
return findActualTypeArgument(getClass(), Converter.class, 1);
}
/**
* Get the Converter instance from {@link ExtensionLoader} with the specified source and target type
*
* @param sourceType the source type
* @param targetType the target type
* @return
* @see ExtensionLoader#getSupportedExtensionInstances()
*/
static Converter<?, ?> getConverter(Class<?> sourceType, Class<?> targetType) {
return getExtensionLoader(Converter.class)
.getSupportedExtensionInstances()
.stream()
.filter(converter -> converter.accept(sourceType, targetType))
.findFirst()
.orElse(null);
}
/**
* Convert the value of source to target-type value if possible
*
* @param source the value of source
* @param targetType the target type
* @param <T> the target type
* @return <code>null</code> if can't be converted
* @since 2.7.8
*/
static <T> T convertIfPossible(Object source, Class<T> targetType) {
Converter converter = getConverter(source.getClass(), targetType);
if (converter != null) {
return (T) converter.convert(source);
}
return null;
}
}

View File

@ -0,0 +1,70 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.dubbo.common.convert;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class ConverterUtil {
private final FrameworkModel frameworkModel;
private final Map<Class<?>, Map<Class<?>, List<Converter>>> converterCache = new ConcurrentHashMap<>();
public ConverterUtil(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
}
/**
* Get the Converter instance from {@link ExtensionLoader} with the specified source and target type
*
* @param sourceType the source type
* @param targetType the target type
* @return
* @see ExtensionLoader#getSupportedExtensionInstances()
*/
public Converter<?, ?> getConverter(Class<?> sourceType, Class<?> targetType) {
Map<Class<?>, List<Converter>> toTargetMap = converterCache.computeIfAbsent(sourceType, (k) -> new ConcurrentHashMap<>());
List<Converter> converters = toTargetMap.computeIfAbsent(targetType, (k) -> frameworkModel.getExtensionLoader(Converter.class)
.getSupportedExtensionInstances()
.stream()
.filter(converter -> converter.accept(sourceType, targetType))
.collect(Collectors.toList()));
return converters.size() > 0 ? converters.get(0) : null;
}
/**
* Convert the value of source to target-type value if possible
*
* @param source the value of source
* @param targetType the target type
* @param <T> the target type
* @return <code>null</code> if can't be converted
* @since 2.7.8
*/
public <T> T convertIfPossible(Object source, Class<T> targetType) {
Converter converter = getConverter(source.getClass(), targetType);
if (converter != null) {
return (T) converter.convert(source);
}
return null;
}
}

View File

@ -17,6 +17,8 @@
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.common.convert.Converter;
import org.apache.dubbo.common.convert.ConverterUtil;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.lang.reflect.Array;
@ -28,6 +30,11 @@ import static java.lang.reflect.Array.newInstance;
* @since 2.7.6
*/
public class StringToArrayConverter implements StringToMultiValueConverter {
private ConverterUtil converterUtil;
public StringToArrayConverter(FrameworkModel frameworkModel) {
converterUtil = frameworkModel.getBeanFactory().getBean(ConverterUtil.class);
}
public boolean accept(Class<String> type, Class<?> multiValueType) {
if (multiValueType != null && multiValueType.isArray()) {
@ -41,7 +48,7 @@ public class StringToArrayConverter implements StringToMultiValueConverter {
Class<?> componentType = targetType.getComponentType();
Converter converter = Converter.getConverter(String.class, componentType);
Converter converter = converterUtil.getConverter(String.class, componentType);
Object array = newInstance(componentType, size);

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
@ -25,6 +27,9 @@ import java.util.concurrent.LinkedBlockingDeque;
* @since 2.7.6
*/
public class StringToBlockingDequeConverter extends StringToIterableConverter<BlockingDeque> {
public StringToBlockingDequeConverter(FrameworkModel frameworkModel) {
super(frameworkModel);
}
@Override
protected BlockingDeque createMultiValue(int size, Class<?> multiValueType) {

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
@ -26,6 +28,9 @@ import java.util.concurrent.BlockingQueue;
* @since 2.7.6
*/
public class StringToBlockingQueueConverter extends StringToIterableConverter<BlockingQueue> {
public StringToBlockingQueueConverter(FrameworkModel frameworkModel) {
super(frameworkModel);
}
@Override
protected BlockingQueue createMultiValue(int size, Class<?> multiValueType) {

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.ArrayList;
import java.util.Collection;
@ -25,6 +27,9 @@ import java.util.Collection;
* @since 2.7.6
*/
public class StringToCollectionConverter extends StringToIterableConverter<Collection> {
public StringToCollectionConverter(FrameworkModel frameworkModel) {
super(frameworkModel);
}
@Override
protected Collection createMultiValue(int size, Class<?> multiValueType) {

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.ArrayDeque;
import java.util.Deque;
@ -25,6 +27,9 @@ import java.util.Deque;
* @since 2.7.6
*/
public class StringToDequeConverter extends StringToIterableConverter<Deque> {
public StringToDequeConverter(FrameworkModel frameworkModel) {
super(frameworkModel);
}
@Override
protected Deque createMultiValue(int size, Class<?> multiValueType) {

View File

@ -16,12 +16,13 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.common.convert.ConverterUtil;
import org.apache.dubbo.common.convert.StringConverter;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.Collection;
import java.util.Optional;
import static org.apache.dubbo.common.convert.Converter.getConverter;
import static org.apache.dubbo.common.utils.ClassUtils.getAllInterfaces;
import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom;
import static org.apache.dubbo.common.utils.TypeUtils.findActualTypeArgument;
@ -32,6 +33,12 @@ import static org.apache.dubbo.common.utils.TypeUtils.findActualTypeArgument;
* @since 2.7.6
*/
public abstract class StringToIterableConverter<T extends Iterable> implements StringToMultiValueConverter {
private ConverterUtil converterUtil;
public StringToIterableConverter(FrameworkModel frameworkModel) {
converterUtil = frameworkModel.getBeanFactory().getBean(ConverterUtil.class);
}
public boolean accept(Class<String> type, Class<?> multiValueType) {
return isAssignableFrom(getSupportedType(), multiValueType);
@ -63,7 +70,7 @@ public abstract class StringToIterableConverter<T extends Iterable> implements S
protected abstract T createMultiValue(int size, Class<?> multiValueType);
protected Optional<StringConverter> getStringConverter(Class<?> elementType) {
StringConverter converter = (StringConverter) getConverter(String.class, elementType);
StringConverter converter = (StringConverter) converterUtil.getConverter(String.class, elementType);
return Optional.ofNullable(converter);
}
@ -74,7 +81,7 @@ public abstract class StringToIterableConverter<T extends Iterable> implements S
@Override
public final int getPriority() {
int level = getAllInterfaces(getSupportedType(), type ->
isAssignableFrom(Iterable.class, type)).size();
isAssignableFrom(Iterable.class, type)).size();
return MIN_PRIORITY - level;
}
}

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.ArrayList;
import java.util.List;
@ -25,6 +27,9 @@ import java.util.List;
* @since 2.7.6
*/
public class StringToListConverter extends StringToIterableConverter<List> {
public StringToListConverter(FrameworkModel frameworkModel) {
super(frameworkModel);
}
@Override
protected List createMultiValue(int size, Class<?> multiValueType) {

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.NavigableSet;
import java.util.SortedSet;
import java.util.TreeSet;
@ -26,6 +28,9 @@ import java.util.TreeSet;
* @since 2.7.6
*/
public class StringToNavigableSetConverter extends StringToIterableConverter<NavigableSet> {
public StringToNavigableSetConverter(FrameworkModel frameworkModel) {
super(frameworkModel);
}
@Override
protected NavigableSet createMultiValue(int size, Class<?> multiValueType) {

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Queue;
@ -26,6 +28,9 @@ import java.util.Queue;
* @since 2.7.6
*/
public class StringToQueueConverter extends StringToIterableConverter<Queue> {
public StringToQueueConverter(FrameworkModel frameworkModel) {
super(frameworkModel);
}
@Override
protected Queue createMultiValue(int size, Class<?> multiValueType) {

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.HashSet;
import java.util.Set;
@ -25,6 +27,9 @@ import java.util.Set;
* @since 2.7.6
*/
public class StringToSetConverter extends StringToIterableConverter<Set> {
public StringToSetConverter(FrameworkModel frameworkModel) {
super(frameworkModel);
}
@Override
protected Set createMultiValue(int size, Class<?> multiValueType) {

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.SortedSet;
import java.util.TreeSet;
@ -25,6 +27,9 @@ import java.util.TreeSet;
* @since 2.7.6
*/
public class StringToSortedSetConverter extends StringToIterableConverter<SortedSet> {
public StringToSortedSetConverter(FrameworkModel frameworkModel) {
super(frameworkModel);
}
@Override
protected SortedSet createMultiValue(int size, Class<?> multiValueType) {

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
@ -25,6 +27,9 @@ import java.util.concurrent.TransferQueue;
* @since 2.7.6
*/
public class StringToTransferQueueConverter extends StringToIterableConverter<TransferQueue> {
public StringToTransferQueueConverter(FrameworkModel frameworkModel) {
super(frameworkModel);
}
@Override
protected TransferQueue createMultiValue(int size, Class<?> multiValueType) {

View File

@ -17,7 +17,8 @@
package org.apache.dubbo.common.utils;
import org.apache.dubbo.common.convert.Converter;
import org.apache.dubbo.common.convert.ConverterUtil;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
@ -324,13 +325,17 @@ public class ClassUtils {
}
public static Object convertPrimitive(Class<?> type, String value) {
return convertPrimitive(FrameworkModel.defaultModel(), type, value);
}
public static Object convertPrimitive(FrameworkModel frameworkModel, Class<?> type, String value) {
if (isEmpty(value)) {
return null;
}
Class<?> wrapperType = WRAPPER_PRIMITIVE_TYPE_MAP.getOrDefault(type, type);
Object result = null;
try {
result = Converter.convertIfPossible(value, wrapperType);
result = frameworkModel.getBeanFactory().getBean(ConverterUtil.class).convertIfPossible(value, wrapperType);
} catch (Exception e) {
// ignore exception
}

View File

@ -37,6 +37,7 @@ import org.apache.dubbo.config.support.Parameter;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ModuleModel;
import org.apache.dubbo.rpc.model.ScopeModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
@ -726,7 +727,7 @@ public abstract class AbstractConfig implements Serializable {
&& ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)
&& !isIgnoredAttribute(obj.getClass(), propertyName)) {
value = environment.resolvePlaceholders(value);
method.invoke(obj, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
method.invoke(obj, ClassUtils.convertPrimitive(ScopeModelUtil.getFrameworkModel(getScopeModel()), method.getParameterTypes()[0], value));
}
} catch (Exception e) {
logger.info("Failed to override the property " + method.getName() + " in " +

View File

@ -26,6 +26,7 @@ import org.apache.dubbo.config.annotation.Method;
import org.apache.dubbo.config.support.Parameter;
import org.apache.dubbo.rpc.model.AsyncMethodInfo;
import org.apache.dubbo.rpc.model.ModuleModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;
import java.util.ArrayList;
import java.util.Collections;
@ -253,7 +254,7 @@ public class MethodConfig extends AbstractMethodConfig {
String value = StringUtils.trim(subPropsConfiguration.getString(kebabPropertyName));
if (StringUtils.hasText(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
value = environment.resolvePlaceholders(value);
method.invoke(argument, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
method.invoke(argument, ClassUtils.convertPrimitive(ScopeModelUtil.getFrameworkModel(getScopeModel()), method.getParameterTypes()[0], value));
}
} catch (Exception e) {
logger.info("Failed to override the property " + method.getName() + " in " +

View File

@ -16,10 +16,12 @@
*/
package org.apache.dubbo.common.convert;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.apache.dubbo.common.convert.Converter.convertIfPossible;
import static org.apache.dubbo.common.convert.Converter.getConverter;
import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
@ -31,19 +33,31 @@ import static org.junit.jupiter.api.Assertions.assertSame;
*/
public class ConverterTest {
private ConverterUtil converterUtil;
@BeforeEach
public void setup() {
converterUtil = FrameworkModel.defaultModel().getBeanFactory().getBean(ConverterUtil.class);
}
@AfterEach
public void tearDown() {
FrameworkModel.destroyAll();
}
@Test
public void testGetConverter() {
getExtensionLoader(Converter.class)
.getSupportedExtensionInstances()
.forEach(converter -> {
assertSame(converter, getConverter(converter.getSourceType(), converter.getTargetType()));
assertSame(converter, converterUtil.getConverter(converter.getSourceType(), converter.getTargetType()));
});
}
@Test
public void testConvertIfPossible() {
assertEquals(Integer.valueOf(2), convertIfPossible("2", Integer.class));
assertEquals(Boolean.FALSE, convertIfPossible("false", Boolean.class));
assertEquals(Double.valueOf(1), convertIfPossible("1", Double.class));
assertEquals(Integer.valueOf(2), converterUtil.convertIfPossible("2", Integer.class));
assertEquals(Boolean.FALSE, converterUtil.convertIfPossible("false", Boolean.class));
assertEquals(Double.valueOf(1), converterUtil.convertIfPossible("1", Double.class));
}
}

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -36,7 +38,7 @@ public class StringToArrayConverterTest {
@BeforeEach
public void init() {
converter = new StringToArrayConverter();
converter = new StringToArrayConverter(FrameworkModel.defaultModel());
}
@Test

View File

@ -17,6 +17,7 @@
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -55,7 +56,7 @@ public class StringToQueueConverterTest {
@BeforeEach
public void init() {
converter = new StringToQueueConverter();
converter = new StringToQueueConverter(FrameworkModel.defaultModel());
}
@Test

View File

@ -17,6 +17,7 @@
package org.apache.dubbo.common.convert.multiple;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -55,7 +56,7 @@ public class StringToSetConverterTest {
@BeforeEach
public void init() {
converter = new StringToSetConverter();
converter = new StringToSetConverter(FrameworkModel.defaultModel());
}
@Test

View File

@ -17,8 +17,10 @@
package org.apache.dubbo.metadata.annotation.processing.rest;
import org.apache.dubbo.common.convert.Converter;
import org.apache.dubbo.common.convert.ConverterUtil;
import org.apache.dubbo.metadata.annotation.processing.rest.jaxrs.JAXRSServiceRestMetadataResolver;
import org.apache.dubbo.metadata.annotation.processing.rest.springmvc.SpringMvcServiceRestMetadataResolver;
import org.apache.dubbo.rpc.model.FrameworkModel;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
@ -32,7 +34,6 @@ import java.util.Set;
import static java.lang.String.valueOf;
import static java.util.Arrays.asList;
import static org.apache.dubbo.common.convert.Converter.getConverter;
import static org.apache.dubbo.common.utils.ClassUtils.forName;
import static org.apache.dubbo.common.utils.StringUtils.SLASH_CHAR;
import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.warn;
@ -146,7 +147,7 @@ public class DefaultServiceRestMetadataResolver extends AbstractServiceRestMetad
boolean supported;
try {
Class<?> targetType = forName(className, classLoader);
supported = getConverter(String.class, targetType) != null;
supported = FrameworkModel.defaultModel().getBeanFactory().getBean(ConverterUtil.class).getConverter(String.class, targetType) != null;
} catch (ClassNotFoundException e) {
supported = false;
}