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

SpringBoot集成Shiro+JWT(Hutool)完整代碼示例

 更新時(shí)間:2025年08月07日 10:54:38   作者:Rysxt_  
Apache Shiro是一個(gè)強(qiáng)大且易用的Java 安全框架,提供了認(rèn)證、授權(quán)、加密和會(huì)話管理功能,在現(xiàn)代應(yīng)用開(kāi)發(fā)中,Shiro 因其簡(jiǎn)單性和靈活性而被廣泛采用,下面通過(guò)本文給大家介紹SpringBoot集成Shiro+JWT(Hutool)完整代碼示例,感興趣的朋友跟隨小編一起看看吧

一、背景介紹

1.1 為什么使用Shiro?

Apache Shiro 是一個(gè)強(qiáng)大且易用的 Java 安全框架,提供了認(rèn)證、授權(quán)、加密和會(huì)話管理功能。在現(xiàn)代應(yīng)用開(kāi)發(fā)中,Shiro 因其簡(jiǎn)單性和靈活性而被廣泛采用:

  • ??簡(jiǎn)單易用??:相比 Spring Security,Shiro 的 API 更加直觀和簡(jiǎn)單
  • ??功能全面??:提供認(rèn)證、授權(quán)、會(huì)話管理、加密等企業(yè)級(jí)安全功能
  • ??輕量級(jí)??:不依賴任何容器,可以獨(dú)立運(yùn)行
  • ??業(yè)界規(guī)范??:被眾多企業(yè)采用,有豐富的社區(qū)支持和文檔

1.2 為什么需要雙Token?

在原有單Token方案基礎(chǔ)上引入 ??Access Token(訪問(wèn)令牌)?? 和 ??Refresh Token(刷新令牌)?? 的組合,解決以下問(wèn)題:

  • ??安全性??:Access Token 短期有效降低泄露風(fēng)險(xiǎn),Refresh Token 獨(dú)立存儲(chǔ)且過(guò)期時(shí)間長(zhǎng)
  • ??用戶體驗(yàn)??:自動(dòng)刷新 Access Token,用戶無(wú)感知續(xù)期
  • ??合規(guī)性??:符合 OAuth 2.0 標(biāo)準(zhǔn)流程

二、技術(shù)棧組成

技術(shù)組件作用版本要求
SpringBoot基礎(chǔ)框架3.x
Apache Shiro認(rèn)證和授權(quán)核心2.0.0+
Hutool-JWT令牌生成與驗(yàn)證5.8.24+

三、環(huán)境準(zhǔn)備

3.1 創(chuàng)建 SpringBoot 項(xiàng)目

<!-- pom.xml -->
<dependencies>
    <!-- Shiro核心依賴 -->
    <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <classifier>jakarta</classifier>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <classifier>jakarta</classifier>
            <version>${shiro.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <classifier>jakarta</classifier>
            <version>${shiro.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    <!-- Hutool-JWT -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.39</version>
    </dependency>
</dependencies>

四、核心代碼實(shí)現(xiàn)

4.1 JWT工具類(JwtUtil.java)

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.core.date.DateTime;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class JwtUtil {
    private static final String TOKEN_KEY = "sys:token:";
    private static final long ACCESS_EXPIRE = 1000 * 60 * 15; // 15分鐘
    /**
     * 獲取密鑰(可選,我這里做的是動(dòng)態(tài)配置的,可以根據(jù)需要寫死就行)
     * @return 密鑰
     */
    private static byte[] getJwtSSecret() {
        SysParamsService sysParamsService = SpringUtil.getBean(SysParamsService.class);
        String jwtSecret =  sysParamsService.getValue("jwt.secret", true);
        return jwtSecret.getBytes();
    }
    /**
     * 獲取刷新token過(guò)期時(shí)間-單位天(可選,我這里做的是動(dòng)態(tài)配置的,可以根據(jù)需要寫死就行)
     * @return 過(guò)期時(shí)間-單位天
     */
    private static int getRefreshExp() {
        SysParamsService sysParamsService = SpringUtil.getBean(SysParamsService.class);
        String refreshExp =  sysParamsService.getValue("jwt.exp", true);
        return Integer.parseInt(refreshExp);
    }
    // 生成雙Token
    public static Map<String, String> generateTokens(Long userId,String username) {
        Map<String, String> tokens = new HashMap<>();
        // Access Token
        tokens.put("accessToken", createToken(userId,username));
        // Refresh Token
        tokens.put("refreshToken", createRefreshToken(userId));
        return tokens;
    }
    public static String createToken(Long userId,String username) {
        Map<String, Object> payload = new HashMap<>();
        payload.put("userId", userId);
        payload.put("username", username);
        payload.put("type", "access");
        payload.put("exp", new DateTime(System.currentTimeMillis() + ACCESS_EXPIRE).getTime());
        return JWTUtil.createToken(payload, getJwtSSecret());
    }
    public static String createRefreshToken(Long userId) {
        Map<String, Object> payload = new HashMap<>();
        payload.put("userId", userId);
        payload.put("type", "refresh");
        String refreshToken = JWTUtil.createToken(payload, getJwtSSecret());
        RedisTemplate<String, String> redisTemplate = SpringUtil.getBean("redisTemplate", RedisTemplate.class);
        redisTemplate.opsForValue().set(TOKEN_KEY+userId, refreshToken, getRefreshExp(), TimeUnit.DAYS);
        return refreshToken;
    }
    // 刷新Token
    public static void refreshAccessToken(String refreshToken) {
        Assert.isTrue(JWTUtil.verify(refreshToken, getJwtSSecret()), "非法Token錯(cuò)誤");
        JWT jwt = JWTUtil.parseToken(refreshToken);
        Assert.isTrue(ObjectUtil.equals("refresh", jwt.getPayload("type")), "非法Token錯(cuò)誤");
        Long userId = (Long) jwt.getPayload("userId");
        RedisTemplate<String, String> redisTemplate = SpringUtil.getBean("redisTemplate", RedisTemplate.class);
        String redis_refreshToken = redisTemplate.opsForValue().get(TOKEN_KEY + userId);
        Assert.isTrue(ObjectUtil.equals(redis_refreshToken, refreshToken), "Token已過(guò)期");
        //可選 用于延長(zhǎng)緩存時(shí)間
        long expire = redisTemplate.getExpire(TOKEN_KEY + userId, TimeUnit.DAYS);
        if(expire == 0){
            redisTemplate.expire(TOKEN_KEY + userId, 7, TimeUnit.DAYS);
        }
    }
    /**
     * 從Token中獲取用戶Id
     * @param refreshToken JWT Token字符串
     * @return 用戶Id
     */
    public static Long getUserIdFromRefreshToken(String refreshToken) {
        Assert.isTrue(JWTUtil.verify(refreshToken, getJwtSSecret()), "非法Token錯(cuò)誤");
        JWT jwt = JWTUtil.parseToken(refreshToken);
        Assert.isTrue(ObjectUtil.equals("refresh", jwt.getPayload("type")), "非法Token錯(cuò)誤");
        Long userId = (Long) jwt.getPayload("userId");
        RedisTemplate<String, String> redisTemplate = SpringUtil.getBean("redisTemplate", RedisTemplate.class);
        long expire = redisTemplate.getExpire(TOKEN_KEY + userId, TimeUnit.DAYS);
        Assert.isTrue(expire >= 0, "Token已過(guò)期");
        String redis_refreshToken = redisTemplate.opsForValue().get(TOKEN_KEY + userId);
        Assert.isTrue(ObjectUtil.equals(redis_refreshToken, refreshToken), "Token已過(guò)期");
        return (Long) jwt.getPayload("userId");
    }
    /**
     * 從Token中獲取用戶Id
     * @param token JWT Token字符串
     * @return 用戶Id
     */
    public static Long getUserIdFromToken(String token) {
        Assert.isTrue(JWTUtil.verify(token, getJwtSSecret()), "非法Token錯(cuò)誤");
        JWT jwt = JWTUtil.parseToken(token);
        Assert.isTrue(ObjectUtil.equals(jwt.getPayload("type"),"access"), "非法Token錯(cuò)誤");
        Assert.isTrue(jwt.getPayload("exp")!=null && jwt.validate(0),"token已失效");
        return (Long) jwt.getPayload("userId");
    }
    /**
     * 從Token中獲取用戶名
     * @param token JWT Token字符串
     * @return 用戶名
     */
    public static String getUsernameFromToken(String token) {
        Assert.isTrue(JWTUtil.verify(token, getJwtSSecret()), "非法Token錯(cuò)誤");
        JWT jwt = JWTUtil.parseToken(token);
        Assert.isTrue(jwt.getPayload("exp")!=null && jwt.validate(0),"token已失效");
        return jwt.getPayload("username").toString();
    }
}

4.2 Shiro配置類(ShiroConfig.java)

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.config.ShiroFilterConfiguration;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import jakarta.servlet.Filter;
@Configuration
public class ShiroConfig {
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(false);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }
    @Bean("securityManager")
    public SecurityManager securityManager(Oauth2Realm oAuth2Realm, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(oAuth2Realm);
        securityManager.setSessionManager(sessionManager);
        securityManager.setRememberMeManager(null);
        return securityManager;
    }
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager, SysParamsService sysParamsService) {
        ShiroFilterConfiguration config = new ShiroFilterConfiguration();
        config.setFilterOncePerRequest(true);
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setShiroFilterConfiguration(config);
        Map<String, Filter> filters = new HashMap<>();
        // oauth過(guò)濾
        filters.put("oauth2", new Oauth2Filter());
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/v3/api-docs/**", "anon");
        filterMap.put("/doc.html", "anon");
        filterMap.put("/favicon.ico", "anon");
        filterMap.put("/refreshToken", "anon");
        filterMap.put("/login", "anon");
        filterMap.put("/**", "oauth2");
        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;
    }
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

4.3 注冊(cè)過(guò)濾器

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<DelegatingFilterProxy> shiroFilterRegistration() {
        FilterRegistrationBean<DelegatingFilterProxy> registration = new FilterRegistrationBean<>();
        registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        // 該值缺省為false,表示生命周期由SpringApplicationContext管理,設(shè)置為true則表示由ServletContainer管理
        registration.addInitParameter("targetFilterLifecycle", "true");
        registration.setEnabled(true);
        registration.setOrder(Integer.MAX_VALUE - 1);
        registration.addUrlPatterns("/*");
        return registration;
    }
}

4.4 注冊(cè)oauth2過(guò)濾器

public class Oauth2Filter extends AuthenticatingFilter {
    private static final Logger logger = LoggerFactory.getLogger(Oauth2Filter.class);
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        // 獲取請(qǐng)求token
        String token = getRequestToken((HttpServletRequest) request);
        if (StringUtils.isBlank(token)) {
            logger.warn("createToken:token is empty");
            return null;
        }
        return new Oauth2Token(token);
    }
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
            return true;
        }
        return false;
    }
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        // 獲取請(qǐng)求token,如果token不存在,直接返回401
        String token = getRequestToken((HttpServletRequest) request);
        if (StringUtils.isBlank(token)) {
            logger.warn("onAccessDenied:token is empty");
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setContentType("application/json;charset=utf-8");
            httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
            String json = JsonUtils.toJsonString(new Result<Void>().error(ErrorCode.UNAUTHORIZED));
            httpResponse.getWriter().print(json);
            return false;
        }
        return executeLogin(request, response);
    }
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request,
            ServletResponse response) {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
        try {
            Throwable throwable = e.getCause() == null ? e : e.getCause();
            Result<Void> r = new Result<Void>().error(ErrorCode.UNAUTHORIZED, throwable.getMessage());
            String json = JsonUtils.toJsonString(r);
            httpResponse.getWriter().print(json);
        } catch (IOException e1) {
        }
        return false;
    }
    /**
     * 獲取請(qǐng)求的token
     */
    private String getRequestToken(HttpServletRequest httpRequest) {
        String token = null;
        // 從header中獲取token
        String authorization = httpRequest.getHeader(Constant.AUTHORIZATION);
        if (StringUtils.isNotBlank(authorization) && authorization.startsWith("Bearer ")) {
            token = authorization.replace("Bearer ", "");
        }
        return token;
    }
}

4.5 認(rèn)證類

import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
@Component
public class Oauth2Realm extends AuthorizingRealm {
    @Resource
    private UserService userService;
    private static final Logger logger = LoggerFactory.getLogger(Oauth2Realm.class);
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof Oauth2Token;
    }
    /**
     * 授權(quán)(驗(yàn)證權(quán)限時(shí)調(diào)用)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        UserDetail user = (UserDetail) principals.getPrimaryPrincipal();
        // 用戶權(quán)限列表
        Set<String> permsSet = new HashSet<>();
        if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) {
            permsSet.add("sys:role:superAdmin");
            permsSet.add("sys:role:normal");
        } else {
            permsSet.add("sys:role:normal");
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permsSet);
        return info;
    }
    /**
     * 認(rèn)證(登錄時(shí)調(diào)用)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String accessToken = (String) token.getPrincipal();
        // 根據(jù)accessToken,查詢用戶信息
        Long userId = JwtUtil.getUserIdFromToken(accessToken);
        Assert.notNull(userId, "token已過(guò)期,請(qǐng)重新登入");
        // 查詢用戶信息
        SysUserEntity userEntity = userService.getUser(userId);
        // 轉(zhuǎn)換成UserDetail對(duì)象
        UserDetail userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class);
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userDetail, accessToken, getName());
        return info;
    }
}

4.6 token類

public class Oauth2Token implements AuthenticationToken {
    private String token;
    public Oauth2Token(String token) {
        this.token = token;
    }
    @Override
    public String getPrincipal() {
        return token;
    }
    @Override
    public Object getCredentials() {
        return token;
    }
}

4.7 Shiro獲取用戶信息工具類

public class SecurityUser {
    public static Subject getSubject() {
        try {
            return SecurityUtils.getSubject();
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * 獲取用戶信息
     */
    public static UserDetail getUser() {
        Subject subject = getSubject();
        if (subject == null) {
            return new UserDetail();
        }
        UserDetail user = (UserDetail) subject.getPrincipal();
        if (user == null) {
            return new UserDetail();
        }
        return user;
    }
    public static String getToken() {
        return getUser().getToken();
    }
    /**
     * 獲取用戶ID
     */
    public static Long getUserId() {
        return getUser().getId();
    }
}

五、雙Token實(shí)現(xiàn)原理

5.1 Token結(jié)構(gòu)對(duì)比

Token類型有效期存儲(chǔ)位置包含信息
Access Token15分鐘客戶端用戶ID、用戶名稱,類型,過(guò)期時(shí)間,角色(可選)、權(quán)限(可選)
Refresh Token7天客戶端,Redis用戶ID、類型

5.2 核心流程圖

六、完整代碼示例

6.1 登錄控制器(AuthController.java)

@RestController
public class AuthController {
    @PostMapping("/login")
    public Result login(@RequestBody LoginRequest request) {
        User user = userService.findByUsername(request.getUsername());
        if (user == null || !user.getPassword().equals(request.getPassword())) {
            return Result.error("賬號(hào)或密碼錯(cuò)誤");
        }
        String accessToken = JwtUtil.createAccessToken(
            user.getUsername(), 
            user.getId()
        );
        String refreshToken = JwtUtil.createRefreshToken(user.getUsername(),user.getId());
        return Result.success(Map.of(
            "accessToken", accessToken,
            "refreshToken", refreshToken
        ));
    }
    @PostMapping("/refreshToken")
    public Result<String> refreshToken(@RequestHeader("refreshToken") String refreshToken) {
        Long userId = JwtUtil.getUserIdFromRefreshToken(refreshToken);
        //if(userId==null)userId=1904748826795986946L;
        SysUserDTO user = sysUserService.getByUserId(userId);
        Assert.notNull(user, "token異常,非法登入");
        String newAccessToken = JwtUtil.createToken(user.getId(),user.getUsername());
        JwtUtil.refreshAccessToken(refreshToken);//自動(dòng)續(xù)租
        return new Result<String>().ok(newAccessToken);
    }
}

七、雙Token優(yōu)勢(shì)總結(jié)

??維度????單Token方案????雙Token方案??
??安全性??單一Token泄露風(fēng)險(xiǎn)高Access Token短期有效,Refresh Token雙存儲(chǔ)(可自動(dòng)延遲過(guò)期時(shí)間,并保證安全性)
??用戶體驗(yàn)??頻繁登錄/重新認(rèn)證自動(dòng)續(xù)期,用戶無(wú)感知
??合規(guī)性??不符合OAuth2.0標(biāo)準(zhǔn)完全遵循OAuth2.0標(biāo)準(zhǔn)流程

八 、補(bǔ)充

為確保系統(tǒng)的安全性,本方案采用了??單刷新Token綁定機(jī)制??。具體而言,每個(gè)用戶的刷新Token(Refresh Token)與單一設(shè)備或終端綁定。當(dāng)同一用戶在其他設(shè)備或終端上登錄時(shí),新的登錄操作將導(dǎo)致之前設(shè)備的刷新Token失效(通常伴隨Access Token的到期登入失效)。這種機(jī)制有效防止了同一賬戶在多設(shè)備間的異常并行登錄,增強(qiáng)了賬戶的安全性。

然而,根據(jù)不同的業(yè)務(wù)需求和安全策略,開(kāi)發(fā)者可以根據(jù)實(shí)際情況對(duì)Token管理機(jī)制進(jìn)行調(diào)整。例如,若業(yè)務(wù)場(chǎng)景允許多設(shè)備同時(shí)在線,可以修改Token綁定策略,支持多刷新Token共存。此外,開(kāi)發(fā)者們也歡迎在下方評(píng)論區(qū)提出自己的建議,共同探討和優(yōu)化。

到此這篇關(guān)于SpringBoot集成Shiro+JWT(Hutool)完整代碼示例的文章就介紹到這了,更多相關(guān)SpringBoot Shiro JWT集成內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mybatis的分頁(yè)實(shí)現(xiàn)方式

    Mybatis的分頁(yè)實(shí)現(xiàn)方式

    MyBatis 的分頁(yè)實(shí)現(xiàn)方式主要有以下幾種,每種方式適用于不同的場(chǎng)景,且在性能、靈活性和代碼侵入性上有所差異,對(duì)Mybatis的分頁(yè)實(shí)現(xiàn)方式感興趣的朋友一起看看吧
    2025-06-06
  • springboot+mybatis報(bào)錯(cuò)找不到實(shí)體類的問(wèn)題

    springboot+mybatis報(bào)錯(cuò)找不到實(shí)體類的問(wèn)題

    這篇文章主要介紹了springboot+mybatis報(bào)錯(cuò)找不到實(shí)體類的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • SpringCloudStream原理和深入使用小結(jié)

    SpringCloudStream原理和深入使用小結(jié)

    Spring?Cloud?Stream是一個(gè)用于構(gòu)建與共享消息傳遞系統(tǒng)連接的高度可擴(kuò)展的事件驅(qū)動(dòng)型微服務(wù)的框架,本文給大家介紹SpringCloudStream原理和深入使用,感興趣的朋友跟隨小編一起看看吧
    2024-06-06
  • Java?Stream流使用最多的方式示例詳解

    Java?Stream流使用最多的方式示例詳解

    在 Java 編程中,Stream 流提供了一種高效、便捷的方式來(lái)處理集合數(shù)據(jù),本文將詳細(xì)介紹 Java 中 Stream 流的用法,包括基礎(chǔ)用法、中級(jí)用法、高級(jí)用法以及一些特殊方法的使用,感興趣的朋友一起看看吧
    2024-12-12
  • Launch4j打包將jar包生成exe執(zhí)行文件實(shí)踐

    Launch4j打包將jar包生成exe執(zhí)行文件實(shí)踐

    Launch4j是一個(gè)開(kāi)源工具,用于將Java應(yīng)用程序的JAR包轉(zhuǎn)換為Windows原生可執(zhí)行文件(.exe),支持自定義圖標(biāo)、JRE版本綁定、啟動(dòng)參數(shù)配置等
    2026-01-01
  • SpringBoot 配置 okhttp3的操作

    SpringBoot 配置 okhttp3的操作

    這篇文章主要介紹了SpringBoot 配置 okhttp3的操作方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • 微服務(wù)和分布式的區(qū)別詳解

    微服務(wù)和分布式的區(qū)別詳解

    在本篇文章里小編給各位整理了關(guān)于微服務(wù)和分布式的區(qū)別以及相關(guān)知識(shí)點(diǎn)總結(jié),有興趣的朋友們學(xué)習(xí)下。
    2019-07-07
  • Dubbo服務(wù)無(wú)法注冊(cè)到ZK上問(wèn)題

    Dubbo服務(wù)無(wú)法注冊(cè)到ZK上問(wèn)題

    這篇文章主要介紹了Dubbo服務(wù)無(wú)法注冊(cè)到ZK上問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Spring Boot實(shí)現(xiàn)分片上傳、斷點(diǎn)續(xù)傳與進(jìn)度條功能

    Spring Boot實(shí)現(xiàn)分片上傳、斷點(diǎn)續(xù)傳與進(jìn)度條功能

    本文介紹了如何使用SpringBoot實(shí)現(xiàn)分片上傳、斷點(diǎn)續(xù)傳和進(jìn)度條等功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2026-02-02
  • IDEA打包應(yīng)用程序的教程圖解

    IDEA打包應(yīng)用程序的教程圖解

    這篇文章主要介紹了IDEA打包應(yīng)用程序的教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07

最新評(píng)論

威信县| 青海省| 盈江县| 中西区| 广平县| 清镇市| 莒南县| 富锦市| 邛崃市| 休宁县| 商洛市| 高要市| 军事| 淅川县| 巨鹿县| 曲阜市| 读书| 十堰市| 南川市| 库尔勒市| 临沧市| 阜平县| 东丽区| 壶关县| 甘南县| 宁都县| 得荣县| 宣恩县| 新兴县| 墨脱县| 泸西县| 淮南市| 阳高县| 吐鲁番市| 呼玛县| 临漳县| 尼勒克县| 基隆市| 娱乐| 镇巴县| 措美县|