You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2025/07/29 03:35:59 UTC
(skywalking-java) branch main updated: Fix enhanced instance causes Spring Application startup failure (#762)
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 <78...@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() {
+ }
}