Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When @Aspect and @FeignClient are used simultaneously in one application, an exception will be thrown in native-image #34388

Closed
wangliang181230 opened this issue Feb 27, 2023 · 2 comments
Assignees
Labels
for: external-project For an external project and not something we can fix

Comments

@wangliang181230
Copy link

wangliang181230 commented Feb 27, 2023

This exception only throw in the native-image.
When running spring-aot-mode without native-image, this exception will not be thrown.

Through adding logs and debugging, found that the root cause is that the JDK proxy object cannot perform the 'getMethod' operation in the native-image, it will threw the NoSuchMethodException, resulting in the method obtained by the 'ClassUtils. getMostSpecificMethod (...)' method is not as expected.

For example: jdkProxy.getClass().getMethod(methodName); will throw NoSuchMethodException in native-image, even if the method exists in the proxied interface.

I also report another issue: oracle/graal#6079

The simplest example:

https://github.com/wangliang181230/example__spring-projects_spring-boot_issue-34388


Classes:

My FeignClient

@FeignClient(name = "openfeign-example", url = "https://httpbin.org", contextId = "openfeign-example")
public interface HttpbinClient {

	@GetMapping("/delay/3")
	String delay();

	@GetMapping("/status/500")
	String status500();


	@GetMapping("/get")
	String get();

}

My Aspect

@Aspect
@Component
public class SentinelResourceAspect extends AbstractSentinelAspectSupport {

	private static final Logger LOGGER = LoggerFactory.getLogger(SentinelResourceAspect.class);
	

	@Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")
	public void sentinelResourceAnnotationPointcut() {
	}

	@Around("sentinelResourceAnnotationPointcut()")
	public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {
		LOGGER.info("Start aspect");
		
		// do something......
	}
}

Modified AspectJExpressionPointcut and print error log:

I modified the AspectJExpressionPointcut to catch the exception and print exception log.
Otherwise, the application will not start.

The modified AspectJExpressionPointcut:

public class AspectJExpressionPointcut extends AbstractExpressionPointcut
		implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {

	......

	private ShadowMatch getTargetShadowMatch(Method method, Class<?> targetClass) {
		Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);

		if (targetClass.getName().startsWith("jdk.")) {
			// In `native image`,    the value of `targetMethod.getDeclaringClass()` is the FeignClient interface: `HttpbinClient.class`,
			// In 'spring-aot-mode', the value of `targetMethod.getDeclaringClass()` is the proxy type: `jdk.proxy4.$Proxy46`.
			logger.info("\r\n    getTargetShadowMatch:" + " targetClass: " + targetClass.getName() + ", targetMethod: " + targetMethod.toGenericString()
					+ ", targetMethod.getDeclaringClass(): " + targetMethod.getDeclaringClass().getName()
			);
		}

		if (targetMethod.getDeclaringClass().isInterface()) {
			// Try to build the most specific interface possible for inherited methods to be
			// considered for sub-interface matches as well, in particular for proxy classes.
			// Note: AspectJ is only going to take Method.getDeclaringClass() into account.
			Set<Class<?>> ifcs = ClassUtils.getAllInterfacesForClassAsSet(targetClass);
			if (ifcs.size() > 1) {
				Class<?>[] interfaces = ClassUtils.toClassArray(ifcs);
				try {
					Class<?> compositeInterface = ClassUtils.createCompositeInterface(
							interfaces, targetClass.getClassLoader());
					targetMethod = ClassUtils.getMostSpecificMethod(targetMethod, compositeInterface);
				}
				catch (IllegalArgumentException ex) {
					// Implemented interfaces probably expose conflicting method signatures...
					// Proceed with original target method.
				}
				catch (Throwable t) {
					// Catch the exception, and print the log
					// Otherwise, the application will not start
					logger.error(Arrays.toString(interfaces) + " for class '"+targetClass.getName()+"' is createCompositeInterface failed", t);
				}
			}
		}
		return getShadowMatch(targetMethod, method);
	}

	......

}

The error log in native-image:

// Reason for the BUG
2023-02-27T15:22:59.474+08:00  WARN 9980 --- [main] org.springframework.util.ClassUtils      : No such method 'status500' by the target class 'jdk.proxy4.$Proxy46'

// For observation
2023-02-27T15:22:59.474+08:00  INFO 9980 --- [main] o.s.a.aspectj.AspectJExpressionPointcut  : 
    getTargetShadowMatch: targetClass: jdk.proxy4.$Proxy46, targetMethod: public abstract java.lang.String cn.wangliang181230.spring_projects__spring_boot__issue_34388.openfeign.HttpbinClient.status500(), targetMethod.getDeclaringClass(): cn.wangliang181230.spring_projects__spring_boot__issue_34388.openfeign.HttpbinClient

// The error log
2023-02-27T15:22:59.475+08:00 ERROR 9980 --- [main] o.s.a.aspectj.AspectJExpressionPointcut  : 
    [interface cn.wangliang181230.spring_projects__spring_boot__issue_34388.openfeign.HttpbinClient, interface java.io.Serializable]
    for class 'jdk.proxy4.$Proxy46' is createCompositeInterface failed

com.oracle.svm.core.jdk.UnsupportedFeatureError: Proxy class defined by interfaces [interface cn.wangliang181230.spring_projects__spring_boot__issue_34388.openfeign.HttpbinClient, interface java.io.Serializable] not found. Generating proxy classes at runtime is not supported. Proxy classes need to be defined at image build time by specifying the list of interfaces that they implement. To define proxy classes use -H:DynamicProxyConfigurationFiles=<comma-separated-config-files> and -H:DynamicProxyConfigurationResources=<comma-separated-config-resources> options.
	at org.graalvm.nativeimage.builder/com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:89) ~[na:na]
	at org.graalvm.nativeimage.builder/com.oracle.svm.core.reflect.proxy.DynamicProxySupport.getProxyClass(DynamicProxySupport.java:171) ~[na:na]
	at java.base@17.0.6/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:47) ~[spring-projects_spring-boot_issue-34388.exe:na]
	at java.base@17.0.6/java.lang.reflect.Proxy.getProxyClass(Proxy.java:398) ~[spring-projects_spring-boot_issue-34388.exe:na]
	at org.springframework.util.ClassUtils.createCompositeInterface(ClassUtils.java:783) ~[na:na]
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.getTargetShadowMatch(AspectJExpressionPointcut.java:432) ~[na:na]
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.matches(AspectJExpressionPointcut.java:289) ~[na:na]
	at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:251) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:288) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:320) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:128) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:97) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:78) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:366) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:318) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:435) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1866) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:105) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1823) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getObjectForBeanInstance(AbstractAutowireCapableBeanFactory.java:1265) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:259) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1628) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1585) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1368) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1325) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.aot.AutowiredFieldValueResolver.resolveValue(AutowiredFieldValueResolver.java:189) ~[na:na]
	at org.springframework.beans.factory.aot.AutowiredFieldValueResolver.resolveAndSet(AutowiredFieldValueResolver.java:167) ~[na:na]
	at cn.wangliang181230.spring_projects__spring_boot__issue_34388.controller.TestController__Autowiring.apply(TestController__Autowiring.java:14) ~[na:na]
	at org.springframework.beans.factory.support.InstanceSupplier$1.get(InstanceSupplier.java:82) ~[na:na]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:961) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:915) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[spring-projects_spring-boot_issue-34388.exe:6.0.2]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-projects_spring-boot_issue-34388.exe:3.0.0]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[spring-projects_spring-boot_issue-34388.exe:3.0.0]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) ~[spring-projects_spring-boot_issue-34388.exe:3.0.0]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-projects_spring-boot_issue-34388.exe:3.0.0]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[spring-projects_spring-boot_issue-34388.exe:3.0.0]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[spring-projects_spring-boot_issue-34388.exe:3.0.0]
	at cn.wangliang181230.spring_projects__spring_boot__issue_34388.ExampleApplication.main(ExampleApplication.java:16) ~[spring-projects_spring-boot_issue-34388.exe:na]

Other DEBUG information

图片


Recurrence steps:

  1. git clone https://github.com/wangliang181230/spring-projects_spring-boot_issue-34388.git
  2. mvn clean native:compile -Pnative
  3. start ./target/spring-projects_spring-boot_issue-34388.exe, it will print the error log 3 times.
  4. Browse http://localhost:8080/confirm-bug to confirm the BUG.

Environment and versions:

OS: Windows 10
JDK: graalvm-ce-java17-22.3.1
Spring Boot: 3.0.0
Native maven plugin: 0.9.20

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 27, 2023
@wangliang181230 wangliang181230 changed the title When @Aspect exists, use @FeignClient to throw an exception When @Aspect and @FeignClient are used at the same time, an exception will be thrown Feb 27, 2023
@wangliang181230 wangliang181230 changed the title When @Aspect and @FeignClient are used at the same time, an exception will be thrown When @Aspect and @FeignClient are used at the same application, an exception will be thrown Feb 27, 2023
@wangliang181230 wangliang181230 changed the title When @Aspect and @FeignClient are used at the same application, an exception will be thrown When @Aspect and @FeignClient are used at one application, an exception will be thrown Feb 27, 2023
@wangliang181230 wangliang181230 changed the title When @Aspect and @FeignClient are used at one application, an exception will be thrown When @Aspect and @FeignClient are used simultaneously in one application, an exception will be thrown in native-image Feb 27, 2023
@wilkinsona wilkinsona self-assigned this Feb 27, 2023
@wilkinsona
Copy link
Member

I think this should have been fixed by spring-projects/spring-framework#29519. Please test with the latest version of Spring Boot. If the problem still occurs, please open a Spring Framework issue.

@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale Feb 27, 2023
@wilkinsona wilkinsona added for: external-project For an external project and not something we can fix and removed status: waiting-for-triage An issue we've not yet triaged labels Feb 27, 2023
@wangliang181230
Copy link
Author

Fixed, Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: external-project For an external project and not something we can fix
Projects
None yet
Development

No branches or pull requests

3 participants