开源项目程序员Java

Springboot代码生成器V2.21版本更新——升级相关依赖

2021-01-10  本文已影响0人  小螺旋丸

前言

生成器下载地址:http://www.zrxlh.top:8088/coreCode/.
源码地址:https://gitee.com/zrxjava/codeMan

由于工作和生活等多方面的影响,半年多没有更新博客,代码生成器也没有继续维护,后来不断有朋友发私信催我更新,并且提出相关的优化建议,今天终于得空把代码生成器根据朋友们的建议整体改良了一下,以后我也会尽量保持健康的更新速度,希望可以对大家有所帮助!

全局跨域配置

之前生成的代码每一个controller都会加上@CrossOrigin,现在则采用了整体的跨域配置,前者可以灵活控制,后者更加简单直接,但跨域一般都是为了前后端联调方便,所以采用了后者,因为正式环境上的前后台一般会使用nginx统一做转发处理,把跨域的接口写成调本域的接口,然后将这些接口转发到真正的请求地址,也就不存在跨域的问题。
有一点需要注意(与跨域无关),现在新版goole浏览器加强了安全方面的监控,主要是为了防止注入类攻击,如果发现访问应用的地址和应用的父级地址不同源,登录时会无法设置cookie,如果使用session控制用户的登录,就会出现获取不到seesion的问题,因为无法设置cookie,就没有了seesionId,导致登录无效。对此也有相应的解决办法:
1.打开Chrome设置,将chrome://flags/#same-site-by-default-cookies禁用,然后重启浏览器即可;
2.采用token代替cokkie做验证,也就是我们常用的使用redis保存token做验证的方式。
跨域配置的代码如下:

/**
 * 跨域配置
 *
 * @author zrx
 */
@Configuration
public class CorsConfig {

    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("token");
        config.addAllowedHeader("Content-Type");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("OPTIONS");
        config.setMaxAge(1000L * 60 * 60);
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
        //过滤前会在拦截器之前执行,就不会被拦截器影响
        bean.setOrder(0);
        return bean;
    }
}

添加控制登录和日志记录的注解

在实际的项目开发中,往往一个项目后台有很多api,有些api需要登录鉴权,有些则不需要,为了灵活控制,采用注解的方式来对此进行控制,原理很简单,在拦截器中获取请求方法的注解信息,如果注解存在则进行登录验证,如不存在则直接放行,相关代码如下:

/**
 * 该注解用于REST API
 * 如果一个API需要用户用户登录,添加此注解
 *
 * @author zrx
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface LoginRequired {
}

@Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(new HandlerInterceptor() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws Exception {
                if (handler instanceof HandlerMethod) {
                    HandlerMethod handlerMethod = (HandlerMethod) handler;
                    LoginRequired loginRequired = handlerMethod.getMethodAnnotation(LoginRequired.class);
                    if (null == loginRequired) {
                        return true;
                    }
                    // 预请求
                    if (RequestMethod.OPTIONS.name().equals(request.getMethod())) {
                        return true;
                    }
                    HttpSession session = request.getSession();
                    User user = (User) session.getAttribute("user");
                    if (user == null) {
                        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
                        response.setHeader("Access-Control-Allow-Methods", "*");
                        response.setHeader("Access-Control-Max-Age", "3600");
                        response.setHeader("Access-Control-Allow-Credentials", "true");
                        response.setContentType("application/json; charset=utf-8");
                        response.setCharacterEncoding("utf-8");
                        PrintWriter pw = response.getWriter();
                        pw.write("{\"code\":" + HttpServletResponse.SC_UNAUTHORIZED + ",\"status\":\"no\",\"msg\":\"无授权访问,请先登录\"}");
                        pw.flush();
                        pw.close();
                        return false;
                    }
                }
                return true;

            }
        }).addPathPatterns("/**").excludePathPatterns("/login", "/register", "/login/doLogin", "/user/register",
                "/mystatic/**", "/druid/**", "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
    }

使用方法也及其简单,直接在controller的方法上添加此注解即可,例:

    /**
     * 查询
     *
     * @return
     */
    @ApiOperation(value = "查询")
    //添加此注解则表明调用此方法需要登录方可调用
    @LoginRequired
    @PostMapping(value = "/select")
    public List<TestTableEntity> select(@RequestBody TestTableEntity entity) {
        return service.select(entity);
    }

日志记录注解与登录注解同理,添加日志记录注解可以实现方法级别的日志记录,使用方法同上,代码如下:

/**
 * 需要记录的日志的注解
 * 在需要记录日志的controller上添加该注解,可以记录日志
 *
 * @author zrx
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RecordLog {
    String value() default "";
}

@Aspect
@Component
public class LogAopAspect {

    private static final Logger logger = LoggerFactory.getLogger(LogAopAspect.class);


    @Around("@annotation(bootdemo.core.annotation.RecordLog)")
    public Object process(ProceedingJoinPoint pjp) throws Throwable {
        Class<?> currentClass = pjp.getTarget().getClass();
        MethodSignature signature = (MethodSignature) (pjp.getSignature());
        String className = currentClass.getSimpleName();
        String methodName = currentClass.getMethod(signature.getName(), signature.getParameterTypes()).getName();
        logger.info("======= 开始执行:" + className + " — " + methodName + " ========");
        Object obj = pjp.proceed();
        logger.info("======= 执行结束:" + className + " — " + methodName + " ========");
        return obj;
    }
}

响应统一处理

之前代码生成器的响应已经做了一层抽取,把返回的信息统一封装到了对象当中,但没有做到完全解耦,现在对响应做了全局的进一步封装,controller只需要书写业务代码,不需要再去new一个响应体对象,进一步降低了耦合度,对于程序异常只需要throw相应的异常即可,统一处理类会封装异常信息给予前台用户提示,代码如下:

@Slf4j
@ControllerAdvice
@ResponseBody
public class AllExceptionHandler implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter returnType, Class clazz) {
        return null == returnType.getMethodAnnotation(NoPack.class);
    }
    /**
     * 响应返回之前对响应内容进行包装
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (MediaType.APPLICATION_JSON.equals(selectedContentType) || MediaType.APPLICATION_JSON_UTF8.equals(selectedContentType)) {
            Method method = (Method) returnType.getExecutable();
            if (ResponseEntity.class.equals(method.getReturnType())) {
                return body;
            }
            if (null == body) {
                return ResponseResult.success();
            }
            if (body instanceof ResponseResult) {
                return body;
            }
            return ResponseResult.success(body);
        }

        return body;
    }
    ....
    /**
     * 普通业务异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseResult businessExceptionHandler(HttpServletResponse response, BusinessException ex) {
        log.error(ex.getMessage(), ex);
        response.setStatus(ResponseStatus.BUSINESS_EXCEPTION.hCode);
        return ResponseResult.failed(ex.getCode(), ex.getMessage());
    }
    /**
     * 其他错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({Exception.class})
    public ResponseResult exception(HttpServletResponse response, Exception ex) {
        log.error(ResponseStatus.OTHER_EXCEPTION.valueLog, ex);
        response.setStatus(ResponseStatus.OTHER_EXCEPTION.hCode);
        return ResponseResult.failed(ResponseStatus.OTHER_EXCEPTION.bCode, ResponseStatus.OTHER_EXCEPTION.valueZh);
    }
}

/**
 * 请求响应体
 *
 * @author zrx
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ResponseResult implements Serializable {

    private static final long serialVersionUID = 6041766238120354185L;

    private int code;
    private String status;
    private String msg;
    private Object data;

    public static ResponseResult success() {
        return success(null);
    }

    public static ResponseResult success(Object data) {
        return ResponseResult.builder()
                .status(ResponseStatus.SUCCESS.valueEn)
                .msg(ResponseStatus.SUCCESS.valueZh)
                .code(ResponseStatus.SUCCESS.bCode)
                .data(data)
                .build();
    }

    public static ResponseResult failed() {
        return failed("失败");
    }

    public static ResponseResult failed(String msg) {
        return failed(ResponseStatus.FAILED.bCode, msg);
    }

    public static ResponseResult failed(int code, String msg) {
        return ResponseResult.builder()
                .status(ResponseStatus.FAILED.valueEn)
                .msg(msg)
                .code(code)
                .build();
    }
}   
/**
 * 该注解用于REST API
 *
 * 如果一个API的返回不需要被ResponseWrapper包装,添加此注解
 * 
 * @author zrx
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface NoPack {

}

代码如上所示,实现ResponseBodyAdvice接口中的beforeBodyWrite方法,即可对返回的内容进行包装,spring中无时无刻都在渗透着aop的核心思想。
如果不想包装响应内容,则可以在controller的方法上添加NoPack注解来实现,原理与上面提到的登录注解一样:实现ResponseBodyAdvice的supports方法,如果不存在NoPack注解,则对响应内容做包装,spring已经帮我们实现了整体的功能,我们只需要重写方法加入相关业务即可。

swagger优化

swagger官方的样式比较丑,所以添加了新的swagger样式依赖,样式如下:


swagger

依赖如下:

<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.5</version>
        </dependency>

其他更新

除了以上更新之外,生成器还升级了mysql的版本依赖至8.0.18,springboot版本至2.2.6.RELEASE,spring版本至5.2.5.RELEASE,添加了lombok支持,修复了一些已知的bug,下一步准备加入权限配置的相关代码生成,进一步完善现有功能。

生成的代码展示

v2.21版 代码展示

结语

本次更新介绍到这里就结束了,其中响应的统一处理个人认为还算是比较有意义的,也感谢大家提出的宝贵意见,工作忙,加上生活上的琐事,更新可能不会太及时,还望见谅,下次再见啦!

生成器下载地址:http://www.zrxlh.top:8088/coreCode/.
源码地址:https://gitee.com/zrxjava/codeMan

上一篇下一篇

猜你喜欢

热点阅读