第二章 基于spring-boot和spring-cloud应用型框架-CORE

文章目录

      • 前言:
      • 1.父层目录
      • 2.FW-BOOT
      • 3.FW-ClOUD
      • 4.FW-CORE
        • 4.1.core-web
          • 1.WebMvcConfigurer的配置
          • 2.core-web实现
            • 4.1.2.1.统一数据返回处理实现
            • 4.1.2.2 统一异常处理
            • 4.1.2.3 在线文档实现
            • 4.1.2.4 上下文会话

前言:

上一章已经把整个框架做了介绍,同时也略带了后续的微服务生态图。这章将从框架的搭建开始说起,spring-boot和spring-cloud对于看这篇文章的jr吗都不陌生,我们就从它们开始。

还是先附上项目结构图:
第二章 基于spring-boot和spring-cloud应用型框架-CORE_第1张图片

1.父层目录

是一个空的maven项目,在pom文件中定义spring-boot/clould和其他依赖的版本:spring-boot版本是:2.1.3.RELEASE
spring-cloud:版本是Greenwich.SR2;在父pom中以import的方式申明。
第二章 基于spring-boot和spring-cloud应用型框架-CORE_第2张图片

2.FW-BOOT

第二章 基于spring-boot和spring-cloud应用型框架-CORE_第3张图片
这个包相当简单,把Spring-boot的相关依赖统一放在这边管理,为什么这么做呢?之前在core包中集成大量的spring-boot包,导致pom文件相当臃肿不好管理;单独拎出来,会让整个pom变得很清晰,后续所有spring-boot的升级,新增依赖都只要在这个maven模块维护即可,很多人会有疑问这样做是不是有点多余,我的观点是,如果我们在这样的细节上做一些小的调整,能为整个框架维护和扩展带来一些好处的话是值得的,这种好处看似没啥,实际上渗透在每个框架开发人员中(自行体会吧)。这个模块不会对外暴露,只会在core中引用。ok,接下啦看下pom文件的引用:引入了spring-boot的相关依赖



    
        fw-parent
        com.mars.fw
        1.0.0-SNAPSHOT
    
    4.0.0
    com.mars.fw
    FW-BOOT
    1.0.0-SNAPSHOT
    jar

    
        
            org.springframework.boot
            spring-boot-starter-data-jpa
        
        
            org.springframework.boot
            spring-boot-starter-jdbc
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.springframework.boot
            spring-boot-starter-tomcat
            compile
        
        
            org.apache.tomcat.embed
            tomcat-embed-jasper
            compile
        

        
        
            org.springframework.boot
            spring-boot-devtools
            true
        
        
            org.springframework.boot
            spring-boot-starter-logging
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
        
            org.springframework.boot
            spring-boot-starter-aop
        
        
        
        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
        
        
            org.springframework.boot
            spring-boot-autoconfigure
        
        
    

3.FW-ClOUD

这个和FW-BOOT一样把spring-cloud的相关配置依赖抽取出来,在core中引用,目的是一样的;看下pom文件的引用:注意在这里面我引入了spring-cloud-starter-alibaba-nacos-config和spring-cloud-starter-alibaba-nacos-discovery,这是因为服务的注册中心我使用了nacos 而没有使用尤里卡;两种我都使用过,nacos在可视化界面和配置文件管理方面我认为不错,实践下来很刚。nacos的安装部署使用会写专门的章节来介绍使用。



    
        fw-parent
        com.mars.fw
        1.0.0-SNAPSHOT
    
    4.0.0
    com.mars.fw
    FW-CLOUD
    1.0.0-SNAPSHOT
    jar
    MARS-FW-CLOUD-STARTER
    
        
            dengjinde
            [email protected]
        
    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    
    
        
            org.springframework.cloud
            spring-cloud-starter-alibaba-nacos-config
        
        
            org.springframework.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
            2.1.1.RELEASE
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-hystrix
            2.1.1.RELEASE
        
    

4.FW-CORE

这个是核心,接下来围绕这个详细来讲解实践过程,没有很复杂难懂的东西。
第二章 基于spring-boot和spring-cloud应用型框架-CORE_第4张图片
从上面的目录结构来看:我把core分成了web模块和其他模块。先来讲下web模块:我的定义是所有有关提供API和WEB应用相关的操作都放在这个包中;我们来思考下对于一个web的应用需要具备哪些基础功能(实际上是对springMVC的封装和增强)。

4.1.core-web
1.WebMvcConfigurer的配置

要支持spring-web相关的功能首先要进行web相关的配置,这个大家都清楚了我们新建一个类来实现这个接口进行配置。
这里面主要处理了这么几个问题:

1.前端浏览器ajax的跨域问题
2.静态资源路径设置问题
3.接下来会讲到的统一参数返回的处理就是这个方法:
第二章 基于spring-boot和spring-cloud应用型框架-CORE_第5张图片

package com.mars.fw;

import com.mars.fw.web.reponse.handler.MarsRespValueHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author King
 * @Date 2020-04-20
 */
@Configuration
@EnableAspectJAutoProxy
@EnableAutoConfiguration
public class MarsWebMvcConfigure implements WebMvcConfigurer {

    /**
     * 实例请求映射处理适配器
     */
    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    /**
     * 解决跨域问题
     * 

* * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT") .maxAge(3600); } private CorsConfiguration addcorsConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); List<String> list = new ArrayList<>(); list.add("*"); corsConfiguration.setAllowedOrigins(list); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", addcorsConfig()); return new CorsFilter(source); } /** * mvc 静态资源路径访问权限配置 * 例如:registry.addResourceHandler("/upload/**").addResourceLocations("classpath:/upload/"); * /upload/ 路径下的静态资源可以访问 *

* * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //所有目录都可以访问 registry.addResourceHandler("/static/**"). addResourceLocations("file:/usr/share/nginx/images/"); registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); } /** * @PostConstruct 非静态的void()方法 尽量少在复杂的逻辑方法上注解这个 影响启动速度 *

Tomcat6.x 以上 * 服务器加载Servlet的时候运行,并且只会被服务器执行一次 *

* 服务器加载Servlet的时候运行 执行实例请求映射处理适配器下自定义的参数拦截统一处理 返回标准数据结构 */ @PostConstruct public void initResponseValue() { final List<HandlerMethodReturnValueHandler> originalHandlers = new ArrayList<>(requestMappingHandlerAdapter.getReturnValueHandlers()); RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null; for (int i = 0; i < originalHandlers.size(); i++) { final HandlerMethodReturnValueHandler valueHandler = originalHandlers.get(i); if (RequestResponseBodyMethodProcessor.class.isAssignableFrom(valueHandler.getClass())) { requestResponseBodyMethodProcessor = (RequestResponseBodyMethodProcessor) valueHandler; break; } } MarsRespValueHandler marsRespValueHandler = new MarsRespValueHandler(requestResponseBodyMethodProcessor); final int deferredPos = obtainValueHandlerPosition(originalHandlers, DeferredResultMethodReturnValueHandler.class); originalHandlers.add(deferredPos + 1, marsRespValueHandler); requestMappingHandlerAdapter.setReturnValueHandlers(originalHandlers); } private int obtainValueHandlerPosition(final List<HandlerMethodReturnValueHandler> originalHandlers, Class<?> handlerClass) { for (int i = 0; i < originalHandlers.size(); i++) { final HandlerMethodReturnValueHandler valueHandler = originalHandlers.get(i); if (handlerClass.isAssignableFrom(valueHandler.getClass())) { return i; } } return -1; } }

2.core-web实现
4.1.2.1.统一数据返回处理实现

首先我们希望我们的API接口返回的数据格式是统一的,这边就设计到SpringMVC里面怎么封装统一的数据格式,原理这边不深讲。首先我们先定义系统中的统一返回码和统一返回数据格式:
第二章 基于spring-boot和spring-cloud应用型框架-CORE_第6张图片
定义一个IRCode的接口用来给业务层做自定义实现,同时定义一个King类来实现框架中包含的code和message;

package com.mars.fw.web.reponse;

/**
 * @Author King
 * @create 2020/4/20 11:43
 */
public interface IRCode {

    String message();

    int code();
}

package com.mars.fw.web.reponse;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

import java.io.Serializable;

/**
 * @Author King
 * @create 2020/4/20 11:24
 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class King<T> implements Serializable {

    private static final long serialVersionUID = -2466174531203439862L;

    public static final Integer SUCCESS_CODE = 1000;
    public static final String SUCCESS_MSG = "操作成功";

    private int code;
    private String msg;
    private T data;


    public King() {
        this(SUCCESS_CODE, SUCCESS_MSG, null);
    }

    public King(IRCode irCode) {
        this(irCode.code(), irCode.message(), null);
    }

    public King(IRCode irCode, T data) {
        this(irCode.code(), irCode.message(), data);
    }

    public King(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public static <T> King<T> success(IRCode irCode, T data) {
        return new King(KingCode.SUCCESS, data);
    }

    public static <T> King<T> success(String message) {
        return new King(KingCode.SUCCESS, message);
    }

    public static <T> King<T> fail(IRCode irCode, T data) {
        return new King(KingCode.FAULT, data);
    }

    public static <T> King<T> fail(String message) {
        return new King(KingCode.FAULT, message);
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

除此之外我们还需定义一个系统枚举的code:KingCode

package com.mars.fw.web.reponse;

/**
 * @Author King
 * @create 2020/4/20 11:52
 */
public enum KingCode implements IRCode {


    /**
     * 状态码定义
     */
    FAULT(9999, "失败"),
    SUCCESS(10000, "成功"),
    LOGIN_SUCCESS(10001, "登录成功"),
    LOGOUT_SUCCESS(10002, "登录失败"),
    PASS_WRONG(10002, "密码错误"),
    USER_NOT_FIND(10003, "用户不存在"),
    CAPTCHA_TIMEOUT(10004, "验证码失效"),
    CAPTCHA_ERROR(10005, "验证码错误"),
    LOGIN_LOCK(10006, "该账号已被关闭"),
    SMS_EXCEPTION(10007, "短信登录异常"),
    TOKEN_NOT_EMPTY(90000, "token不能为空"),
    TOKEN_ERROR(90001, "token校验失败"),
    TOKEN_EXCEPTION(90002, "token校验异常"),
    URL_EXPIRED(90003, "访问的URL过期"),
    DEFAULT_EXCEPTION(99999, "系统异常");

    final int code;
    final String message;

    @Override
    public int code() {
        return this.code;
    }

    @Override
    public String message() {
        return this.message;
    }

    private KingCode(final int code, final String message) {
        this.code = code;
        this.message = message;
    }
}

写到这边我发现这些不用贴上代码只讲核心实现就好,这些直接到源码去看。
核心的实现在我们自定义一个MarsRespValueHandler,这个类是实现了spring-web包中的HandlerMethodReturnValueHandler。这个handler是spring提供给我们的,用来处理controller层方法返回的结果处理器。我们可以重写里面的方法来得到我们需要的数据格式。代码里面我有详细的注释,handleReturnValue 是我们处理的关键,我们把我们处理后的值重新放回spring-web response的链路中也就是this.requestResponseBodyMethodProcessor.handleReturnValue。

package com.mars.fw.web.reponse.handler;

import com.mars.fw.web.reponse.King;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

/**
 * @description: mvc 返回值统一处理 统一处理成Response实体结构 标准输出到前端
 * @author:dengjinde
 * @date:2020/4/20
 */
public class MarsRespValueHandler implements HandlerMethodReturnValueHandler {

    private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor;

    /**
     * 构造函数
     *
     * @param requestResponseBodyMethodProcessor
     */
    public MarsRespValueHandler(RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor) {
        this.requestResponseBodyMethodProcessor = requestResponseBodyMethodProcessor;
    }

    /**
     * 当返回值为 true的时候才会开启自定义的handler
     *
     * @param returnType
     * @return
     */
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class));
    }

    /**
     * 自定义handler逻辑 当返回值为空的时候自动返回标准格式
     * 这边的逻辑可以根据需要扩展
     *
     * @param returnValue
     * @param returnType
     * @param mavContainer
     * @param webRequest
     * @throws Exception
     */
    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        King response = null;
        if (!King.class.isAssignableFrom(returnType.getParameterType())) {
            response = new King(King.SUCCESS_CODE, "success", returnValue);
        } else {
            response = (King) returnValue;
        }
        this.requestResponseBodyMethodProcessor.handleReturnValue(response, returnType, mavContainer, webRequest);
    }
}

定义好这个handler后在前面的MarsWebMvcConfigure中,在指定的位置织入我们定义好的handler这样就可以实现自定义的数据处理并返回了。我们可以很自由的在这里面定义我们想要的数据格式,做更多的事情。实现的细节可以看源码。

4.1.2.2 统一异常处理

有了统一的数据格式返回,我们常常会遇到一些异常,直接返回异常肯定是不可取的。所以我们需要对异常统一处理,这边只讲核心实现,细节看源码。实现的原理是利用spring-web提供的@ControllerAdvice controller增强器实现的。
第二章 基于spring-boot和spring-cloud应用型框架-CORE_第7张图片

package com.mars.fw.web.exception.advice;

import com.mars.fw.web.exception.KingException;
import com.mars.fw.web.reponse.ExceptionCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.regex.Pattern;

/**
 * 统一系统异常处理
 *
 * @Author King
 * @create 2020/4/20 15:25
 */
@Slf4j
@ControllerAdvice
public class KingExceptionHandlerAdvice {


    /**
     * 统一异常处理
     *
     * @param request
     * @param response
     * @param ex
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Object controllerExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) {

        String message = ExceptionCode.DEFAULT_EXCEPTION.message();

        if (KingException.class.isAssignableFrom(ex.getClass())) {
            KingException kingException = (KingException) ex;
            printExceptionLog(kingException, ex);
            return kingException.transferResponse();
        }

        if (ex instanceof MissingServletRequestParameterException) {
            MissingServletRequestParameterException exception = (MissingServletRequestParameterException) ex;
            message = "必填 " + exception.getParameterType() + "类型参数 '" + exception.getParameterName() + "' 不存在";
        } else if (ex instanceof EmptyResultDataAccessException) {
            EmptyResultDataAccessException exception = (EmptyResultDataAccessException) ex;
            String pattern = "No class.*with.*exists!.*";
            boolean isMatch = Pattern.matches(pattern, ex.getMessage());
            if (isMatch) {
                message = "数据不存在,请确认";
            }
        } else {
            if (null != ex.getMessage()) {
                message = "系统异常,联系管理员";
            }
        }

        KingException kingException = new KingException(ExceptionCode.DEFAULT_EXCEPTION, message);
        printExceptionLog(kingException, ex);
        if (log.isDebugEnabled()) {
            log.debug(ex.getMessage(), ex.getStackTrace());
        }
        return kingException.transferResponse();

    }


    /**
     * 错误日志
     *
     * @param kingException
     */
    private void printExceptionLog(KingException kingException, Exception ex) {
        if (kingException.getCause() == null) {
            log.error("######ErrorCode: {},message: {}#######", kingException.getCode().code(), kingException.getMessage());
        } else {
            log.error("######ErrorCode: {},message: {}#######", kingException.getCode().code(), kingException.getMessage(), kingException);
        }
        log.error("######ErrorCode: {},message: {}#######", kingException.getCode().code(), kingException.getMessage(), ex);
    }

}

基于此我们根据团队自定义的异常,根据ExceptionHandler处理指定异常。

4.1.2.3 在线文档实现

API文档是很重要的,统一的API文档更为重要,不要研发们另外维护一套文档我觉得是更重要的。所以在线文档就显得很迫切,这边采用的是swagger2的升级版:knife4j-spring-ui
首先在pom中引入包:

  <!--框架其他核心功能依赖的第三方包-swagger -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-ui</artifactId>
        </dependency>
  <!--框架其他核心功能依赖的第三方包-swagger -->
package com.mars.fw.web.doc;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @author King
 * @description swagger相关配置类
 * @date 2020/4/20
 */
@EnableSwagger2
@Configuration
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(true)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.mars"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {

        return new ApiInfoBuilder()
                .title("King 自定义 DOC 文档")
                .description("king-swagger-api")
                .termsOfServiceUrl("http://localhost:9098")
                .version("v0.1")
                .build();
    }
}

第二章 基于spring-boot和spring-cloud应用型框架-CORE_第8张图片

4.1.2.4 上下文会话

在微服务中现在几乎都是无状态的,与往常的session不同;更多的是使用token来做用户的信息认证,那么接口的用户信息的全局读取我们就要做一些处理。实现的方式也很多,我这边使用的是利用ThreadLocal,来做全局信息的保存。接口授权后,会将用户等全局信息保存到ThreadLocal中;ThreadLocal我们知道它是线程的副本,意味着每个线程都持有,是隔离的,在线程的生命周期中的任何地方都能取到。基于这些就能满足我们的需要,其实spring的事务,也是利用ThreaLocal的特性,当然有人可能会考虑到ThreadLocal的内存泄露问题,不过只要在实现的时候注意就可以避免这个问题;这边不做详细阐述,后续会在单独章节讲解这个。ok,我们来看下实现:
第二章 基于spring-boot和spring-cloud应用型框架-CORE_第9张图片
首先先定义一个全局的GlobalEntry 作为数据存储的载体,里面可以根据需要在自己的团队中定义;然后定义一个上下文KingContext,作为数据的管理类;紧接着在定义一个KingContextProvider接口,里面是可以定义Entry的获取方法,由业务侧根据需要做具体实现,这边主要是想约束GlobalEntry获取的实现 因为GlobalEntry是比较敏感或者说能影响全局的数据。

package com.mars.fw.web.context;

import com.mars.fw.web.exception.KingException;
import org.apache.commons.lang.StringUtils;

/**
 * 封装会话上下文 利用的是ThreadLocal的特性
 * 这个是使用来管理接口生命周期内的全局变量的 最多的应用场景是在 用户信息的存储
 * 

* 这边的封装想尽量简化调用者的复杂度 * * @Author King * @create 2020/4/21 17:27 */ public class KingContext { private static ThreadLocal<GlobalEntry> CONTEXT = new ThreadLocal<GlobalEntry>(); public static void setContext(GlobalEntry context) { CONTEXT.set(context); } public static void removeContext() { CONTEXT.remove(); } /** * 获取用户ID * * @return */ public static Long getUserId() { GlobalEntry entry = CONTEXT.get(); Long userId = null == entry ? null : entry.getUserId(); if (null == userId) { throw new KingException("请先登录"); } return userId; } /** * 获取用户Code * * @return */ public static String getUserCode() { GlobalEntry entry = CONTEXT.get(); String userCode = null == entry ? null : entry.getUserCode(); if (StringUtils.isBlank(userCode)) { throw new KingException("请先登录"); } return userCode; } /** * 获取token * * @return */ public static String getToken() { GlobalEntry entry = CONTEXT.get(); String token = null == entry ? null : entry.getToken(); if (org.springframework.util.StringUtils.isEmpty(token)) { throw new KingException("请先登录"); } return token; } /** * 获取全局存储的信息 * * @return */ public static Object getObject() { GlobalEntry entry = CONTEXT.get(); Object object = null == entry ? null : entry.getObject(); if (org.springframework.util.StringUtils.isEmpty(object)) { throw new KingException("会话上下文没有设置"); } return object; } }

ok,这张的篇幅有点长,其他内容放到下章接着讲。
源码地址:码云源码地址
上一章
下一章

你可能感兴趣的:(#,应用类,spring,spring,boot,java,分布式)