diff --git a/pom.xml b/pom.xml index 3bd3a84a4..50377e517 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.springframework.boot spring-boot-starter-parent - 3.1.5 + 3.2.0 com.alipay.sofa @@ -37,7 +37,7 @@ 4.2.0-SNAPSHOT ${revision} - 3.1.5 + 3.2.0 17 UTF-8 diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java index 7619090ac..4ff673e60 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java @@ -35,6 +35,7 @@ import com.alipay.sofa.runtime.spi.service.BindingConverter; import com.alipay.sofa.runtime.spi.service.BindingConverterContext; import com.alipay.sofa.runtime.spi.service.BindingConverterFactory; +import com.alipay.sofa.runtime.spring.bean.LocalVariableTableParameterNameDiscoverer; import com.alipay.sofa.runtime.spring.bean.SofaBeanNameGenerator; import com.alipay.sofa.runtime.spring.bean.SofaParameterNameDiscoverer; import com.alipay.sofa.runtime.spring.factory.ReferenceFactoryBean; @@ -60,8 +61,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ScannedGenericBeanDefinition; import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.NativeDetector; import org.springframework.core.Ordered; import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.PrioritizedParameterNameDiscoverer; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.StandardMethodMetadata; @@ -113,6 +116,11 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) if (parameterNameDiscoverer == null) { parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); } + // keep compatible for second jars + if (parameterNameDiscoverer instanceof PrioritizedParameterNameDiscoverer prioritizedParameterNameDiscoverer + && !NativeDetector.inNativeImage()) { + prioritizedParameterNameDiscoverer.addDiscoverer(new LocalVariableTableParameterNameDiscoverer()); + } ((AbstractAutowireCapableBeanFactory) beanFactory) .setParameterNameDiscoverer(new SofaParameterNameDiscoverer(parameterNameDiscoverer, referenceAnnotationWrapper)); } diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/bean/LocalVariableTableParameterNameDiscoverer.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/bean/LocalVariableTableParameterNameDiscoverer.java new file mode 100644 index 000000000..81e8b82b7 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/bean/LocalVariableTableParameterNameDiscoverer.java @@ -0,0 +1,270 @@ +/* + * 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 com.alipay.sofa.runtime.spring.bean; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.asm.ClassReader; +import org.springframework.asm.ClassVisitor; +import org.springframework.asm.Label; +import org.springframework.asm.MethodVisitor; +import org.springframework.asm.Opcodes; +import org.springframework.asm.SpringAsmInfo; +import org.springframework.asm.Type; +import org.springframework.core.BridgeMethodResolver; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Deprecate in spring, fork codes for compatible + */ +@Deprecated +public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer { + + private static final Log logger = LogFactory + .getLog(LocalVariableTableParameterNameDiscoverer.class); + + // marker object for classes that do not have any debug info + private static final Map NO_DEBUG_INFO_MAP = Collections + .emptyMap(); + + // the cache uses a nested index (value is a map) to keep the top level cache relatively small in size + private final Map, Map> parameterNamesCache = new ConcurrentHashMap<>( + 32); + + @Override + @Nullable + public String[] getParameterNames(Method method) { + Method originalMethod = BridgeMethodResolver.findBridgedMethod(method); + return doGetParameterNames(originalMethod); + } + + @Override + @Nullable + public String[] getParameterNames(Constructor ctor) { + return doGetParameterNames(ctor); + } + + @Nullable + private String[] doGetParameterNames(Executable executable) { + Class declaringClass = executable.getDeclaringClass(); + Map map = this.parameterNamesCache.computeIfAbsent(declaringClass, this::inspectClass); + return (map != NO_DEBUG_INFO_MAP ? map.get(executable) : null); + } + + /** + * Inspects the target class. + *

Exceptions will be logged, and a marker map returned to indicate the + * lack of debug information. + */ + private Map inspectClass(Class clazz) { + InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz)); + if (is == null) { + // We couldn't load the class file, which is not fatal as it + // simply means this method of discovering parameter names won't work. + if (logger.isDebugEnabled()) { + logger.debug("Cannot find '.class' file for class [" + clazz + + "] - unable to determine constructor/method parameter names"); + } + return NO_DEBUG_INFO_MAP; + } + // We cannot use try-with-resources here for the InputStream, since we have + // custom handling of the close() method in a finally-block. + try { + ClassReader classReader = new ClassReader(is); + Map map = new ConcurrentHashMap<>(32); + classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0); + if (logger.isWarnEnabled()) { + logger + .warn("Using deprecated '-debug' fallback for parameter name resolution. Compile the " + + "affected code with '-parameters' instead or avoid its introspection: " + + clazz.getName()); + } + return map; + } catch (IOException ex) { + if (logger.isDebugEnabled()) { + logger.debug("Exception thrown while reading '.class' file for class [" + clazz + + "] - unable to determine constructor/method parameter names", ex); + } + } catch (IllegalArgumentException ex) { + if (logger.isDebugEnabled()) { + logger + .debug( + "ASM ClassReader failed to parse class file [" + + clazz + + "], probably due to a new Java class file version that isn't supported yet " + + "- unable to determine constructor/method parameter names", ex); + } + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + return NO_DEBUG_INFO_MAP; + } + + /** + * Helper class that inspects all methods and constructors and then + * attempts to find the parameter names for the given {@link Executable}. + */ + private static class ParameterNameDiscoveringVisitor extends ClassVisitor { + + private static final String STATIC_CLASS_INIT = ""; + + private final Class clazz; + + private final Map executableMap; + + public ParameterNameDiscoveringVisitor(Class clazz, + Map executableMap) { + super(SpringAsmInfo.ASM_VERSION); + this.clazz = clazz; + this.executableMap = executableMap; + } + + @Override + @Nullable + public MethodVisitor visitMethod(int access, String name, String desc, String signature, + String[] exceptions) { + // exclude synthetic + bridged && static class initialization + if (!isSyntheticOrBridged(access) && !STATIC_CLASS_INIT.equals(name)) { + return new LocalVariableTableVisitor(this.clazz, this.executableMap, name, desc, + isStatic(access)); + } + return null; + } + + private static boolean isSyntheticOrBridged(int access) { + return (((access & Opcodes.ACC_SYNTHETIC) | (access & Opcodes.ACC_BRIDGE)) > 0); + } + + private static boolean isStatic(int access) { + return ((access & Opcodes.ACC_STATIC) > 0); + } + } + + private static class LocalVariableTableVisitor extends MethodVisitor { + + private static final String CONSTRUCTOR = ""; + + private final Class clazz; + + private final Map executableMap; + + private final String name; + + private final Type[] args; + + private final String[] parameterNames; + + private final boolean isStatic; + + private boolean hasLvtInfo = false; + + /* + * The nth entry contains the slot index of the LVT table entry holding the + * argument name for the nth parameter. + */ + private final int[] lvtSlotIndex; + + public LocalVariableTableVisitor(Class clazz, Map map, + String name, String desc, boolean isStatic) { + super(SpringAsmInfo.ASM_VERSION); + this.clazz = clazz; + this.executableMap = map; + this.name = name; + this.args = Type.getArgumentTypes(desc); + this.parameterNames = new String[this.args.length]; + this.isStatic = isStatic; + this.lvtSlotIndex = computeLvtSlotIndices(isStatic, this.args); + } + + @Override + public void visitLocalVariable(String name, String description, String signature, + Label start, Label end, int index) { + this.hasLvtInfo = true; + for (int i = 0; i < this.lvtSlotIndex.length; i++) { + if (this.lvtSlotIndex[i] == index) { + this.parameterNames[i] = name; + } + } + } + + @Override + public void visitEnd() { + if (this.hasLvtInfo || (this.isStatic && this.parameterNames.length == 0)) { + // visitLocalVariable will never be called for static no args methods + // which doesn't use any local variables. + // This means that hasLvtInfo could be false for that kind of methods + // even if the class has local variable info. + this.executableMap.put(resolveExecutable(), this.parameterNames); + } + } + + private Executable resolveExecutable() { + ClassLoader loader = this.clazz.getClassLoader(); + Class[] argTypes = new Class[this.args.length]; + for (int i = 0; i < this.args.length; i++) { + argTypes[i] = ClassUtils.resolveClassName(this.args[i].getClassName(), loader); + } + try { + if (CONSTRUCTOR.equals(this.name)) { + return this.clazz.getDeclaredConstructor(argTypes); + } + return this.clazz.getDeclaredMethod(this.name, argTypes); + } catch (NoSuchMethodException ex) { + throw new IllegalStateException( + "Method [" + + this.name + + "] was discovered in the .class file but cannot be resolved in the class object", + ex); + } + } + + private static int[] computeLvtSlotIndices(boolean isStatic, Type[] paramTypes) { + int[] lvtIndex = new int[paramTypes.length]; + int nextIndex = (isStatic ? 0 : 1); + for (int i = 0; i < paramTypes.length; i++) { + lvtIndex[i] = nextIndex; + if (isWideType(paramTypes[i])) { + nextIndex += 2; + } else { + nextIndex++; + } + } + return lvtIndex; + } + + private static boolean isWideType(Type aType) { + // float is not a wide type + return (aType == Type.LONG_TYPE || aType == Type.DOUBLE_TYPE); + } + } + +} diff --git a/sofa-boot-project/sofaboot-dependencies/pom.xml b/sofa-boot-project/sofaboot-dependencies/pom.xml index 7f335ef48..6bc92f82e 100644 --- a/sofa-boot-project/sofaboot-dependencies/pom.xml +++ b/sofa-boot-project/sofaboot-dependencies/pom.xml @@ -19,7 +19,7 @@ 0.4 3.19.0 3.1.2 - 0.8.9 + 0.8.11 3.0.0 6.1.8 @@ -28,11 +28,11 @@ 2.0.1 1.6.6 3.5.1 - 2.2.3 + 3.0.1-SNAPSHOT 1.6.1 - 2022.0.3 - 9.4 + 2023.0.0 + 9.5 1.2.83 3.29.2-GA 3.22.2 @@ -537,18 +537,6 @@ ${commons.io.version} - - mysql - mysql-connector-java - ${mysql.version} - - - com.google.protobuf - protobuf-java - - - - com.alibaba druid diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/RpcSofaBootApplication.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/RpcSofaBootApplication.java index 4092a5c79..66ea0eb85 100644 --- a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/RpcSofaBootApplication.java +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/RpcSofaBootApplication.java @@ -18,16 +18,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.FilterType; /** * * @author yuanxuan * @version : RpcSofaBootApplication.java, v 0.1 2023年02月03日 15:19 yuanxuan Exp $ */ -@SpringBootApplication -@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.alipay.sofa.smoke.tests.rpc.boot.*") }) +@SpringBootApplication(scanBasePackages = "none") public class RpcSofaBootApplication { public static void main(String[] args) { diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-tracer/pom.xml b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-tracer/pom.xml index 5c33fd88e..13126d1c1 100644 --- a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-tracer/pom.xml +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-tracer/pom.xml @@ -42,8 +42,8 @@ - mysql - mysql-connector-java + com.mysql + mysql-connector-j