封装springboot基础框架(含demo)

12 篇文章 0 订阅

    写此篇博文目的有2个:

        一、我想做一个最小化框架,包括配置、拦截器,工具包等等都一应具备,使开发可以直接开始编写业务代码,所以我的demo的名字叫做springboot-base。

        二、这也是为了以后介绍springcloud做的一个铺垫。

    该框架所有设置均来自本人工作实际开发经验总结出来的。好,废话不多说,我们开始吧!


一、springboot介绍

    java一直给人很“重”的感觉,从ssh到s2sh到ssm,都离不开大量的xml文件以及搭建tomcat,所以springboot应运而生。springboot其实本质还是ssh(springmvc、spring、jpa(底层是hibernate)),它给这些框架赋予默认的配置(可手动修改),使得你的项目可以不需要任何xml就可以运行起来。当然它内嵌了tomcat,所以可以java -jar就可以运行,无需手动装tomcat。

二、自定义封装框架介绍

    ①对request请求,添加拦截器。继承webMvcConfiguerAdapter,将自定义的拦截器注入即可。

      该拦截器主要5个功能:

        1、如果有OpenInterface注解(自定义注解)则跳过拦截,主要用于第三方回调,或者接口直接调用。

        2、获取前端app_name、app_version,这两个参数主要用于验证app版本是否需要更新,如果是web项目这两个参数可以不要,最后把他们写到MDC去(springboot默认logback,MDC是sle4j的一个记录线程数据工具,底层实现是用threadlocal)

        3、添加追踪信息到MDC里去。traceId作用类似于springcloud的sleuth,可以在查询日志时候,以该关键字拉取某个请求全部日志。用uuid生成,保证每个请求的traceId不一样。

        4、如果网页需要切换语言,把下面注释的部分打开就可以了,一个做语言国际化标准。

        5、token校验,此处是自己写的token校验,如果大家用shiro或者spring security这个可以不用。主要功能还是判断是否登入,不需要该功能的可以删除。

    总结:这里对请求做了格式要求。把基础信息写在header里,把token写在cookie里,这也是我一直以来做项目的习惯,大家有不同习惯可以自行更改。

@Configuration
public class GlobalRequestHandler extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(requestInterceptor());
    }

    public HandlerInterceptorAdapter requestInterceptor() {
        return new HandlerInterceptorAdapter() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                //OpenInterface注释的接口不拦截
                if (((HandlerMethod) handler).getMethod().isAnnotationPresent(OpenInterface.class)) {
                    return true;
                }

                interceptorHandler(request, handler);

                return true;
            }
        };
    }

    private void interceptorHandler(HttpServletRequest request, Object handler) {
        //获取请求信息
        String appName = request.getHeader(Constants.APP_NAME);
        String appVersion = request.getHeader(Constants.APP_VERSION);
        //String lang = request.getHeader(Constants.LANG);
        if (StringUtils.isEmpty(appName) || StringUtils.isEmpty(appVersion)) {
            throw new SystemException(ExceptionCode.HANDLER_PARAM_ERROR.getCode(), "handler param is null!");
        }

        //清空线程缓存
        MDC.clear();

        //设置请求信息
        MDC.put(Constants.APP_NAME, appName);
        MDC.put(Constants.APP_VERSION, appVersion);
        MDC.put("traceId", UUID.randomUUID().toString());
        //语言国际化,有需要开启
        //LocaleContextHolder.setLocale(StringUtils.isEmpty(lang) ? Constants.DEFAULT_LANG : Locale.forLanguageTag(lang));

        //检查token,标记UncheckToken不检查token
        if (!((HandlerMethod) handler).getMethod().isAnnotationPresent(UncheckToken.class)) {
            String token = "";
            token = Utils.getCookieByName(request.getCookies(), Constants.COOKILE_TOKEN);

            //TODO 校验token步骤
            //if (StringUtils.isEmpty(token) &&  ){
            //    throw new SystemException(ExceptionCode.NEED_LOGIN.getCode(), "token is over time");
            //}
        }
    }

}

    ②封装response返回格式

        此处修改了reponse返回格式。效果:不管返回的是对象还是string都统一格式化成code、data、msg。

        这里之所以把springmvc的源码进行修改,是因为如果返回string类型,那么如果你把它格式化后会报错,原因是springmvc认为string类型应该用string信息解析器,但是你却把它封装成了个对象,导致无法解析。

@RestControllerAdvice
public class GlobalResponseHandler extends WebMvcRegistrationsAdapter{

    /*
    *  WebMvcRegistrationsAdapter通过getRequestMappingHandlerAdapter获取RequestMappingHandlerAdapter。
    *  RequestMappingHandlerAdapter重写ReturnValueHandlers方法
    *  传入的视图解析器是jackson
    *  注:ReturnValueHandlers调用视图转换器,所以在此处替换returnValue
    */

    @Override
    public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter requestMappingHandlerAdapter = new RequestMappingHandlerAdapter();
        List<HandlerMethodReturnValueHandler> handlerList = new ArrayList<>();
        handlerList.add(new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())){
            @Override
            public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
                super.handleReturnValue(packageResult(returnValue), returnType, mavContainer, webRequest);
            }
        });
        requestMappingHandlerAdapter.setReturnValueHandlers(handlerList);
        return requestMappingHandlerAdapter;
    }

    private Object packageResult(Object data){
        Response response = new Response();
        response.setData(data);
        return response;
    }
}

    ③全局异常拦截

        作用和上面差不多,只是上面的是对response格式化,这里是全局异常拦截并且格式化。很简单,不讲解。

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Throwable.class)
    public Response handlerException(Throwable throwable) {
        Response response = new Response();
        response.setMeg(throwable.getMessage());
        response.setCode(ExceptionCode.SYSTEM_ERROR.getCode());

        return getError(throwable, response);
    }

    private Response getError(Throwable throwable, Response response){

        if (throwable instanceof ServiceException){
            response.setCode(((ServiceException)throwable).getCode());
        }else if (throwable instanceof NoHandlerFoundException) {
            response.setCode(ExceptionCode.NO_HANDLER_ERROR.getCode());
        } else if (throwable instanceof HttpMessageNotReadableException) {
            response.setCode(ExceptionCode.PARAM_TYPE_ERROR.getCode());
        }

        return response;
    }
}

    ④日志(logback)

        springboot默认是用logback,如果用log4j朋友也可以换,但是强烈建议你用logback。

        我用的是logback.groovy,更多人用的是logback.xml。这两个可以相互转的,但是我喜欢groovy的简洁。这里唯一需要提的就是logstash是对接ELK的,所以FILE-INFO用该格式化,可以直接对接ELK。

def final LOCATION = "/data/log/app/"
def final SERVER_NAME = "base"
def final SAVE_TIME_RANGE = 7
String ENV = System.getProperty("env")

if (StringUtils.isEmpty(ENV)) {
    appender('CONSOLE', ConsoleAppender) {
        encoder(PatternLayoutEncoder) {
            pattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} %relative [%thread] %-5level %logger{36} %X{requestId} - %msg%n"
        }
    }

    root(INFO, ['CONSOLE'])

} else {
    appender('FILE-INFO', RollingFileAppender) {
        rollingPolicy(TimeBasedRollingPolicy) {
            fileNamePattern = String.format("%s%s/%s%s%s", LOCATION, SERVER_NAME, "logFile.", ENV, ".%d{yyyy-MM-dd}.log.gz")
            maxHistory = SAVE_TIME_RANGE
        }

        encoder(LogstashEncoder) {
            includeMdcKeyNames = ["traceId"]
        }
    }

    root(INFO, ['FILE-INFO'])
}

注意点:要想运行该项目必须要装mysql,主要我集成了数据库,方便大家把框架拿过来改改名字就能直接用。端口默认是9000,数据库信息等都在application.yml里修改。

该项目还有很多我自己写的工具方法等等,在我日常工作中还将不断的添砖加瓦,谢谢!

github地址:https://github.com/wangsunqun/springboot-base.git

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值