SpringBoot实现上下文管理
简述
在日常开发中,经常会用到上下文来实现一些变量的托管和程序逻辑的实现(如收集接口调用信息、记录日志等,会通过上下文获取用户信息、业务变量等),本文详细介绍了如何实现和使用上下文。
本文的上下文环境中用户信息的赋值在用户登录验证中实现,可参考文章:
【SpringBoot实现登录拦截】
上下文管理的实现步骤如下:
- 定义上下文变量所保存的内容
- 定义上下文管理工具
原理
SpringBoot在响应一个请求时,会创建一个线程,而实现上下文的管理,可以通过创建线程本地变量来实现,这样的话,多个请求的响应之间就不会发生上下文的冲突。
管理的线程变量的方式有很多,比较常见的有:
- synchronized(锁)
- ThreadLocal
这里我们使用的是ThreadLocal,因为synchronized(锁)是采用时间换空间的方式,多个线程(请求响应)之间共用一份变量,当一个线程访问时,其他线程进行等待,这样不仅会造成多线程的效率下降,而且一个线程改变了变量的值,其他线程读取到的就是改变后的值。
而ThreadLocal是为每一个线程提供了变量副本,每个线程使用自己的副本,无论对变量进行什么操作,都不会影响其他线程的变量。
所以我们的上下文管理的实现方式就是,将上下文注册到ThreadLocal中,通过我们的工具类进行赋值和读取。
定义上下文变量
所定义的上下文变量应具有高兼容性,支持存放比较复杂的数据,且方便读取。一般的实现方式是放置一个Map<String, Object>来实现。
本文所实现的上下文,可存放变量,以及用户信息,我所使用的工程为了区分一般变量和业务变量,定义了两个存放变量的Map,一般使用一个Map就可以。
定义上下文管理接口类,具体的管理逻辑在实现类中编写,上下文的具体变量也在实现类中编写。
package com.project.base.context;
import java.io.Serializable;
import java.util.Map;
public interface BaseContext extends Serializable {
// 项目中定义的用户实体类
BaseCurrentUser getBaseCurrentUser();
// 对用户进行赋值的方法
void setBaseCurrentUser(BaseCurrentUser baseCurrentUser);
// 获取变量
Object getProperty(Object var);
// 添加变量
void addProperty(Object varKey, Object varValue);
// 移出变量
void removeProperty(Object var);
// 获取所有变量
Map<Object, Object> getProperties();
// 初始化变量
void setProperties(Map<Object, Object> map);
// 移出所有变量
void removeAllProperties();
// 添加业务变量
void addBusiProperty(Object busiVarKey, Object busiVarValue);
// 获取业务变量
Object getBusiProperty(Object busiVarKey);
// 获取所有业务变量
Map<Object, Object> getBusiProperties();
// 初始化业务变量
void setBusiProperties(Map<Object, Object> map);
}
实现类:
package com.project.base.context.impl;
import com.project.base.context.BaseContext;
import com.project.base.context.BaseCurrentUser;
import lombok.ToString;
import java.util.HashMap;
import java.util.Map;
@ToString
public class BaseContextImpl implements BaseContext {
private static final long serialVersionUID = 8383356012441014698L;
// 用户类
private BaseCurrentUser baseCurrentUser = new BaseCurrentUser();
// 变量
private Map<Object, Object> properties = new HashMap<>();
// 业务变量
private Map<Object, Object> busiProperties = new HashMap<>();
public BaseContextImpl() {}
@Override
public BaseCurrentUser getBaseCurrentUser() {
return this.baseCurrentUser;
}
@Override
public void setBaseCurrentUser(BaseCurrentUser baseCurrentUser) {
this.baseCurrentUser = baseCurrentUser;
}
@Override
public Object getProperty(Object var) {
return this.properties.get(var);
}
@Override
public void addProperty(Object varKey, Object varValue) {
this.properties.put(varKey, varValue);
}
@Override
public void removeProperty(Object var) {
this.properties.remove(var);
}
@Override
public Map<Object, Object> getProperties() {
return this.properties;
}
@Override
public void setProperties(Map<Object, Object> map) {
this.properties = map;
}
@Override
public void removeAllProperties() {
this.properties.clear();
}
@Override
public void addBusiProperty(Object busiVarKey, Object busiVarValue) {
this.busiProperties.put(busiVarKey, busiVarValue);
}
@Override
public Object getBusiProperty(Object busiVarKey) {
return this.busiProperties.get(busiVarKey);
}
@Override
public Map<Object, Object> getBusiProperties() {
return this.busiProperties;
}
@Override
public void setBusiProperties(Map<Object, Object> map) {
this.busiProperties = map;
}
}
定义上下文变量管理类
定义一个类,统一管理
package com.project.base.context;
import com.project.base.context.impl.BaseContextImpl;
public class BaseContextHolder {
// 创建一个线程变量,类型为BaseContext
private static final ThreadLocal<BaseContext> contextHolder = new ThreadLocal<>();
public BaseContextHolder() {
}
// 设置变量
public static void setContext(BaseContext baseContext) { contextHolder.set(baseContext); }
public static BaseContext getContext() {
BaseContext obj = contextHolder.get();
if (obj == null) {
// 如果为空就创建一个新的
obj = new BaseContextImpl();
setContext((BaseContext) obj);
}
return (BaseContext) obj;
}
}
这样在程序中就能通过BaseContextHolder来获取和配置上下文变量了,使用的方式如下:
BaseContext baseContext = BaseContextHolder.getContext();
baseContext.getBaseCurrentUser();
baseContext.getProperty(keyName);
baseContext.getProperties();
baseContext.getBusiProperties();
...
// 在BaseContextImpl中实现的方法都可以使用