This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/skywalking-java.git
The following commit(s) were added to refs/heads/main by this push: new c3a6fcb32b Fix enhanced instance causes Spring Application startup failure (#762) c3a6fcb32b is described below commit c3a6fcb32b342d39bdf072c0fc8ea853a1aedc47 Author: tjuwangning <78459090+tjuwangn...@users.noreply.github.com> AuthorDate: Tue Jul 29 11:35:53 2025 +0800 Fix enhanced instance causes Spring Application startup failure (#762) Root cause: SkyWalking's enhancement changes how Spring decides which proxy type to use. As mentioned in [#13221](https://github.com/apache/skywalking/issues/13221#issuecomment-2854170165), SkyWalking modifies Spring's `hasNoUserSuppliedProxyInterfaces()` to avoid affecting the choice of proxy type. But once Spring Retry has added `Retryable` to the list of interfaces, `hasNoUserSuppliedProxyInterfaces()` will find a user-supplied interface and return false, which changes the proxy type and leads [...] Starting in 4.0.2.RELEASE, Spring adds a new hook method `evaluateProxyInterfaces()`, which allows proxy-related flags (like proxyTargetClass) to be determined earlier in the lifecycle. We also need to enhance `evaluateProxyInterfaces()` (and its related callbacks) so that SkyWalking's EnhancedInstance interface is always ignored. --- CHANGES.md | 2 + .../spring/patch/AutoProxyCreatorInterceptor.java | 53 +++++++++++++++ .../patch/ProxyProcessorSupportInterceptor.java | 52 +++++++++++++++ .../define/AutoProxyCreatorInstrumentation.java | 78 ++++++++++++++++++++++ .../ProxyProcessorSupportInstrumentation.java | 69 +++++++++++++++++++ .../src/main/resources/skywalking-plugin.def | 2 + .../plugin/scenarios/spring-4.1.x-scenario/pom.xml | 21 ++++++ .../RetryConfig.java} | 23 ++----- .../testcase/spring3/service/TestServiceBean.java | 9 +++ 9 files changed, 292 insertions(+), 17 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4f1bc7cee8..116241e4d8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,8 @@ Release Notes. * Support for tracking in lettuce versions 6.5.x and above. * Upgrade byte-buddy version to 1.17.6. * Support gRPC 1.59.x and 1.70.x server interceptor trace +* Fix the `CreateAopProxyInterceptor` in the Spring core-patch changes the AOP proxy type when a class is + enhanced by both SkyWalking and Spring AOP. All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/236?closed=1) diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/AutoProxyCreatorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/AutoProxyCreatorInterceptor.java new file mode 100644 index 0000000000..943ac8983d --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/AutoProxyCreatorInterceptor.java @@ -0,0 +1,53 @@ +/* + * 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.skywalking.apm.plugin.spring.patch; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +import java.lang.reflect.Method; + +/** + * <code>AutoProxyCreatorInterceptor</code> determines whether the given interface is {@link EnhancedInstance}, + * and therefore does not consider it a reasonable proxy interface. + */ +public class AutoProxyCreatorInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, + Object ret) throws Throwable { + + Class<?> ifc = (Class<?>) allArguments[0]; + return ((boolean) ret) || ifc.getName().equals("org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance"); + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class<?>[] argumentsTypes, Throwable t) { + + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/ProxyProcessorSupportInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/ProxyProcessorSupportInterceptor.java new file mode 100644 index 0000000000..d8840d4ef2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/ProxyProcessorSupportInterceptor.java @@ -0,0 +1,52 @@ +/* + * 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.skywalking.apm.plugin.spring.patch; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +import java.lang.reflect.Method; + +/** + * <code>ProxyProcessorSupportInterceptor</code> determines whether the given interface is {@link EnhancedInstance}, + * and therefore does not consider it a reasonable proxy interface. + */ +public class ProxyProcessorSupportInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, + Object ret) throws Throwable { + Class<?> ifc = (Class<?>) allArguments[0]; + return ((boolean) ret) || ifc.getName().equals("org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance"); + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class<?>[] argumentsTypes, Throwable t) { + + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/AutoProxyCreatorInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/AutoProxyCreatorInstrumentation.java new file mode 100644 index 0000000000..26e6147e69 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/AutoProxyCreatorInstrumentation.java @@ -0,0 +1,78 @@ +/* + * 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.skywalking.apm.plugin.spring.patch.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import java.util.Collections; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class AutoProxyCreatorInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator"; + public static final String ENHANCE_METHOD = "isConfigurationCallbackInterface"; + public static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.spring.patch.AutoProxyCreatorInterceptor"; + + @Override + public final ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public final InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher<MethodDescription> getMethodsMatcher() { + return named(ENHANCE_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + protected List<WitnessMethod> witnessMethods() { + return Collections.singletonList(new WitnessMethod(ENHANCE_CLASS, named(ENHANCE_METHOD))); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/ProxyProcessorSupportInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/ProxyProcessorSupportInstrumentation.java new file mode 100644 index 0000000000..78de2969c2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/ProxyProcessorSupportInstrumentation.java @@ -0,0 +1,69 @@ +/* + * 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.skywalking.apm.plugin.spring.patch.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class ProxyProcessorSupportInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.aop.framework.ProxyProcessorSupport"; + public static final String ENHANCE_METHOD = "isInternalLanguageInterface"; + public static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.spring.patch.ProxyProcessorSupportInterceptor"; + + @Override + public final ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public final InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher<MethodDescription> getMethodsMatcher() { + return named(ENHANCE_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/resources/skywalking-plugin.def index e51178b97b..3a21d2b4aa 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/resources/skywalking-plugin.def @@ -19,3 +19,5 @@ spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.Autowired spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.AopExpressionMatchInstrumentation spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.AspectJExpressionPointCutInstrumentation spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.BeanWrapperImplInstrumentation +spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.ProxyProcessorSupportInstrumentation +spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.AutoProxyCreatorInstrumentation diff --git a/test/plugin/scenarios/spring-4.1.x-scenario/pom.xml b/test/plugin/scenarios/spring-4.1.x-scenario/pom.xml index 012ab866eb..b6a8d68bb0 100644 --- a/test/plugin/scenarios/spring-4.1.x-scenario/pom.xml +++ b/test/plugin/scenarios/spring-4.1.x-scenario/pom.xml @@ -33,6 +33,7 @@ <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version> <test.framework.version>4.1.0.RELEASE</test.framework.version> <test.framework>spring</test.framework> + <apm-toolkit-trace.version>9.4.0</apm-toolkit-trace.version> </properties> <name>skywalking-spring-4.1.x-scenario</name> @@ -86,6 +87,26 @@ <artifactId>log4j-core</artifactId> <version>2.8.1</version> </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-aop</artifactId> + <version>${test.framework.version}</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-aspects</artifactId> + <version>${test.framework.version}</version> + </dependency> + <dependency> + <groupId>org.springframework.retry</groupId> + <artifactId>spring-retry</artifactId> + <version>1.2.5.RELEASE</version> + </dependency> + <dependency> + <groupId>org.apache.skywalking</groupId> + <artifactId>apm-toolkit-trace</artifactId> + <version>${apm-toolkit-trace.version}</version> + </dependency> </dependencies> <build> diff --git a/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java b/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/config/RetryConfig.java similarity index 56% copy from test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java copy to test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/config/RetryConfig.java index c940d5425c..4d7d40d153 100644 --- a/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java +++ b/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/config/RetryConfig.java @@ -16,23 +16,12 @@ * */ -package test.apache.skywalking.apm.testcase.spring3.service; +package test.apache.skywalking.apm.testcase.spring3.config; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import test.apache.skywalking.apm.testcase.spring3.component.TestComponentBean; -import test.apache.skywalking.apm.testcase.spring3.dao.TestRepositoryBean; +import org.springframework.context.annotation.Configuration; +import org.springframework.retry.annotation.EnableRetry; -@Service -public class TestServiceBean { - @Autowired - private TestComponentBean componentBean; - - @Autowired - private TestRepositoryBean repositoryBean; - - public void doSomeBusiness(String name) { - componentBean.componentMethod(name); - repositoryBean.doSomeStuff(name); - } +@Configuration +@EnableRetry +public class RetryConfig { } diff --git a/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java b/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java index c940d5425c..b99b6b38c3 100644 --- a/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java +++ b/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java @@ -18,7 +18,10 @@ package test.apache.skywalking.apm.testcase.spring3.service; +import org.apache.skywalking.apm.toolkit.trace.Trace; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; import test.apache.skywalking.apm.testcase.spring3.component.TestComponentBean; import test.apache.skywalking.apm.testcase.spring3.dao.TestRepositoryBean; @@ -35,4 +38,10 @@ public class TestServiceBean { componentBean.componentMethod(name); repositoryBean.doSomeStuff(name); } + + // Test the class is enhanced by both SkyWalking and Spring AOP + @Retryable(value = Exception.class, backoff = @Backoff(delay = 1000, multiplier = 2)) + @Trace + private void doRetry() { + } }