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() {
+    }
 }

Reply via email to