先说结论:

项目中定制了spring 的redisTemplate,而这个template没有使用我自定义的Jackson ObjectMapper。所以不生效。

下面是详细过程:

起因是spring boot项目加入了shiro,我打算使用redis去存储shiro的会话,方便以后横向扩展。

参考了网上的实现后,决定通过扩展org.apache.shiro.session.mgt.eis.AbstractSessionDAO来实现。

以下是实现代码:

 package com.ceiec.baseplatform.config;

 import com.ceiec.baseplatform.redis.StringKeyRedisTemplate;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import java.io.Serializable;
import java.util.Collection;
import java.util.concurrent.TimeUnit; @Component
public class RedisSessionDAO extends AbstractSessionDAO {
private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class); @SuppressWarnings("rawtypes")
@Autowired
private StringKeyRedisTemplate<String, Object> redisTemplate; private static final String DEFAULT_SESSION_KEY_PREFIX = "shirosession:"; private String keyPrefix = DEFAULT_SESSION_KEY_PREFIX; private long expireTime = 120000; public RedisSessionDAO() {
super();
} public RedisSessionDAO(long expireTime) {
super();
this.expireTime = expireTime;
} @Override // 更新session
public void update(Session session) throws UnknownSessionException {
System.out.println("===============update================");
if (session == null || session.getId() == null) {
return;
}
session.setTimeout(expireTime);
String key = getKey(session);
redisTemplate.opsForValue().set(key, session, expireTime, TimeUnit.MILLISECONDS);
} private String getKey(Session session) {
return this.keyPrefix + String.valueOf(session.getId());
}
private String getSessionIdKey(String sessionId) {
return this.keyPrefix + String.valueOf(sessionId);
} @Override // 删除session
public void delete(Session session) {
System.out.println("===============delete================");
if (null == session) {
return;
}
redisTemplate.opsForValue().getOperations().delete(getKey(session));
} @Override
// 获取活跃的session,可以用来统计在线人数,如果要实现这个功能,可以在将session加入redis时指定一个session前缀,统计的时候则使用keys("session-prefix*")的方式来模糊查找redis中所有的session集合
public Collection<Session> getActiveSessions() {
// System.out.println("==============getActiveSessions=================");
// return redisTemplate.keys("*");
return CollectionUtils.EMPTY_COLLECTION;
} @Override// 加入session
protected Serializable doCreate(Session session) {
System.out.println("===============doCreate================");
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId); redisTemplate.opsForValue().set(getKey(session), session, expireTime, TimeUnit.MILLISECONDS);
return sessionId;
} @Override// 读取session
protected Session doReadSession(Serializable sessionId) {
System.out.println("==============doReadSession=================");
if (sessionId == null) {
return null;
}
return (Session) redisTemplate.opsForValue().get(getSessionIdKey(String.valueOf(sessionId)));
} public long getExpireTime() {
return expireTime;
} public void setExpireTime(long expireTime) {
this.expireTime = expireTime;
} }

然后将该RedisSessionDao注册到sessionManager等,这个不在本文范围内,有兴趣可搜索相关shiro配置。

    @Autowired
private RedisSessionDAO redisSessionDAO; @Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO);
securityManager.setSessionManager(sessionManager);
//设置realm.
securityManager.setRealm(myShiroRealm());
return securityManager;
}

一切看起来不错。运行,登录,然后,报错了。

现在不太能重现那个错误,大概是,登录时,会把org.apache.shiro.session.mgt.SimpleSession的实例写入到redis。

其中有一个方法如下:

/**
* @since 0.9
*/
public boolean isValid() {
return !isStopped() && !isExpired();
}
序列化时,会序列化一个valid:true的属性到json中。

而在后续读会话时,会反序列化session。然后报错,提示不认识valid属性。

于是在网上查询spring boot如何定制objectMapper去忽略不认识的属性,
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
按照网上说法和文档,如果只要替换ObjectMapper的话,只要按照下面说的这样去定义一个@Bean和@Primary标注的class。

但是在按照上述方法去配置后,发现没有效果。

后边想了很久。。。。突然发现自己的项目中,因为不想用jdk的序列化器,所以自定义了

Spring的RedisTemplate,去使用jackson的序列化器。
而这个template中可能没有使用我自定义的ObjectMapper。 后边发现,果然如此。
然后修改后如下:
package com.ceiec.baseplatform.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.stereotype.Component; import java.io.IOException; /**
* desc: redis操作类
* @author:
* creat_date: 2018/1/4
* creat_time: 17:18
**/
@Component
public class StringKeyRedisTemplate<K, V> extends RedisTemplate<K, V> { /**
* Constructs a new <code>StringRedisTemplate</code> instance. {@link #setConnectionFactory(RedisConnectionFactory)}
* and {@link #afterPropertiesSet()} still need to be called.
*/
public StringKeyRedisTemplate() { } /**
* Constructs a new <code>StringRedisTemplate</code> instance ready to be used.
*
* @param connectionFactory connection factory for creating new connections
*/
@Autowired
public StringKeyRedisTemplate(RedisConnectionFactory connectionFactory) {
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
//设置key序列化器
setKeySerializer(stringSerializer);
setHashKeySerializer(stringSerializer);
//设置value的序列化器
ObjectMapper mapper = jacksonObjectMapper();
setValueSerializer(new GenericJackson2JsonRedisSerializer(mapper));
setHashValueSerializer(new GenericJackson2JsonRedisSerializer(mapper)); setConnectionFactory(connectionFactory);
afterPropertiesSet();
} @Override
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
} public ObjectMapper jacksonObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); logger.info("construct complete! " + objectMapper);
return objectMapper;
}
}
												

spring boot定制Jackson ObjectMapper,为什么不生效的更多相关文章

  1. Spring Boot 定制与优化内置的Tomcat容器

    1.Spring Boot定制与优化内置Tomcat容器. > 内置的容器有三个分别是Undertow.Jetty.Tomcat,Spring Boot 对这三个容器分别进行了实现,它们上层接口 ...

  2. Spring Boot 定制URL匹配规则的方法

    事情的起源:有人问我,说编写了一个/hello访问路径,但是吧,不管是输入/hello还是/hello.html,还是/hello.xxx都能进行访问.当时我还以为他对代码进行处理了,后来发现不是,后 ...

  3. Spring Boot定制启动图案

    启动图案 Spring Boot在启动的时候会显示一个默认的Spring的图案,对应的类为SpringBootBanner. . ____ _ __ _ _ /\\ / ___'_ __ _ _(_) ...

  4. 75. Spring Boot 定制URL匹配规则【从零开始学Spring Boot】

    在之前有一篇文章说了,博客名称从原来的<从零开始学Spring Boot>更改为<Spring Boot常见异常汇总>,后来写了几篇文章之后发展,有些文章还是一些知识点,所以后 ...

  5. Spring MVC或Spring Boot配置默认访问页面不生效?

    相信在开发项目过程中,设置默认访问页面应该都用过.但是有时候设置了却不起作用.你知道是什么原因吗?今天就来说说我遇到的问题. 首先说说配置默认访问页面有哪几种方式. 1.tomcat配置默认访问页面 ...

  6. Spring boot 定制自己的错误

    1).如何定制错误的页面: ​ 1).有模板引擎的情况下:error/状态码; [将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error文件夹下],发生此状态码的错误就会来到 对 ...

  7. Spring Boot项目中如何定制PropertyEditors

    本文首发于个人网站:Spring Boot项目中如何定制PropertyEditors 在Spring Boot: 定制HTTP消息转换器一文中我们学习了如何配置消息转换器用于HTTP请求和响应数据, ...

  8. spring boot(一):入门篇

    构建微服务:Spring boot 入门篇 什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框 ...

  9. 最全spring boot视频系列,你值得拥有

    ================================== 从零开始学Spring Boot视频 ================================== àSpringBoot ...

随机推荐

  1. JDBC控制事务

    概念 事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit).事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并 ...

  2. SAP OLE中常用的一些方法和属性

    1.ole中如何保存和退出. call method of sheetname = filepath # =. call method of applicationname 'quit'. 2.给sh ...

  3. Filecoin协议(挖矿)

    Filecoin协议是如何运作的? 用户如何使用Filecoin系统来查询,存储数据? 存储矿工如何挖矿,如何获取FIL代币? 检索矿工如何挖矿,获取FIL代币? 本文主要回答上面的这些问题!!! 首 ...

  4. Swift 笔记汇总

    Swift 3 笔记 Swift: The Basics :介绍 Swift 一些零碎的点. Swift: Basic Operators :介绍 Swift 的操作符. Functions : 介绍 ...

  5. Android 零散知识点整理

    Android 零散知识点整理 为什么Android的更新试图操作必须在主线程中进行? 这是因为Android系统中的视图组件并不是线程安全的.通常应该让主线程负责创建.显示和更新UI,启动子线程,停 ...

  6. 使用git将本地代码传到github

    方法可能有些小小的差别,但是最终的结果都是一样的 在github上新建代码仓库 确定之后会显示一个仓库的url,复制下来 在本地找一个作为本地仓库的文件夹右键Git Bash Here打开git 把g ...

  7. Mycat 配置说明(server.xml)

    server.xml 几乎保存了所有mycat需要的系统配置信息,包括 mycat 用户管理.DML权限管理等,其在代码内直接的映射类为SystemConfig 类. user 标签 该标签主要用于定 ...

  8. 【windows】 配置一个本地的tomcat服务器

    配置tomcat 公司的许多业务都是用java+tomcat模式的,做本地测试的时候经常要搭建一个自己的tomcat服务器.整个操作不难,但是记录一下,万一以后遇到什么问题也可以放这里. ■ 安装ja ...

  9. mysql新手入门随笔

    1.启动/关闭服务器 第一种方法:通过Notifier 第二种方法: 通过Windows自带的服务管理:计算机右键选择管理弹出框选择"服务和应用程序"里的服务列表,从列表中找到My ...

  10. python(字符串操作)

    一.字符串的局部替换 python 字符串替换可以用2种方法实现:1是用字符串本身的方法.2用正则来替换字符串 下面用个例子来实验下:a = 'hello word'我把a字符串里的word替换为py ...