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中实现的方法都可以使用