【问题标题】:Spring serialization error for AmazonS3 clientAmazonS3 客户端的 Spring 序列化错误
【发布时间】:2021-10-23 17:25:31
【问题描述】:

我有一个奇怪的问题,我无法真正理解。

我有一个使用 AmazonS3 客户端将文件上传到 AWS S3 存储桶的代码。我有一个 try-catch 块,我在其中捕获任何异常,在 catch 块中,我抛出了由控制器方法 @ExceptionHandler 处理的定义的异常,并且应该返回 thymeleaf 模板 errorUpload.html。

@Service
public class UploadService {
    private final AmazonS3 amazonS3;
    private final String bucketName;

    public UploadService(AmazonS3 amazonS3, @Value("${aws.bucketName}") String bucketName) {
        this.amazonS3 = amazonS3;
        this.bucketName = bucketName;
    }

    public void manualUpload(MultipartFile file) {
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(file.getContentType());
        metadata.setContentLength(file.getSize());

        try {
            amazonS3.putObject(new PutObjectRequest(bucketName, file.getName(), file.getInputStream(), metadata));
        } catch (Exception e) {
            throw new UploadException(e.getMessage());
        }
    }
}

异常处理程序:

@ExceptionHandler({UploadException.class})
public String uploadError() {
    return "errorPages/errorUpload";
}

当我在没有任何逻辑的情况下简单地在 manualUpload 方法中抛出异常时,它可以正常工作。但是当使用 AmazonS3 对象并抛出异常时,逻辑会转到 ExceptionHandler 但它会引发以下错误:

org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.Object] to type [byte[]] for value 'com.amazonaws.services.s3.AmazonS3Client@4b0ac28c'; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.amazonaws.services.s3.AmazonS3Client]
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.serialize(JdbcIndexedSessionRepository.java:623) ~[spring-session-jdbc-2.5.1.jar:2.5.1]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.access$400(JdbcIndexedSessionRepository.java:133) ~[spring-session-jdbc-2.5.1.jar:2.5.1]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository$1.setValues(JdbcIndexedSessionRepository.java:487) ~[spring-session-jdbc-2.5.1.jar:2.5.1]
    at org.springframework.jdbc.core.JdbcTemplate.lambda$batchUpdate$4(JdbcTemplate.java:1042) ~[spring-jdbc-5.3.9.jar:5.3.9]
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651) ~[spring-jdbc-5.3.9.jar:5.3.9]
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:691) ~[spring-jdbc-5.3.9.jar:5.3.9]
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:1034) ~[spring-jdbc-5.3.9.jar:5.3.9]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.insertSessionAttributes(JdbcIndexedSessionRepository.java:479) ~[spring-session-jdbc-2.5.1.jar:2.5.1]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.access$1200(JdbcIndexedSessionRepository.java:133) ~[spring-session-jdbc-2.5.1.jar:2.5.1]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository$JdbcSession.lambda$save$10(JdbcIndexedSessionRepository.java:854) ~[spring-session-jdbc-2.5.1.jar:2.5.1]
    at org.springframework.transaction.support.TransactionOperations.lambda$executeWithoutResult$0(TransactionOperations.java:68) ~[spring-tx-5.3.9.jar:5.3.9]
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.9.jar:5.3.9]
    at org.springframework.transaction.support.TransactionOperations.executeWithoutResult(TransactionOperations.java:67) ~[spring-tx-5.3.9.jar:5.3.9]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository$JdbcSession.save(JdbcIndexedSessionRepository.java:836) ~[spring-session-jdbc-2.5.1.jar:2.5.1]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository$JdbcSession.access$200(JdbcIndexedSessionRepository.java:665) ~[spring-session-jdbc-2.5.1.jar:2.5.1]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.save(JdbcIndexedSessionRepository.java:422) ~[spring-session-jdbc-2.5.1.jar:2.5.1]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.save(JdbcIndexedSessionRepository.java:133) ~[spring-session-jdbc-2.5.1.jar:2.5.1]
    at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:226) ~[spring-session-core-2.5.1.jar:2.5.1]
    at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:193) ~[spring-session-core-2.5.1.jar:2.5.1]
    at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:145) ~[spring-session-core-2.5.1.jar:2.5.1]
    at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82) ~[spring-session-core-2.5.1.jar:2.5.1]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.3.9.jar:5.3.9]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:711) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:461) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:385) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:313) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:398) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:257) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:352) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:177) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1723) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at java.base/java.lang.Thread.run(Thread.java:831) ~[na:na]
Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.amazonaws.services.s3.AmazonS3Client]
    at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:64) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:33) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.core.convert.support.GenericConversionService$ConverterAdapter.convert(GenericConversionService.java:386) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41) ~[spring-core-5.3.9.jar:5.3.9]
    ... 47 common frames omitted
Caused by: java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.amazonaws.services.s3.AmazonS3Client]
    at org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:43) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.core.serializer.Serializer.serializeToByteArray(Serializer.java:56) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:60) ~[spring-core-5.3.9.jar:5.3.9]
    ... 50 common frames omitted

我认为这可能与将 SPRING_SECURITY_CONTEXT 存储在 spring_session_attributes 表中有关,因此因为 AmazonS3 未序列化它会引发该错误。有什么想法可以解决这个问题吗?

【问题讨论】:

    标签: java spring amazon-s3 spring-security


    【解决方案1】:

    我找到了解决方案。 任何不可序列化的对象都不能作为 bean 创建和使用,因为在这种情况下,当这个特定的 bean 将存储在 SPRING_SECURITY_CONTEXT 中时,会发生序列化异常。 所以解决方案就是不创建 Bean,而是在我们想使用它时使用“new”关键字创建对象。

    @Service
    public class UploadService {
        private final String bucketName;
        private final S3Client s3Client;
    
        public UploadService(@Value("${aws.bucketName}") String bucketName,
                             @Value("${aws.accessKey}") String awsAccessKey,
                             @Value("${aws.secretKey}") String awsSecretKey) {
            this.s3Client = S3Client.builder()
                    .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(awsAccessKey, awsSecretKey)))
                    .region(Region.US_EAST_1)
                    .build();
            this.bucketName = bucketName;
        }
    
        public void manualUpload(MultipartFile file) {
            try {
                s3Client.putObject(PutObjectRequest.builder().bucket(bucketName).key(file.getName()).build(),
                        RequestBody.fromBytes(file.getBytes()));
            } catch (Exception e) {
                throw new UploadException(e.getMessage());
            }
        }
    }
    

    【讨论】:

    • 太棒了!实际上,在我的案例中,我正在使用构建器,并没有注意到您没有。不过,很高兴知道您发现了问题。
    • @JoãoDias 谢谢 :)
    【解决方案2】:

    这个错误确实很奇怪。在我看来,您使用的是旧版本的 AWS SDK for Java (https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-s3),因此我建议您将其更新到新版本 (https://mvnrepository.com/artifact/software.amazon.awssdk/s3) 并检查问题是否仍然存在。

    有关更多详细信息,请查看有关如何迁移 https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/migration.html 的 AWS 文档。

    【讨论】:

    • 我已经用新版本的 aws sdk 替换了上传逻辑,这是同样的问题。 Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.io.NotSerializableException: software.amazon.awssdk.services.s3.DefaultS3Client
    猜你喜欢
    • 2015-10-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-16
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多