Skip to content

SpringBoot fatjar模式下,Instrument方法中出现强转逻辑导致asm报 java.lang.TypeNotPresentException: Type com/one/agent/app/api/model/EmployeeAndy not present #47

@saleson

Description

@saleson

异常栈:

2023-03-14 10:32:39 [main] ERROR c.a.o.s.OneAgentClassFileTransformer -transform error, loader: org.springframework.boot.loader.LaunchedURLClassLoader@6a03bcb1, className: com/saleson/oneagent/springboot/app/demo/service/EmployeeServiceImpl, transformer: com.alibaba.bytekit.asm.instrument.InstrumentTransformer@293d0107
java.lang.TypeNotPresentException: Type com/one/agent/app/api/model/EmployeeAndy not present
	at com.alibaba.deps.org.objectweb.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1041)
	at com.alibaba.deps.org.objectweb.asm.SymbolTable.addMergedType(SymbolTable.java:1202)
	at com.alibaba.deps.org.objectweb.asm.Frame.merge(Frame.java:1299)
	at com.alibaba.deps.org.objectweb.asm.Frame.merge(Frame.java:1197)
	at com.alibaba.deps.org.objectweb.asm.MethodWriter.computeAllFrames(MethodWriter.java:1611)
	at com.alibaba.deps.org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1547)
	at com.alibaba.deps.org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:767)
	at com.alibaba.deps.org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:647)
	at com.alibaba.deps.org.objectweb.asm.tree.ClassNode.accept(ClassNode.java:451)
	at com.alibaba.bytekit.utils.AsmUtils.toBytes(AsmUtils.java:86)
	at com.alibaba.bytekit.asm.instrument.InstrumentTransformer.transform(InstrumentTransformer.java:60)
	at com.alibaba.oneagent.service.OneAgentClassFileTransformer.transform(OneAgentClassFileTransformer.java:35)
	at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
	at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:473)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:151)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at org.springframework.util.ClassUtils.forName(ClassUtils.java:284)
	at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:469)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1621)
	at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1548)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:704)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:674)
	at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1684)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:570)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:542)
	at org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationCondition.checkServletRegistration(DispatcherServletAutoConfiguration.java:187)
	at org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationCondition.getMatchOutcome(DispatcherServletAutoConfiguration.java:167)
	at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47)
	at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108)
	at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:225)
	at org.springframework.context.annotation.ConfigurationClassParser.processMemberClasses(ConfigurationClassParser.java:371)
	at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:271)
	at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:249)
	at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:599)
	at org.springframework.context.annotation.ConfigurationClassParser.access$800(ConfigurationClassParser.java:110)
	at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.lambda$processGroupImports$1(ConfigurationClassParser.java:812)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:809)
	at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:780)
	at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:192)
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331)
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112)
	at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:423)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1317)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
	at com.saleson.oneagent.springboot.app.demo.AppDemoApplication.main(AppDemoApplication.java:21)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
	at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: java.lang.ClassNotFoundException: com.one.agent.app.api.model.EmployeeAndy
	at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at com.alibaba.deps.org.objectweb.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1039)
	... 72 common frames omitted

复现逻辑:

  1. 在common module中新建2个类,Employee, 以及其子类EmployeeAndy。
  2. 新建springboot module, 并添加spring-boot-maven-plugin 将其编译为springboot fatjar。
  3. 在instrument-lib.jar中添加Instrument类,并在增强方法中加入强转代码:
@Instrument(Interface = {"com.one.agent.app.api.EmployeeService"})
public class EmployeeServiceInst {

    public void check(Employee employee) {
        if(employee instanceof EmployeeAndy){
            employee = (EmployeeAndy) employee;
            System.out.println("EmployeeServiceInst.check employee type is " + EmployeeAndy.class);
        }
        
        InstrumentApi.invokeOrigin();

    }
  1. 添加one-java-agent参数运行springboot fatjar, 在oneagent.log中出现上述异常日志。

原因:

反复排查后,发现asm在计算方法的最大栈空间和最大本地变量数时,遇到强转逻辑会执行到ClassWriter.getCommonSuperClass()方法,该方法会去尝试加载被转换对象的class和转换的目标class,进行assignable from比对判断。问题在于此方法中的ClassLoader是Launcher$AppClassLoader, debug截图如下:
image

在springboot fatjar 运行模式下, Launcher$AppClassLoader是无法加载到springboot fatjar中的class和资源,所以导致此处抛出ClassNotFoundException, 被捕获后包装成TypeNotPresentException抛出。

demo-all.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions