【问题标题】:Spring session tries serialize service with session scopeSpring session 尝试使用 session 范围序列化服务
【发布时间】:2017-07-08 03:40:59
【问题描述】:

我有服务

@Service
@Scope(value = SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyService {
    public void method() throws MyException {
        throw new MyException();
    }
}

还有一个休息控制器:

@RestController
public class MyController {

    @Autowired
    private MyService myService;

    @RequestMapping(value = "/do", method = RequestMethod.POST)
    public String do() {
        myService.method();
        return null;
    }

    @ExceptionHandler(MyException.class)
    public ResponseEntity<MyException> exceptionHandler(MyException e) {
        return new ResponseEntity<MyException>(e, HttpStatus.UNAUTHORIZED);
    }
}

及配置文件:

@EnableRedisHttpSession
public class AppConfig {
}

当我请求控制器的 do 方法时,服务会抛出 MyExceptionExceptionHandler 捕获它。但是exceptionHandler返回后,我有一个例外:

org.springframework.data.redis.serializer.SerializationException: Cannot serialize; 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 [my.example.MyService$$EnhancerBySpringCGLIB$$442fa3ef_5]
    at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:92) ~[spring-data-redis-1.8.0.RELEASE.jar:na]
    at org.springframework.data.redis.core.AbstractOperations.rawHashValue(AbstractOperations.java:171) ~[spring-data-redis-1.8.0.RELEASE.jar:na]
    at org.springframework.data.redis.core.DefaultHashOperations.putAll(DefaultHashOperations.java:129) ~[spring-data-redis-1.8.0.RELEASE.jar:na]
    at org.springframework.data.redis.core.DefaultBoundHashOperations.putAll(DefaultBoundHashOperations.java:86) ~[spring-data-redis-1.8.0.RELEASE.jar:na]
    at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.saveDelta(RedisOperationsSessionRepository.java:778) ~[spring-session-1.3.0.RELEASE.jar:na]
    at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.access$000(RedisOperationsSessionRepository.java:670) ~[spring-session-1.3.0.RELEASE.jar:na]
    at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:388) ~[spring-session-1.3.0.RELEASE.jar:na]
    at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:245) ~[spring-session-1.3.0.RELEASE.jar:na]
    at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:245) ~[spring-session-1.3.0.RELEASE.jar:na]
    at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:217) ~[spring-session-1.3.0.RELEASE.jar:na]
    at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:170) ~[spring-session-1.3.0.RELEASE.jar: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 [my.example.MyService$$EnhancerBySpringCGLIB$$442fa3ef_5]
    at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:68) ~[spring-core-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:35) ~[spring-core-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:90) ~[spring-data-redis-1.8.0.RELEASE.jar:na]
    ... 33 common frames omitted
Caused by: java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [my.example.MyService$$EnhancerBySpringCGLIB$$442fa3ef_5]
    at org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:43) ~[spring-core-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:63) ~[spring-core-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    ... 35 common frames omitted

在调试时,我的会话尝试将 Myservice 对象序列化为键 "sessionAttr:scopedTarget.myService"

我不想让 MyService 类 Serializeble。还有其他解决方案吗?

【问题讨论】:

    标签: spring spring-mvc session spring-boot aop


    【解决方案1】:

    你的 bean 应该像下面这样可序列化:

    @Service
    @Scope(value = SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class MyService implements Serializable {
        public void method() throws MyException {
            throw new MyException();
        }
    }
    

    原因是使用redis你的bean必须准备好被序列化

    1. bean 已经在网络中旅行到 redis
    2. bean 已在 redis 数据存储中序列化

    您可能应该考虑不要将 redis 用作会话存储,事实上我相信您需要为每个会话创建一个新的 bean 实例,因此集中式会话数据存储可能无效,经典方法可能是更适合这个提议。

    我希望这种反思可能有助于您理解为什么我认为您应该考虑阳极解决方案。

    【讨论】:

    • 另外,如果你的 bean 被命名为“Service”,那可能意味着你不应该首先使用 @Scope("session")。会话范围的 bean 用于持久化会话状态。类似“服务”的对象应该是无状态的,因此它们可以安全地用作单例(Spring 的默认范围)。作为一般规则,对服务使用默认范围(即单例);如果他们需要会话状态,则将该状态放入单独的会话范围 bean(它作为一个愚蠢的可序列化 POJO)并将其注入到您的服务中。
    猜你喜欢
    • 2011-11-27
    • 1970-01-01
    • 2011-11-03
    • 2016-11-26
    • 1970-01-01
    • 2019-01-11
    • 2017-02-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多