先看效果
寫本專案的目的有幾點:
- 學習下vue+electron桌面開發
- 學習下java和spring開發(本人一直使用PHP)
- 一直缺少一款能適合自己的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());
}
}