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

Springboot整合SpringSecurity實(shí)現(xiàn)登錄認(rèn)證和鑒權(quán)全過程

 更新時(shí)間:2023年12月11日 10:02:13   作者:德魯伊的扛把子  
這篇文章主要介紹了Springboot整合SpringSecurity實(shí)現(xiàn)登錄認(rèn)證和鑒權(quán)全過程,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一、Springboot整合SpringSecurity實(shí)現(xiàn)登錄認(rèn)證

1、springsecurity是通過在web一系列原生filter攔截器中增加自己的過濾器鏈來攔截web請求,然后請求會在經(jīng)過過濾器鏈的過程中會完成認(rèn)證與授權(quán),如果中間發(fā)現(xiàn)這條請求未認(rèn)證或者未授權(quán),會根據(jù)被保護(hù)API的權(quán)限去拋出異常,然后由異常處理器去處理這些異常。

在這里插入圖片描述

2、SpringSecurity通過FilterChainProxy管理眾多SecurityFilterChain, 而FilterChainProxy則被DelegatingFilterProxy管理并被DelegatingFilterProxy放入web原生的過濾器鏈中;

每個(gè)SecurityFilterChain下則是具體的擁有攔截規(guī)則的filter,這些filter由SpringSecurity進(jìn)行代理操作,可以理解為他是"Security Filter",而不是原生的"Web Filter";

總結(jié)就是:

【DelegatingFilterProxy】——管理——>【FilterChainProxy】——管理——>【SecurityFilterChain】——管理——>【Security Filter】

3、springboot整合springsecurity,springboot會通過一系列xxxAutoConfiguration進(jìn)行自動配置默認(rèn)的Spring Security的一系列底層組件,如WebSecurityConfigurerAdapter和一些默認(rèn)組件,有些"Security Filter"會自動開啟,有些則不會;

整個(gè)認(rèn)證的過程其實(shí)一直在圍繞圖中過濾鏈的綠色部分,而動態(tài)鑒權(quán)主要是圍繞其橙色部分;

Spring Security配置中有兩個(gè)叫formLogin和httpBasic的配置項(xiàng),這兩個(gè)配置項(xiàng)就分別對應(yīng)著圖中分的兩個(gè)過濾器

  • formLogin對應(yīng)著你form表單認(rèn)證方式,即UsernamePasswordAuthenticationFilter。
  • httpBasic對應(yīng)著Basic認(rèn)證方式,即BasicAuthenticationFilter。

圖片

4、我使用的就是UsernamePasswordAuthenticationFilter這個(gè)過濾器,springboot整合springsecurity時(shí)會自動加載這個(gè)過濾器;

在這里插入圖片描述

Spring Security 在自動裝配后,會有默認(rèn)的攔截策略,未登陸的請求都會被攔截并跳轉(zhuǎn)到login登錄頁,此時(shí)輸入賬號密碼登錄就會被這個(gè)UsernamePasswordAuthenticationFilter攔截,并驗(yàn)證賬號是否存在,密碼是否正確

進(jìn)入formlogin,發(fā)現(xiàn)有個(gè)**FormLoginConfigurer()**方法

在這里插入圖片描述

進(jìn)入FormLoginConfigurer()方法,在這里用戶輸入賬號密碼就會被這個(gè)UsernamePasswordAuthenticationFilter攔截,并驗(yàn)證進(jìn)行認(rèn)證

在這里插入圖片描述

發(fā)送登陸請求后,UsernamePasswordAuthenticationFilter會調(diào)用attemptAuthentication() 方法進(jìn)行認(rèn)證,失敗則拋出異常,成功則返回帶有用戶信息的Authentication對象

在這里插入圖片描述

"Security Filter"中,認(rèn)證過程是由 " 主角 " AuthenticationManager(接口)去管理AuthenticationProvider(接口)去實(shí)現(xiàn)的,AuthenticationManager可以有多個(gè),他們?nèi)绻J(rèn)證失敗就會調(diào)用父親也就是全局的AuthenticationManager再去認(rèn)證看看,一般只用一個(gè)全局的.

一個(gè)AuthenticationProvider代表一種認(rèn)證方法,只要其中一個(gè)AuthenticationProvider認(rèn)證通過就算登陸成功,記住兩個(gè)主角的實(shí)現(xiàn)類ProviderManager和DaoAuthenticationProvider

在這里插入圖片描述

回到attemptAuthentication()方法,調(diào)用拿到全局的AuthenticationMananger去執(zhí)行*authenticate()*方法,拿到ProviderManager中所有的AuthenticationProvider,交給他們?nèi)フJ(rèn)證

在這里插入圖片描述

在這里插入圖片描述

在遍歷provider這個(gè)過程中,調(diào)用了provider(DaoAuthenticationProvider)的authenticate方法,由provider去認(rèn)證,AuthenticationProvider的實(shí)現(xiàn)類DaoAuthenticationProvider繼承了AbstractUserDetailsAuthenticationProvider,所以自然也有父類方法的*authenticate()方法,因?yàn)闆]有重寫他,所以在源碼debug階段會進(jìn)入了他的父類的authenticate()方法,他的父類AbstractUserDetailsAuthenticationProvider實(shí)現(xiàn)了AuthenticationProvider

在這里插入圖片描述

在provider(DaoAuthenticationProvider)的authenticate()方法中,先調(diào)用retrieveUser()通過用戶名來獲取我們存儲中是否有該用戶,如果有就封裝到UserDetail中,后面再拿請求中的密碼跟UserDetail用戶信息中的密碼進(jìn)行比較,如果沒有,密碼都不用比較了,因?yàn)橛脩舾静淮嬖?provider中有個(gè)叫UserDetailService的接口,通過用戶名可以獲取我們的用戶數(shù)據(jù)(他功能相當(dāng)于一個(gè)service層去調(diào)用dao層最終返回用戶數(shù)據(jù)),在自動裝配中,默認(rèn)配了個(gè)基于內(nèi)存存儲的InMemoryUserDetailsManager,他是UserDetailService的實(shí)現(xiàn)類;

在這里插入圖片描述

所以在使用springsecurity進(jìn)行登錄認(rèn)證的時(shí)候,除了要創(chuàng)建配置類進(jìn)行相關(guān)內(nèi)容的配置,還要創(chuàng)建UserDetailService的實(shí)現(xiàn)類用于到數(shù)據(jù)庫中查詢登錄認(rèn)證所需要的信息;

并且還要創(chuàng)建UserDetail的實(shí)現(xiàn)類用于封裝查詢出來的數(shù)據(jù),并把數(shù)據(jù)交給springsecurity框架拿去用于認(rèn)證

在這里插入圖片描述

在這里插入圖片描述

最后通過additionalAuthenticationChecks()方法進(jìn)行密碼比較

在這里插入圖片描述

在這里插入圖片描述

認(rèn)證失敗拋異常,認(rèn)證成功則將用戶詳細(xì)信息封裝進(jìn)Authentication返回

在這里插入圖片描述

二、Springboot整合SpringSecurity實(shí)現(xiàn)鑒權(quán)

1、整個(gè)認(rèn)證的過程其實(shí)一直在圍繞圖中過濾鏈的綠色部分,而現(xiàn)在要說的動態(tài)鑒權(quán)主要是圍繞其橙色部分,也就是圖上標(biāo)的:FilterSecurityInterceptor

圖片

2、想知道怎么動態(tài)鑒權(quán)首先我們要搞明白SpringSecurity的鑒權(quán)邏輯,從上圖中我們也可以看出:一個(gè)請求完成了認(rèn)證,且沒有拋出異常之后就會到達(dá)FilterSecurityInterceptor所負(fù)責(zé)的鑒權(quán)部分,也就是說鑒權(quán)的入口就在FilterSecurityInterceptor。

先來看看FilterSecurityInterceptor的定義和主要方法:

public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements
  Filter {
 
            public void doFilter(ServletRequest request, ServletResponse response,
                    FilterChain chain) throws IOException, ServletException {
                FilterInvocation fi = new FilterInvocation(request, response, chain);
                invoke(fi);
            }
}

上文代碼可以看出FilterSecurityInterceptor是抽象類AbstractSecurityInterceptor的一個(gè)實(shí)現(xiàn)類,這個(gè)AbstractSecurityInterceptor中預(yù)先寫好了一段很重要的代碼(后面會說到)。

FilterSecurityInterceptor的主要方法是doFilter方法,請求過來之后會執(zhí)行這個(gè)doFilter方法,F(xiàn)ilterSecurityInterceptor的doFilter方法出奇的簡單,總共只有兩行:

  • 第一行是創(chuàng)建了一個(gè)FilterInvocation對象,這個(gè)FilterInvocation對象你可以當(dāng)作它封裝了request,它的主要工作就是拿請求里面的信息,比如請求的URI和method
  • 第二行就調(diào)用了自身的invoke方法,并將FilterInvocation對象傳入

所以我們主要邏輯肯定是在這個(gè)invoke方法里面了,我們來打開看看:

public void invoke(FilterInvocation fi) throws IOException, ServletException {
        if ((fi.getRequest() != null)
                && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
                && observeOncePerRequest) {
            // filter already applied to this request and user wants us to observe
            // once-per-request handling, so don't re-do security checking
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        }
        else {
            // first time this request being called, so perform security checking
            if (fi.getRequest() != null && observeOncePerRequest) {
                fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
            }
 
            // 進(jìn)入鑒權(quán)
            InterceptorStatusToken token = super.beforeInvocation(fi);
 
            try {
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            }
            finally {
                super.finallyInvocation(token);
            }
 
            super.afterInvocation(token, null);
        }
    }

invoke方法中只有一個(gè)if-else,一般都是不滿足if中的那三個(gè)條件的,然后執(zhí)行邏輯會來到else。

else的代碼也可以概括為兩部分:

  • 調(diào)用了super.beforeInvocation(fi)。
  • 調(diào)用完之后過濾器繼續(xù)往下走。

第二步可以不看,每個(gè)過濾器都有這么一步,所以我們主要看super.beforeInvocation(fi),前文我已經(jīng)說過, FilterSecurityInterceptor實(shí)現(xiàn)了抽象類AbstractSecurityInterceptor, 所以這個(gè)里super其實(shí)指的就是AbstractSecurityInterceptor, 那這段代碼其實(shí)調(diào)用了AbstractSecurityInterceptor.beforeInvocation(fi), 前文我說過AbstractSecurityInterceptor中有一段很重要的代碼就是這一段, 那我們繼續(xù)來看這個(gè)beforeInvocation(fi)方法的源碼:

protected InterceptorStatusToken beforeInvocation(Object object) {
        Assert.notNull(object, "Object was null");
        final boolean debug = logger.isDebugEnabled();
 
        if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
            throw new IllegalArgumentException(
                    "Security invocation attempted for object "
                            + object.getClass().getName()
                            + " but AbstractSecurityInterceptor only configured to support secure objects of type: "
                            + getSecureObjectClass());
        }
 
        Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
                .getAttributes(object);
 
        Authentication authenticated = authenticateIfRequired();
 
        try {
            // 鑒權(quán)需要調(diào)用的接口
            this.accessDecisionManager.decide(authenticated, object, attributes);
        }
        catch (AccessDeniedException accessDeniedException) {
            publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
                    accessDeniedException));
 
            throw accessDeniedException;
        }
 
    }

源碼較長,這段代碼大致可以分為三步:

拿到了一個(gè)Collection對象,這個(gè)對象是一個(gè)List,其實(shí)里面是通過我們在配置文件中配置的過濾規(guī)則獲取到請求需要的角色權(quán)限。

 public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

拿到了Authentication,這里是調(diào)用authenticateIfRequired方法拿到了,其實(shí)里面是通過SecurityContextHolder拿到的

Authentication authenticated = authenticateIfRequired();

調(diào)用了accessDecisionManager.decide(authenticated, object, attributes),前兩步都是對decide方法做參數(shù)的準(zhǔn)備,第三步才是正式去到鑒權(quán)的邏輯,既然這里面才是真正鑒權(quán)的邏輯,那也就是說鑒權(quán)其實(shí)是accessDecisionManager在做。

 // 鑒權(quán)需要調(diào)用的接口
            this.accessDecisionManager.decide(authenticated, object, attributes);

AccessDecisionManager是一個(gè)接口,它聲明了三個(gè)方法,除了第一個(gè)decide()鑒權(quán)方法以外,還有兩個(gè)是輔助性的方法,其作用都是甄別 decide方法中參數(shù)的有效性。

那既然是一個(gè)接口,上文中所調(diào)用的肯定是他的實(shí)現(xiàn)類了

它主要有三個(gè)實(shí)現(xiàn)類,分別代表了三種不同的鑒權(quán)邏輯:

  • AffirmativeBased:一票通過,只要有一票通過就算通過,默認(rèn)是它。
  • UnanimousBased:一票反對,只要有一票反對就不能通過。
  • ConsensusBased:少數(shù)票服從多數(shù)票。

這里的表述為什么要用票呢?因?yàn)樵趯?shí)現(xiàn)類里面采用了委托的形式,將請求委托給投票器,每個(gè)投票器拿著這個(gè)請求根據(jù)自身的邏輯來計(jì)算出能不能通過然后進(jìn)行投票,所以會有上面的表述。

也就是說這三個(gè)實(shí)現(xiàn)類,其實(shí)還不是真正判斷請求能不能通過的類,真正判斷請求是否通過的是投票器,然后實(shí)現(xiàn)類把投票器的結(jié)果綜合起來來決定到底能不能通過。

剛剛已經(jīng)說過,實(shí)現(xiàn)類把投票器的結(jié)果綜合起來進(jìn)行決定,也就是說投票器可以放入多個(gè),每個(gè)實(shí)現(xiàn)類里的投票器數(shù)量取決于構(gòu)造的時(shí)候放入了多少投票器,我們可以看看默認(rèn)的AffirmativeBased的源碼。

public class AffirmativeBased extends AbstractAccessDecisionManager {
 
    public AffirmativeBased(List<AccessDecisionVoter<?>> decisionVoters) {
        super(decisionVoters);
    }
 
    // 拿到所有的投票器,循環(huán)遍歷進(jìn)行投票
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
        int deny = 0;
 
        for (AccessDecisionVoter voter : getDecisionVoters()) {
            int result = voter.vote(authentication, object, configAttributes);
 
            if (logger.isDebugEnabled()) {
                logger.debug("Voter: " + voter + ", returned: " + result);
            }
 
            switch (result) {
                case AccessDecisionVoter.ACCESS_GRANTED:
                    return;
 
                case AccessDecisionVoter.ACCESS_DENIED:
                    deny++;
 
                    break;
 
                default:
                    break;
            }
        }
 
        if (deny > 0) {
            throw new AccessDeniedException(messages.getMessage(
                    "AbstractAccessDecisionManager.accessDenied", "Access is denied"));
        }
 
        // To get this far, every AccessDecisionVoter abstained
        checkAllowIfAllAbstainDecisions();
    }
}

AffirmativeBased的構(gòu)造是傳入投票器List,其主要鑒權(quán)邏輯交給投票器去判斷,投票器返回不同的數(shù)字代表不同的結(jié)果,然后AffirmativeBased根據(jù)自身一票通過的策略決定放行還是拋出異常。

AffirmativeBased默認(rèn)傳入的構(gòu)造器只有一個(gè)->WebExpressionVoter,這個(gè)構(gòu)造器會根據(jù)你在配置文件中的配置進(jìn)行邏輯處理得出投票結(jié)果。

所以SpringSecurity默認(rèn)的鑒權(quán)邏輯就是根據(jù)配置文件中的配置進(jìn)行鑒權(quán),這是符合我們現(xiàn)有認(rèn)知的

3、總結(jié)一下就是:

FilterSecurityInterceptor執(zhí)行doFilter 方法創(chuàng)建FilterInvocation(req,resp,chain)對象;然后調(diào)用自身invoke方法,傳入對象

invoke方法中,在 chain().doFilter 前有 super.beforeInvocation(fi),調(diào)用 AbstractSecurityInterceptor 的beforeInvocation方法

beforeInvocation方法中

  • 通過調(diào)用請求過濾接口obtainSecurityMetadataSource() 的getAttributes()方法獲取一個(gè)Collection對象,這個(gè)對象是一個(gè)list,里面封裝了請求所需要的角色權(quán)限
  • 調(diào)用authenticateIfRequired方法拿到Authentication對象
  • 調(diào)用了accessDecisionManager.decide(authenticated, object, attributes)正式進(jìn)行鑒權(quán)

4、在使用springsecurity進(jìn)行鑒權(quán)操作的時(shí)候,根據(jù)具體業(yè)務(wù)需求去自定義請求過濾器obtainSecurityMetadataSource()和投票器accessDecisionManager()

  • 自定義請求過濾器,重寫getAttributes()方法
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 修改接口角色關(guān)系后重新加載
        if (CollectionUtils.isEmpty(resourceRoleList)) {
            this.loadDataSource();
        }

        //Spring Security 通過FilterInvocation對object進(jìn)行封裝,可以安全的拿到其HttpServletRequest 和 HttpServletResponse對象
        FilterInvocation fi = (FilterInvocation) object;
        // 獲取用戶請求方式
        String method = fi.getRequest().getMethod();
        // 獲取用戶請求Url
        String url = fi.getRequest().getRequestURI();


        //new一個(gè)工具類AntPathMatcher的實(shí)例化對象,把路徑匹配委托給AntPathMatcher實(shí)現(xiàn)
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        // 獲取接口角色信息,若為匿名接口則放行,若無對應(yīng)角色則禁止
        for (ResourceRoleDTO resourceRoleDTO : resourceRoleList) {
            //判斷resourceRoleList中是否有和參數(shù)對象的URL和method完全相同的對象
            if (antPathMatcher.match(resourceRoleDTO.getUrl(), url) && resourceRoleDTO.getRequestMethod().equals(method)) {
                //如果有對象匹配成功,則獲取該對象的角色列表RoleList
                List<String> roleList = resourceRoleDTO.getRoleList();
                if (CollectionUtils.isEmpty(roleList)) {
                    return SecurityConfig.createList("disable");
                }
                return SecurityConfig.createList(roleList.toArray(new String[]{})); //rolelist集合轉(zhuǎn)換成String數(shù)組,通過SecurityConfig.createList(str)對結(jié)果進(jìn)行封裝,然后return
            }
        }
        return null;
    }

自定義投票器

@Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
        // 獲取用戶權(quán)限列表
        List<String> permissionList = authentication.getAuthorities()
                .stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList());
        for (ConfigAttribute item : collection) {
            //item.getAttribute()獲取當(dāng)前用戶訪問資源所需要的權(quán)限
            //如果用戶權(quán)限列表中包含該權(quán)限,則return,否則最后會提示沒有操作權(quán)限
            if (permissionList.contains(item.getAttribute())) {
                return;
            }
        }
        throw new AccessDeniedException("沒有操作權(quán)限");
    }

Config文件中,調(diào)用 postProcess 方法將自定義的請求過濾器和投票器注冊到 Spring 容器中去

 http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O fsi) {
                        fsi.setSecurityMetadataSource(securityMetadataSource()); //設(shè)置請求攔截規(guī)則
                        fsi.setAccessDecisionManager(accessDecisionManager());  //設(shè)置訪問決策管理器,真正的鑒權(quán)操作在這里完成
                        return fsi;
                    }
                })

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java實(shí)現(xiàn)阿里云短信接口的示例

    Java實(shí)現(xiàn)阿里云短信接口的示例

    這篇文章主要介紹了Java實(shí)現(xiàn)阿里云短信接口的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java中Arrays.sort自定義一維數(shù)組、二維數(shù)組的排序方式

    Java中Arrays.sort自定義一維數(shù)組、二維數(shù)組的排序方式

    這篇文章主要介紹了Java中Arrays.sort自定義一維數(shù)組、二維數(shù)組的排序方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • 淺談java的TCP和UDP編程(附實(shí)例講解)

    淺談java的TCP和UDP編程(附實(shí)例講解)

    下面小編就為大家?guī)硪黄獪\談java的TCP和UDP編程(附實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • 從application.properties配置文件獲取的漢字亂碼的解決方法

    從application.properties配置文件獲取的漢字亂碼的解決方法

    平時(shí)從配置文件各種讀取配置參數(shù)都正常,但是有時(shí)候放了個(gè)中文就亂碼,你肯定試過網(wǎng)上好多方法,都沒解決,那么來看下面,恭喜你終于找這里了,本文給大家介紹了從application.properties配置文件獲取的漢字亂碼的解決方法,需要的朋友可以參考下
    2024-03-03
  • Mybatis中一條SQL使用兩個(gè)foreach的問題及解決

    Mybatis中一條SQL使用兩個(gè)foreach的問題及解決

    這篇文章主要介紹了Mybatis中一條SQL使用兩個(gè)foreach的問題及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 淺談Spring中@NotEmpty、@NotBlank、@NotNull區(qū)別

    淺談Spring中@NotEmpty、@NotBlank、@NotNull區(qū)別

    本文主要介紹了淺談Spring中@NotEmpty、@NotBlank、@NotNull區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • IDEA下SpringBoot指定環(huán)境、配置文件啟動操作過程

    IDEA下SpringBoot指定環(huán)境、配置文件啟動操作過程

    這篇文章主要介紹了IDEA下SpringBoot指定環(huán)境、配置文件啟動過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08
  • SpringBoot如何通過Map實(shí)現(xiàn)策略模式

    SpringBoot如何通過Map實(shí)現(xiàn)策略模式

    策略模式是一種行為設(shè)計(jì)模式,它允許在運(yùn)行時(shí)選擇算法的行為,在Spring框架中,我們可以利用@Resource注解和Map集合來優(yōu)雅地實(shí)現(xiàn)策略模式,這篇文章主要介紹了SpringBoot如何通過Map實(shí)現(xiàn)策略模式,需要的朋友可以參考下
    2025-03-03
  • IDEA 2021配置JavaWeb項(xiàng)目超詳細(xì)教程

    IDEA 2021配置JavaWeb項(xiàng)目超詳細(xì)教程

    本文通過圖文并茂的形式給大家介紹IDEA 2021配置JavaWeb項(xiàng)目的過程,內(nèi)容簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2021-08-08
  • Java禁止使用finalize方法

    Java禁止使用finalize方法

    這篇文章主要介紹了Java禁止使用finalize方法,需要的朋友可以參考下
    2017-10-10

最新評論

枣阳市| 偏关县| 马尔康县| 滨海县| 南部县| 区。| 淳化县| 盐边县| 武威市| 潼南县| 北宁市| 五指山市| 张北县| 龙山县| 肥乡县| 四会市| 灌南县| 桃源县| 河西区| 修武县| 桂东县| 罗江县| 崇左市| 双流县| 博罗县| 新蔡县| 永善县| 广宗县| 古交市| 宁晋县| 广西| 陇川县| 大理市| 察雅县| 渑池县| 砚山县| 屯留县| 兴业县| 辽阳县| 察隅县| 台南县|