亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

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

在這里插入圖片描述

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

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

總結(jié)就是:

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

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

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

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

  • formLogin對(duì)應(yīng)著你form表單認(rèn)證方式,即UsernamePasswordAuthenticationFilter。
  • httpBasic對(duì)應(yīng)著B(niǎo)asic認(rèn)證方式,即BasicAuthenticationFilter。

圖片

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

在這里插入圖片描述

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

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

在這里插入圖片描述

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

在這里插入圖片描述

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

在這里插入圖片描述

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

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

在這里插入圖片描述

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

在這里插入圖片描述

在這里插入圖片描述

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

在這里插入圖片描述

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

在這里插入圖片描述

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

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

在這里插入圖片描述

在這里插入圖片描述

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

在這里插入圖片描述

在這里插入圖片描述

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

在這里插入圖片描述

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

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

圖片

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

先來(lái)看看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ù)先寫(xiě)好了一段很重要的代碼(后面會(huì)說(shuō)到)。

FilterSecurityInterceptor的主要方法是doFilter方法,請(qǐng)求過(guò)來(lái)之后會(huì)執(zhí)行這個(gè)doFilter方法,F(xiàn)ilterSecurityInterceptor的doFilter方法出奇的簡(jiǎn)單,總共只有兩行:

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

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

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í)行邏輯會(huì)來(lái)到else。

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

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

第二步可以不看,每個(gè)過(guò)濾器都有這么一步,所以我們主要看super.beforeInvocation(fi),前文我已經(jīng)說(shuō)過(guò), FilterSecurityInterceptor實(shí)現(xiàn)了抽象類AbstractSecurityInterceptor, 所以這個(gè)里super其實(shí)指的就是AbstractSecurityInterceptor, 那這段代碼其實(shí)調(diào)用了AbstractSecurityInterceptor.beforeInvocation(fi), 前文我說(shuō)過(guò)AbstractSecurityInterceptor中有一段很重要的代碼就是這一段, 那我們繼續(xù)來(lái)看這個(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;
        }
 
    }

源碼較長(zhǎng),這段代碼大致可以分為三步:

拿到了一個(gè)Collection對(duì)象,這個(gè)對(duì)象是一個(gè)List,其實(shí)里面是通過(guò)我們?cè)谂渲梦募信渲玫倪^(guò)濾規(guī)則獲取到請(qǐng)求需要的角色權(quán)限。

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

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

Authentication authenticated = authenticateIfRequired();

調(diào)用了accessDecisionManager.decide(authenticated, object, attributes),前兩步都是對(duì)decide方法做參數(shù)的準(zhǔn)備,第三步才是正式去到鑒權(quán)的邏輯,既然這里面才是真正鑒權(quán)的邏輯,那也就是說(shuō)鑒權(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:一票通過(guò),只要有一票通過(guò)就算通過(guò),默認(rèn)是它。
  • UnanimousBased:一票反對(duì),只要有一票反對(duì)就不能通過(guò)。
  • ConsensusBased:少數(shù)票服從多數(shù)票。

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

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

剛剛已經(jīng)說(shuō)過(guò),實(shí)現(xiàn)類把投票器的結(jié)果綜合起來(lái)進(jìn)行決定,也就是說(shuō)投票器可以放入多個(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ù)自身一票通過(guò)的策略決定放行還是拋出異常。

AffirmativeBased默認(rèn)傳入的構(gòu)造器只有一個(gè)->WebExpressionVoter,這個(gè)構(gòu)造器會(huì)根據(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)對(duì)象;然后調(diào)用自身invoke方法,傳入對(duì)象

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

beforeInvocation方法中

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

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

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

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


        //new一個(gè)工具類AntPathMatcher的實(shí)例化對(duì)象,把路徑匹配委托給AntPathMatcher實(shí)現(xiàn)
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        // 獲取接口角色信息,若為匿名接口則放行,若無(wú)對(duì)應(yīng)角色則禁止
        for (ResourceRoleDTO resourceRoleDTO : resourceRoleList) {
            //判斷resourceRoleList中是否有和參數(shù)對(duì)象的URL和method完全相同的對(duì)象
            if (antPathMatcher.match(resourceRoleDTO.getUrl(), url) && resourceRoleDTO.getRequestMethod().equals(method)) {
                //如果有對(duì)象匹配成功,則獲取該對(duì)象的角色列表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ù)組,通過(guò)SecurityConfig.createList(str)對(duì)結(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)前用戶訪問(wèn)資源所需要的權(quán)限
            //如果用戶權(quán)限列表中包含該權(quán)限,則return,否則最后會(huì)提示沒(méi)有操作權(quán)限
            if (permissionList.contains(item.getAttribute())) {
                return;
            }
        }
        throw new AccessDeniedException("沒(méi)有操作權(quán)限");
    }

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

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

總結(jié)

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

相關(guān)文章

  • Java中接口Set的特點(diǎn)及方法說(shuō)明

    Java中接口Set的特點(diǎn)及方法說(shuō)明

    這篇文章主要介紹了Java中接口Set的特點(diǎn)及方法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • IDEA實(shí)現(xiàn)序列化時(shí)如何自動(dòng)生成serialVersionUID的步驟

    IDEA實(shí)現(xiàn)序列化時(shí)如何自動(dòng)生成serialVersionUID的步驟

    這篇文章主要介紹了IDEA實(shí)現(xiàn)序列化時(shí)如何自動(dòng)生成serialVersionUID的步驟,首先安裝GenerateSerialVersionUID插件,當(dāng)出現(xiàn)添加serialVersionUID選項(xiàng),選中則會(huì)自動(dòng)生成serialVersionUID,感興趣的朋友一起學(xué)習(xí)下吧
    2024-02-02
  • java音樂(lè)播放器課程設(shè)計(jì)

    java音樂(lè)播放器課程設(shè)計(jì)

    這篇文章主要為大家詳細(xì)介紹了java音樂(lè)播放器的課程設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 如何安裝jdk及安裝MyEclipse的圖文教程

    如何安裝jdk及安裝MyEclipse的圖文教程

    這篇文章主要介紹了如何安裝jdk及安裝MyEclipse的圖文教程,需要的朋友可以參考下
    2018-03-03
  • IntelliJ IDEA安裝scala插件并創(chuàng)建scala工程的步驟詳細(xì)教程

    IntelliJ IDEA安裝scala插件并創(chuàng)建scala工程的步驟詳細(xì)教程

    這篇文章主要介紹了IntelliJ IDEA安裝scala插件并創(chuàng)建scala工程的步驟,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • JAVA中的日期時(shí)間類用法總結(jié)

    JAVA中的日期時(shí)間類用法總結(jié)

    這篇文章主要給大家介紹了關(guān)于JAVA中日期時(shí)間類用法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java中Request請(qǐng)求轉(zhuǎn)發(fā)詳解

    Java中Request請(qǐng)求轉(zhuǎn)發(fā)詳解

    這篇文章主要介紹了Java中Request請(qǐng)求轉(zhuǎn)發(fā)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出操作詳解

    Java實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出操作詳解

    在平常的辦公工作中,導(dǎo)入導(dǎo)出excel數(shù)據(jù)是常見(jiàn)的需求,今天就來(lái)看一下通過(guò)Java如何來(lái)實(shí)現(xiàn)這個(gè)功能,感興趣的朋友可以了解下
    2022-02-02
  • Mybatis中使用in()查詢的方式詳解

    Mybatis中使用in()查詢的方式詳解

    當(dāng)參數(shù)有值,添加條件查詢,附帶一個(gè)字符串的in查詢,下面這篇文章主要給大家介紹了關(guān)于Mybatis中使用in()查詢的方式,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • 一文詳解如何在Java中啟動(dòng)線程

    一文詳解如何在Java中啟動(dòng)線程

    今天要跟大家聊一聊Java多線程的原理和使用方式,Java多線程是提高程序性能的重要機(jī)制,了解線程生命周期、同步機(jī)制、線程池和線程間通信等概念,可以幫助我們編寫(xiě)出高效、可靠的多線程程序,需要的朋友可以參考下
    2024-07-07

最新評(píng)論