You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot部署API Gateway+Lambda报ClassNotFoundException排查

问题现象

将Spring Boot应用对接API Gateway部署到AWS Lambda时抛出如下类找不到错误:

Class not found: com.myapp.app.LambdaHandler: java.lang.ClassNotFoundException
java.lang.ClassNotFoundException: com.myapp.app.LambdaHandler. Current classpath: file:/var/task/

打包生成的Jar包内包含BOOT-INF、META-INF、org三个目录,业务类实际存放路径为BOOT-INF/classes/com/myapp/app/LambdaHandler。
部署方式为通过serverless.yml配置,执行serverless deploy命令完成部署。


相关代码与配置

StreamLambdaHandler.java

public class StreamLambdaHandler implements RequestStreamHandler {  
  private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler; 
    static {
        try {
            handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
        } catch (Exception e) {
            // 初始化失败时重抛异常触发冷重启
            e.printStackTrace();
            throw new RuntimeException("Could not initialize Spring Boot Application", e);
        }
    }

    @Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
            throws IOException {
        handler.proxyStream(inputStream, outputStream, context);
    } 
}

Application.java

@SpringBootApplication
@Configuration
@EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
    /**
     * 注册所需HandlerMapping,避免生成多个默认HandlerMapping实例
     */
    @Bean
    public HandlerMapping handlerMapping() {
        return new RequestMappingHandlerMapping();
    }

    /**
     * 注册所需HandlerAdapter,避免生成多个默认HandlerAdapter实例
     */
    @Bean
    public HandlerAdapter handlerAdapter() {
        return new RequestMappingHandlerAdapter();
    }
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

业务控制器代码

@RestController
@EnableWebMvc
@Profile("lambda")
@RequestMapping("/app/v2")
public class ClientInformation {
    @Value("${clientDetails.userName}")
    private String userName;
    
    @Value("${clientDetails.userId}")
    private int userId;
    
    @Value("${clientDetails.userAddress}")
    private String userAddress;
    
    @GetMapping("/")
    public Map<String, Object> getDetails()  {  
        Map<String, Object> rtn = new HashMap<>();
        rtn.put("message", "App is working fine !!!");
        return rtn;
    }
    
    @GetMapping("/information")
    public Map<String, Object> getInformation()  {  
        Map<String, Object> rtn = new HashMap<>();
        rtn.put("name", userName);
        rtn.put("id", userId);
        return rtn;
    }
    
    @GetMapping("/ping")
    public Map<String, Object> getD() {
        Map<String, Object> rtn = new HashMap<>();
        rtn.put("name", "Ping is happening !!!");
        rtn.put("status", "200");
        return rtn;
    }
}

serverless.yml配置

service: myapplication-api
provider:
  name: aws
  runtime: java11
  memorySize: 512
  timeout: 15
  profile: default
  stage: production
package:
  artifact: target/myapp-api-0.0.1-SNAPSHOT-exec.jar

functions:
  catchAllAny:
    handler: index.catchAllAny
    events:
      - httpApi: '*'
  catchAllMethod:
    handler: handler.catchAllMethod
    events:
      - httpApi:
          method: '*'
          path: /any/method

pom.xml配置

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath /> <!-- 从仓库拉取父依赖 -->
    </parent>
    <groupId>com.myapp.app</groupId>
    <artifactId>myapp-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>myapp-api</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!-- Spring boot Lambda Serverless 依赖 -->
        <dependency>
            <groupId>com.amazonaws.serverless</groupId>
            <artifactId>aws-serverless-java-container-spring</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-events</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws.serverless</groupId>
            <artifactId>aws-serverless-java-container-springboot2</artifactId>
            <version>1.5.2</version>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.authorize</groupId>
            <artifactId>anet-java-sdk</artifactId>
            <version>2.0.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-bean-validators</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
        <dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>5.2</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.15</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-s3</artifactId>
            <version>1.12.226</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.15</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <classifier>exec</classifier>
                    <executable>false</executable>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

问题根因

一共存在4个核心配置错误,直接导致类找不到异常:

  • Jar包结构不符合Lambda加载要求:当前用Spring Boot默认打包插件生成的是可执行FatJar,所有业务类都放在BOOT-INF/classes目录下,Lambda运行时的默认类加载器只会扫描Jar包根目录下的类,不会读取BOOT-INF下的内容,自然找不到Handler类。
  • serverless.yml中Handler路径配置完全错误:配置里写的index.catchAllAny、handler.catchAllMethod和实际的Handler类全限定名不匹配,实际编写的Handler类是com.myapp.app.StreamLambdaHandler,根本不存在index、handler包下的对应类。
  • Java版本不匹配:provider配置的runtime是java11,但pom.xml中指定的编译版本是Java 1.8,版本不一致会引发类加载兼容性问题。
  • 打包插件配置错误:虽然引入了maven-shade-plugin依赖,但没有在build的plugins节点中配置shade插件的执行规则,实际打包时根本没有生成Lambda可用的扁平化Jar包。另外还同时引入了aws-serverless-java-container-spring和aws-serverless-java-container-springboot2两个依赖,存在版本冲突。

修复步骤
  • 修正pom.xml配置
    • 删掉重复冲突的aws-serverless-java-container-spring依赖,只保留和Spring Boot 2.x匹配的aws-serverless-java-container-springboot2依赖。
    • 将java.version改成11,和Lambda runtime版本对齐。
    • 移除maven-shade-plugin的dependency声明,把shade插件配置到build->plugins节点下,配置生成扁平化Jar的规则,替换原有的Spring Boot可执行Jar打包逻辑,保证所有类直接放在Jar包根目录,去掉BOOT-INF分层结构。
      调整后的build插件配置参考:
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    
  • 修正serverless.yml配置
    • 把package节点下的artifact路径改成shade插件生成的无classifier后缀的Jar包:target/myapp-api-0.0.1-SNAPSHOT.jar
    • 将functions下的handler配置改成实际的Handler类全限定名+方法名,因为StreamLambdaHandler实现了RequestStreamHandler,入口方法是handleRequest,所以配置为com.myapp.app.StreamLambdaHandler::handleRequest,删掉不存在的两个错误handler配置,只保留一个入口函数即可。
      修正后的functions配置参考:
    functions:
      springBootApp:
        handler: com.myapp.app.StreamLambdaHandler::handleRequest
        events:
          - httpApi: '*'
    
  • 重新打包部署
    执行mvn clean package重新打包,解压生成的Jar包确认所有com包直接在Jar根目录、不存在BOOT-INF分层后,再执行serverless deploy部署即可。

内容的提问来源于stack exchange,提问作者Atique Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 20:09:34