最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot集成shiro自定義登陸過濾器方法

 更新時間:2022年08月05日 10:26:59   作者:夢想實現(xiàn)家_Z  
這篇文章主要介紹了springboot集成shiro自定義登陸過濾器方法,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

前言

在上一篇博客springboot集成shiro權限管理簡單實現(xiàn)中,用戶在登錄的過程中,有以下幾個問題:

  • 用戶在沒有登陸的情況下,訪問需要權限的接口,服務器自動跳轉到登陸頁面,前端無法控制;
  • 用戶在登錄成功后,服務器自動跳轉到成功頁,前端無法控制;
  • 用戶在登錄失敗后,服務器自動刷新登錄頁面,前端無法控制;

很顯然,這樣的交互方式,用戶體驗上不是很好,并且在某些程度上也無法滿足業(yè)務上的要求。所以,我們要對默認的FormAuthenticationFilter進行覆蓋,實現(xiàn)我們自定義的Filter來解決用戶交互的問題。

自定義UsernamePasswordAuthenticationFilter

首先我們需要繼承原先的FormAuthenticationFilter

之所以繼承這個FormAuthenticationFilter,有以下幾點原因:

1.FormAuthenticationFilter是默認攔截登錄功能的過濾器,我們本身就是要改造登錄功能,所以繼承它很正常;

2.我們自定義的Filter需要復用里面的邏輯;

public class UsernamePasswordAuthenticationFilter extends FormAuthenticationFilter{}

其次,為了解決第一個問題,我們需要重寫saveRequestAndRedirectToLogin方法

/**
 * 沒有登陸的情況下,訪問需要權限的接口,需要引導用戶登陸
 *
 * @param request
 * @param response
 * @throws IOException
 */
@Override
protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
    //  保存當前請求,以便后續(xù)登陸成功后重新請求
    this.saveRequest(request);
    // 1. 服務端直接跳轉
    // ? - 服務端重定向登陸頁面
    if (autoRedirectToLogin) {
       ?this.redirectToLogin(request, response);
    } else {
       ?// 2. json模式
       ?// ? - json數(shù)據(jù)格式告知前端需要跳轉到登陸頁面,前端根據(jù)指令跳轉登陸頁面
       ?HttpServletRequest req = (HttpServletRequest) request;
       ?HttpServletResponse res = (HttpServletResponse) response;
       ?Map<String, String> metaInfo = new HashMap<>();
       ?// 告知前端需要跳轉的登陸頁面
       ?metaInfo.put("loginUrl", getLoginUrl());
       ?// 告知前端當前請求的url;這個信息也可以保存在前端
       ?metaInfo.put("currentRequest", req.getRequestURL().toString());
       ?ResultWrap.failure(802, "請登陸后再操作!", metaInfo)
          .writeToResponse(res);
    }
}

在這個方法中,我們通過配置autoRedirectToLogin參數(shù)的方式,既保留了原來服務器自動跳轉的功能,又增強了服務器返回json給前端,讓前端根據(jù)返回結果跳轉到登陸頁面的功能。這樣就增強了應用程序的可控性和靈活性了。

重寫登陸成功的處理方法onLoginSuccess:

@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
    // 查詢當前用戶自定義的登陸成功需要跳轉的頁面,可以更加靈活控制用戶頁面跳轉
    String successUrl = loginSuccessPageFetch.successUrl(token, subject);
    // 如果沒有自定義的成功頁面,那么跳轉默認成功頁
    if (StringUtils.isEmpty(successUrl)) {
       ?successUrl = this.getSuccessUrl();
    }
    if (loginSuccessAutoRedirect) {
       ?// 服務端直接重定向到目標頁面
       ?WebUtils.redirectToSavedRequest(request, response, successUrl);
    } else {
       ?SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request);
       ?if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase("GET")) {
         ? ?successUrl = savedRequest.getRequestUrl();
        }
       ?// 返回json數(shù)據(jù)格式告知前端跳轉目標頁面
       ?HttpServletResponse res = (HttpServletResponse) response;
       ?Map<String, String> data = new HashMap<>();
       ?// 登陸成功后跳轉的目標頁面
       ?data.put("successUrl", successUrl);
       ?ResultWrap.success(data).writeToResponse(res);
    }
    return false;
}

1.登陸成功后,我們內(nèi)置了一個個性化的成功頁,用于保證針對不同的用戶會有定制化的登陸成功頁。

2.通過自定義的loginSuccessAutoRedirect屬性來決定用戶登陸成功后是直接由服務端控制頁面跳轉還是返回json讓前端控制交互行為。

3.我們在用戶登陸成功后,會獲取前面保存的請求,以便用戶在登錄成功后能直接回到登錄前點擊的頁面。

重寫用戶登錄失敗的方法onLoginFailure:

@Override
  protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
    if (log.isDebugEnabled()) {
      log.debug("Authentication exception", e);
    }
    this.setFailureAttribute(request, e);
    if (!loginFailureAutoRedirect) {
      // 返回json數(shù)據(jù)格式告知前端跳轉目標頁面
      HttpServletResponse res = (HttpServletResponse) response;
      ResultWrap.failure(803, "用戶名或密碼錯誤,請核對后無誤后重新提交!", null).writeToResponse(res);
    }
    return true;
  }

登陸失敗我們使用自定義屬性loginFailureAutoRedirect來控制失敗的動作是由服務端直接跳轉頁面還是返回json由前端控制用戶交互。

在這個方法的邏輯里面沒有看到跳轉的功能,是因為我們直接把父類的默認實現(xiàn)拿過來了,在原有的邏輯上做了修改。既然默認是服務端跳轉的功能,那么我們只需要補充返回json的功能即可。

覆蓋默認的FormAuthenticationFilter

現(xiàn)在我們已經(jīng)寫好了自定義的用戶名密碼登陸過濾器,下面我們就把它加入到shiro的配置中去,這樣才能生效:

@Bean
  public ShiroFilterFactoryBean shiroFilterFactoryBean() {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager());
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    // 設置不需要權限的url
    String[] permitUrls = properties.getPermitUrls();
    if (ArrayUtils.isNotEmpty(permitUrls)) {
      for (String permitUrl : permitUrls) {
        filterChainDefinitionMap.put(permitUrl, "anon");
      }
    }
    // 設置退出的url
    String logoutUrl = properties.getLogoutUrl();
    filterChainDefinitionMap.put(logoutUrl, "logout");
 ? ?// 設置需要權限驗證的url
    filterChainDefinitionMap.put("/**", "authc");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    // 設置提交登陸的url
    String loginUrl = properties.getLoginUrl();
    shiroFilterFactoryBean.setLoginUrl(loginUrl);
    // 設置登陸成功跳轉的url
    String successUrl = properties.getSuccessUrl();
    shiroFilterFactoryBean.setSuccessUrl(successUrl);
    // 添加自定義Filter
    shiroFilterFactoryBean.setFilters(customFilters());
    return shiroFilterFactoryBean;
  }
?
/**
   * 自定義過濾器
   *
   * @return
   */
  private Map<String, Filter> customFilters() {
    Map<String, Filter> filters = new LinkedHashMap<>();
    // 自定義FormAuthenticationFilter,用于管理用戶登陸的,包括登陸成功后的動作、登陸失敗的動作
    // 可查看org.apache.shiro.web.filter.mgt.DefaultFilter,可覆蓋里面對應的authc
    UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter();
    // 不允許服務器自動控制頁面跳轉
    usernamePasswordAuthenticationFilter.setAutoRedirectToLogin(false);
    usernamePasswordAuthenticationFilter.setLoginSuccessAutoRedirect(false);
    usernamePasswordAuthenticationFilter.setLoginFailureAutoRedirect(false);
    filters.put("authc", usernamePasswordAuthenticationFilter);
    return filters;
  }

上面的代碼重點看 【添加自定義Filte】 ,其實原理就是把默認的authc過濾器給覆蓋掉,換成我們自定義的過濾器,這樣的話,我們的過濾器才能生效。

完整UsernamePasswordAuthenticationFilter代碼

import com.example.awesomespring.vo.ResultWrap;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;
?
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
?
/**
 * @author zouwei
 * @className UsernamePasswordAuthenticationFilter
 * @date: 2022/8/2 上午12:14
 * @description:
 */
@Data
@Slf4j
public class UsernamePasswordAuthenticationFilter extends FormAuthenticationFilter {
  //  如果用戶沒有登陸的情況下訪問需要權限的接口,服務端是否自動調(diào)整到登陸頁面
  private boolean autoRedirectToLogin = true;
  // 登陸成功后是否自動跳轉
  private boolean loginSuccessAutoRedirect = true;
  // 登陸失敗后是否跳轉
  private boolean loginFailureAutoRedirect = true;
  /**
   * 個性化定制每個登陸成功的賬號跳轉的url
   */
  private LoginSuccessPageFetch loginSuccessPageFetch = new LoginSuccessPageFetch(){};
?
  public UsernamePasswordAuthenticationFilter() {
  }
?
  /**
   * 沒有登陸的情況下,訪問需要權限的接口,需要引導用戶登陸
   *
   * @param request
   * @param response
   * @throws IOException
   */
  @Override
  protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
    //  保存當前請求,以便后續(xù)登陸成功后重新請求
    this.saveRequest(request);
    // 1. 服務端直接跳轉
    // ? - 服務端重定向登陸頁面
    if (autoRedirectToLogin) {
      this.redirectToLogin(request, response);
    } else {
      // 2. json模式
      // ? - json數(shù)據(jù)格式告知前端需要跳轉到登陸頁面,前端根據(jù)指令跳轉登陸頁面
      HttpServletRequest req = (HttpServletRequest) request;
      HttpServletResponse res = (HttpServletResponse) response;
      Map<String, String> metaInfo = new HashMap<>();
      // 告知前端需要跳轉的登陸頁面
      metaInfo.put("loginUrl", getLoginUrl());
      // 告知前端當前請求的url;這個信息也可以保存在前端
      metaInfo.put("currentRequest", req.getRequestURL().toString());
      ResultWrap.failure(802, "請登陸后再操作!", metaInfo)
          .writeToResponse(res);
    }
  }
?
  @Override
  protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
    // 查詢當前用戶自定義的登陸成功需要跳轉的頁面,可以更加靈活控制用戶頁面跳轉
    String successUrl = loginSuccessPageFetch.successUrl(token, subject);
    // 如果沒有自定義的成功頁面,那么跳轉默認成功頁
    if (StringUtils.isEmpty(successUrl)) {
      successUrl = this.getSuccessUrl();
    }
    if (loginSuccessAutoRedirect) {
      // 服務端直接重定向到目標頁面
      WebUtils.redirectToSavedRequest(request, response, successUrl);
    } else {
      SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request);
      if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase("GET")) {
        successUrl = savedRequest.getRequestUrl();
      }
      // 返回json數(shù)據(jù)格式告知前端跳轉目標頁面
      HttpServletResponse res = (HttpServletResponse) response;
      Map<String, String> data = new HashMap<>();
      // 登陸成功后跳轉的目標頁面
      data.put("successUrl", successUrl);
      ResultWrap.success(data).writeToResponse(res);
    }
    return false;
  }
?
  @Override
  protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
    if (log.isDebugEnabled()) {
      log.debug("Authentication exception", e);
    }
    this.setFailureAttribute(request, e);
    if (!loginFailureAutoRedirect) {
      // 返回json數(shù)據(jù)格式告知前端跳轉目標頁面
      HttpServletResponse res = (HttpServletResponse) response;
      ResultWrap.failure(803, "用戶名或密碼錯誤,請核對后無誤后重新提交!", null).writeToResponse(res);
    }
    return true;
  }
  /**
   * 針對不同的人員登陸成功后有不同的跳轉頁面而設計
   */
  public interface LoginSuccessPageFetch {
?
    default String successUrl(AuthenticationToken token, Subject subject) {
      return StringUtils.EMPTY;
    }
  }
}

ResultWrap.java

import com.example.awesomespring.util.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
?
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
?
/**
 * @author zouwei
 * @className ResultWrap
 * @date: 2022/8/2 下午2:02
 * @description:
 */
@Data
@AllArgsConstructor
public class ResultWrap<T, M> {
  //  方便前端判斷當前請求處理結果是否正常
  private int code;
  //  業(yè)務處理結果
  private T data;
  //  產(chǎn)生錯誤的情況下,提示用戶信息
  private String message;
  //  產(chǎn)生錯誤情況下的異常堆棧,提示開發(fā)人員
  private String error;
  //  發(fā)生錯誤的時候,返回的附加信息
  private M metaInfo;
?
  /**
   * 成功帶處理結果
   *
   * @param data
   * @param <T>
   * @return
   */
  public static <T> ResultWrap success(T data) {
    return new ResultWrap(HttpStatus.OK.value(), data, StringUtils.EMPTY, StringUtils.EMPTY, null);
  }
?
  /**
   * 成功不帶處理結果
   *
   * @return
   */
  public static ResultWrap success() {
    return success(HttpStatus.OK.name());
  }
?
  /**
   * 失敗
   *
   * @param code
   * @param message
   * @param error
   * @return
   */
  public static <M> ResultWrap failure(int code, String message, String error, M metaInfo) {
    return new ResultWrap(code, null, message, error, metaInfo);
  }
?
  /**
   * 失敗
   *
   * @param code
   * @param message
   * @param error
   * @param metaInfo
   * @param <M>
   * @return
   */
  public static <M> ResultWrap failure(int code, String message, Exception error, M metaInfo) {
    return failure(code, message, error.getStackTrace().toString(), metaInfo);
  }
?
  /**
   * 失敗
   *
   * @param code
   * @param message
   * @param error
   * @return
   */
  public static ResultWrap failure(int code, String message, Exception error) {
    String errorMessage = StringUtils.EMPTY;
    if (Objects.nonNull(error)) {
      errorMessage = error.getStackTrace().toString();
    }
    return failure(code, message, errorMessage, null);
  }
?
  /**
   * 失敗
   *
   * @param code
   * @param message
   * @param metaInfo
   * @param <M>
   * @return
   */
  public static <M> ResultWrap failure(int code, String message, M metaInfo) {
    return failure(code, message, StringUtils.EMPTY, metaInfo);
  }
?
  private static final String APPLICATION_JSON_VALUE = "application/json;charset=UTF-8";
?
  /**
   * 把結果寫入響應中
   *
   * @param response
   */
  public void writeToResponse(HttpServletResponse response) {
    int code = this.getCode();
    if (Objects.isNull(HttpStatus.resolve(code))) {
      response.setStatus(HttpStatus.OK.value());
    } else {
      response.setStatus(code);
    }
    response.setContentType(APPLICATION_JSON_VALUE);
    try (PrintWriter writer = response.getWriter()) {
      writer.write(JsonUtil.obj2String(this));
      writer.flush();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

JsonUtil.java

?import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
?
import java.util.Objects;
?
/**
 * @author zouwei
 * @className JsonUtil
 * @date: 2022/8/2 下午3:08
 * @description:
 */
@Slf4j
public final class JsonUtil {
?
  /** 防止使用者直接new JsonUtil() */
  private JsonUtil() {}
?
  private static ObjectMapper objectMapper = new ObjectMapper();
?
  static {
    // 對象所有字段全部列入序列化
    objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
    /** 所有日期全部格式化成時間戳 因為即使指定了DateFormat,也不一定能滿足所有的格式化情況,所以統(tǒng)一為時間戳,讓使用者按需轉換 */
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
    /** 忽略空Bean轉json的錯誤 假設只是new方式創(chuàng)建對象,并且沒有對里面的屬性賦值,也要保證序列化的時候不報錯 */
    objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    /** 忽略反序列化中json字符串中存在,但java對象中不存在的字段 */
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  }
?
  /**
   * 對象轉換成json字符串
   *
   * @param obj
   * @param <T>
   * @return
   */
  public static <T> String obj2String(T obj) {
    return obj2String(obj, null);
  }
  /**
   * 對象轉換成json字符串
   *
   * @param obj
   * @param <T>
   * @return
   */
  public static <T> String obj2String(T obj, String defaultValue) {
    if (Objects.isNull(obj)) {
      return defaultValue;
    }
    try {
      return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
    } catch (Exception e) {
      log.warn("Parse object to String error", e);
      // 即使序列化出錯,也要保證程序走下去
      return null;
    }
  }
?
  /**
   * 對象轉json字符串(帶美化效果)
   *
   * @param obj
   * @param <T>
   * @return
   */
  public static <T> String obj2StringPretty(T obj) {
    if (Objects.isNull(obj)) {
      return null;
    }
    try {
      return obj instanceof String
          ? (String) obj
          : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
    } catch (Exception e) {
      log.warn("Parse object to String error", e);
      // 即使序列化出錯,也要保證程序走下去
      return null;
    }
  }
?
  /**
   * json字符串轉簡單對象
   *
   * @param <T>
   * @param json
   * @param clazz
   * @return
   */
  public static <T> T string2Obj(String json, Class<T> clazz) {
    if (StringUtils.isEmpty(json) || Objects.isNull(clazz)) {
      return null;
    }
    try {
      return clazz.equals(String.class) ? (T) json : objectMapper.readValue(json, clazz);
    } catch (Exception e) {
      log.warn("Parse String to Object error", e);
      // 即使序列化出錯,也要保證程序走下去
      return null;
    }
  }
?
  /**
   * json字符串轉復雜對象
   *
   * @param json
   * @param typeReference 例如:new TypeReference<List<User>>(){}
   * @param <T> 例如:List<User>
   * @return
   */
  public static <T> T string2Obj(String json, TypeReference<T> typeReference) {
    if (StringUtils.isEmpty(json) || Objects.isNull(typeReference)) {
      return null;
    }
    try {
      return (T)
          (typeReference.getType().equals(String.class)
              ? (T) json
              : objectMapper.readValue(json, typeReference));
    } catch (Exception e) {
      log.warn("Parse String to Object error", e);
      // 即使序列化出錯,也要保證程序走下去
      return null;
    }
  }
?
  /**
   * json字符串轉復雜對象
   *
   * @param json
   * @param collectionClass 例如:List.class
   * @param elementClasses 例如:User.class
   * @param <T> 例如:List<User>
   * @return
   */
  public static <T> T string2Obj(
      String json, Class<?> collectionClass, Class<?>... elementClasses) {
    if (StringUtils.isEmpty(json)
        || Objects.isNull(collectionClass)
        || Objects.isNull(elementClasses)) {
      return null;
    }
    JavaType javaType =
        objectMapper
            .getTypeFactory()
            .constructParametricType(collectionClass, elementClasses);
    try {
      return objectMapper.readValue(json, javaType);
    } catch (Exception e) {
      log.warn("Parse String to Object error", e);
      // 即使序列化出錯,也要保證程序走下去
      return null;
    }
  }
}

這樣在shiro中如何實現(xiàn)更靈活的登陸控制就編寫完畢了。后面會陸續(xù)講解我在使用shiro時遇到的其他問題,以及相應的解決方案。

到此這篇關于springboot集成shiro自定義登陸過濾器方法的文章就介紹到這了,更多相關springboot集成shiro 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java?Spring的使用注解開發(fā)詳解

    Java?Spring的使用注解開發(fā)詳解

    這篇文章主要為大家介紹了Java?Spring注解開發(fā),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • java中循環(huán)刪除list中元素的方法總結

    java中循環(huán)刪除list中元素的方法總結

    下面小編就為大家?guī)硪黄猨ava中循環(huán)刪除list中元素的方法總結。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • Java日常練習題,每天進步一點點(55)

    Java日常練習題,每天進步一點點(55)

    下面小編就為大家?guī)硪黄狫ava基礎的幾道練習題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,希望可以幫到你
    2021-08-08
  • MyBatis之insert主鍵自增和自定義主鍵詳解

    MyBatis之insert主鍵自增和自定義主鍵詳解

    本文介紹了如何使用MyBatis解決插入數(shù)據(jù)時因主鍵唯一性約束導致的錯誤問題,以及如何自定義主鍵生成規(guī)則,文中詳細解釋了如何在MyBatis中配置自增主鍵,并提供了測試示例
    2024-12-12
  • java線程池詳解及代碼介紹

    java線程池詳解及代碼介紹

    這篇文章主要介紹了java中線程池的示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-08-08
  • Java使用C3P0數(shù)據(jù)源鏈接數(shù)據(jù)庫

    Java使用C3P0數(shù)據(jù)源鏈接數(shù)據(jù)庫

    這篇文章主要為大家詳細介紹了Java使用C3P0數(shù)據(jù)源鏈接數(shù)據(jù)庫,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • 如何設置Spring Boot測試時的日志級別

    如何設置Spring Boot測試時的日志級別

    Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發(fā)過程。該框架使用了特定的方式來進行配置,從而使開發(fā)人員不再需要定義樣板化的配置。下面我們來一起學習一下吧
    2019-06-06
  • 解析java中的error該不該捕獲

    解析java中的error該不該捕獲

    這篇文章主要介紹了java中的error該不該捕獲,需要的朋友可以參考下
    2014-02-02
  • Java幾個實例帶你進階升華上篇

    Java幾個實例帶你進階升華上篇

    與其明天開始,不如現(xiàn)在行動,本文為你帶來幾個Java書寫的實際案例,對鞏固編程的基礎能力很有幫助,快來一起往下看看吧
    2022-03-03
  • 20秒教你學會java?List函數(shù)排序操作示例

    20秒教你學會java?List函數(shù)排序操作示例

    這篇文章主要為大家介紹了20秒教你學會List函數(shù)排序操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09

最新評論

龙州县| 武强县| 黄石市| 汉川市| 耒阳市| 保亭| 曲水县| 桐城市| 泽普县| 宜阳县| 台北市| 成武县| 盐源县| 伊春市| 沾化县| 五莲县| 蒲城县| 二手房| 鞍山市| 崇文区| 岳阳市| 阿拉尔市| 梁山县| 河曲县| 兴和县| 昭苏县| 深圳市| 塘沽区| 邵阳县| 孟连| 彭阳县| 重庆市| 镇远县| 城口县| 正宁县| 竹溪县| 永定县| 衢州市| 贡觉县| 九龙县| 平定县|