先看效果

写本项目的目的有几点:

  1. 学习下vue+electron桌面开发
  2. 学习下java和spring开发(本人一直使用PHP)
  3. 一直缺少一款能适合自己的TODO LIST软件,能有桌面端的

可直接打包成dmg、exe 等二进制文件使用。

这是我打包后的效果。

技术栈

  • vue
  • quasar
  • electron
  • springboot
  • mysql

部分后端知识

自定义注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired { }

自定义一个loginRequired注解,标注是否需要登录

然后在拦截器里进行全局检测

        HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
LoginRequired classRequired = method.getDeclaringClass().getAnnotation(LoginRequired.class);
// 判断接口是否需要登录
LoginRequired methodRequired = method.getAnnotation(LoginRequired.class);
if (classRequired == null && methodRequired == null) {
return true;
}
appService.initSession(); //token 方式验证
if (request.getSession().getAttribute(App.SESSION_USER) != null) {
return true;
}

@RestControllerAdvice

利用@RestControllerAdvice注解进行全局控制器异常拦截

   /**
* ConstraintViolationException
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Response handleConstraintViolationException(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return new Response(ResponseRet.parametrErrror, "参数错误", errors);
} /**
* 违反约束异常 字段不为空等
*
* @param ex
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
public Response handHibernateException(ConstraintViolationException ex) {
return new Response(ResponseRet.dbExecuteFail, ex.getSQLException().toString());
} @ExceptionHandler(GenericJDBCException.class)
public Response handGenericJDBCException(GenericJDBCException ex) {
return new Response(ResponseRet.dbExecuteFail, ex.getSQLException().toString());
}

你可以全局处理entity定义的参数约束,或其他异常。

p6spy记录sql和耗时

    @Override
public void onAfterExecuteQuery(PreparedStatementInformation statementInformation, long timeElapsedNanos, SQLException e) {
App.sqlCount.set(App.sqlCount.get() + 1);
Long duration = timeElapsedNanos / 1000000;
App.sqlDuration.set(App.sqlDuration.get() + duration);
Log.info(String.format("执行sql || %s 耗时 %s ms", statementInformation.getSqlWithValues(), duration));
} @Override
public void onAfterExecuteUpdate(PreparedStatementInformation statementInformation, long timeElapsedNanos, int rowCount, SQLException e) {
App.sqlCount.set(App.sqlCount.get() + 1);
Log.info(App.sqlCount.get().toString());
Long duration = timeElapsedNanos / 1000000;
App.sqlDuration.set(App.sqlDuration.get() + duration);
String singleLineSql = statementInformation.getSqlWithValues().replaceAll("\n", "\\\\n");
Log.info(String.format("执行sql || %s 耗时 %s ms", singleLineSql, duration));
}

记录带所有参数的sql和执行耗时

继承DispatcherServlet记录请求参数

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
//创建一个 json 对象,用来存放 http 日志信息
ObjectNode rootNode = mapper.createObjectNode();
rootNode.put("uri", requestWrapper.getRequestURI());
rootNode.put("clientIp", requestWrapper.getRemoteAddr());
// rootNode.set("requestHeaders", mapper.valueToTree(getRequestHeaders(requestWrapper)));
String method = requestWrapper.getMethod();
String contentType = requestWrapper.getContentType();
rootNode.put("method", method);
try {
super.doDispatch(requestWrapper, responseWrapper);
} finally {
if (method.equals("GET") || method.equals("DELETE")) {
rootNode.set("request", mapper.valueToTree(requestWrapper.getParameterMap()));
} else if (contentType.equals("application/x-www-form-urlencoded")) {
rootNode.set("request", mapper.valueToTree(requestWrapper.getParameterMap()));
} else {
JsonNode newNode = mapper.readTree(requestWrapper.getContentAsByteArray());
rootNode.set("request", newNode);
} rootNode.put("status", responseWrapper.getStatus());
JsonNode newNode = mapper.readTree(responseWrapper.getContentAsByteArray());
rootNode.set("response", newNode); responseWrapper.copyBodyToResponse(); // rootNode.set("responseHeaders", mapper.valueToTree(getResponsetHeaders(responseWrapper)));
Log.info(rootNode.toString());
}
}

源码地址

最新文章

  1. How secure FB Messenger is?
  2. PHP裁剪图片并上传完整demo
  3. document.write()
  4. iOS—最全的真机测试教程
  5. SQLServer数据库字典维护方法
  6. 原创 | 《地狱边境》登顶50国iOS下载榜,恐怖游戏或是独立开发者突破口(转)
  7. POJ1463 Strategic game (最小点覆盖 or 树dp)
  8. 根据block取出space_id
  9. python Day 1 - 搭建开发环境
  10. setSingleChoiceItems和setPositiveButton两者触发时期
  11. 在DataTable数据类型最后增加一列,列名为“Column”,内容都为“AAA”
  12. linux大文件分包压缩和批量解压命令tar // tar 排除指定目录
  13. jdk源码阅读笔记-HashSet
  14. CTF最简单的Web题
  15. JS去除空格和换行的正则表达式(推荐)
  16. 随GCTY
  17. iOS常用代码总结
  18. HOW-TO GEEK SCHOOL
  19. PHP TS 和 NTS 版本选择
  20. Spring &lt;context:annotation-config/&gt; 解说(转)

热门文章

  1. Project facet Java version 1.7 is not supported.解决方法
  2. 【Notes_1】现代图形学入门——计算机图形学概述
  3. 微信的两种access_token总结,不能混淆
  4. 如何在 C# 中使用 const,readonly,static
  5. C#的常见集合接口提供的功能
  6. 通达OA 任意文件上传-2013/2015版本
  7. Nebula Storage 2.0 存储格式
  8. FreeBSD 12.2 阿里云镜像使用说明
  9. java基础:变量、常量与作用域
  10. 从Android手机的抢红包插件说起