欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot項目中集成shiro+jwt完整實例代碼

 更新時間:2025年08月07日 11:10:13   作者:Timmer丿  
本文詳細介紹如何在項目中集成Shiro和JWT,實現(xiàn)用戶登錄校驗、token攜帶及接口權限管理,涉及自定義Realm、ModularRealmAuthenticator和JwtFilter的配置,對springboot?集成shiro?jwt完整示例感興趣的朋友一起看看吧

簡介

現(xiàn)在主流的安全框架分別為Shiro和Spring Security。關于兩者之間的優(yōu)缺點不是本文的重點,有興趣的可以在網上搜搜,各種文章也都分析的很清楚。那么簡單來說,Shiro是一個強大易用的Java安全框架,提供了認證、授權、加密和會話管理等功能。(不一定要建立所謂的五張表,我們要做到控制自如的使用

目的

通過集成shiro,jwt我們要實現(xiàn):用戶登錄的校驗;登錄成功后返回成功并攜帶具有身份信息的token以便后續(xù)調用接口的時候做認證;對項目的接口進行權限的限定等。

需要的jar

本文使用的gradel作為jar包管理工具,maven也是使用相同的jar

//shiro的jar
implementation 'org.apache.shiro:shiro-spring:1.7.1'
//jwt的jar
implementation 'com.auth0:java-jwt:3.15.0'
implementation 'com.alibaba:fastjson:1.2.76'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

集成過程

1.配置shiro

@Configuration
public class ShiroConfig {
    /*
     * 解決spring aop和注解配置一起使用的bug。如果您在使用shiro注解配置的同時,引入了spring
     * aop的starter,會有一個奇怪的問題,導致shiro注解的請求,不能被映射 
     */  
    @Bean
    public static DefaultAdvisorAutoProxyCreator creator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
    /**
     * Enable Shiro AOP annotation support. --<1>
     *
     * @param securityManager Security Manager
     * @return AuthorizationAttributeSourceAdvisor
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * Use for login password matcher --<2>
     *
     * @return HashedCredentialsMatcher
     */
    @Bean("hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        // set name of hash
        matcher.setHashAlgorithmName("SHA-256");
        // Storage format is hexadecimal
        matcher.setStoredCredentialsHexEncoded(true);
        return matcher;
    }
    /**
     * Realm for login --<3>
     *
     * @param matcher password matcher
     * @return PasswordRealm
     */
    @Bean
    public LoginRealm loginRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
        LoginRealm loginRealm = new LoginRealm(LOGIN);
        loginRealm.setCredentialsMatcher(matcher);
        return loginRealm;
    }
    /**
     * JwtReal, use for token validation --<4>
     *
     * @return JwtRealm
     */
    @Bean
    public JwtRealm jwtRealm() {
        return new JwtRealm(JWT);
    }
    //  --<5>
    @Bean
    public OurModularRealmAuthenticator userModularRealmAuthenticator() {
        // rewrite ModularRealmAuthenticator
        DataAuthModularRealmAuthenticator modularRealmAuthenticator = new DataAuthModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return modularRealmAuthenticator;
    }
    // --<6>
    @Bean(name = "securityManager")
    public SecurityManager securityManager(
            @Qualifier("userModularRealmAuthenticator") OurModularRealmAuthenticatormodular,
            @Qualifier("jwtRealm") JwtRealm jwtRealm,
            @Qualifier("loginRealm") LoginRealm loginRealm
    ) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        // set realm
        manager.setAuthenticator(modular);
        // set to use own realm
        List<Realm> realms = new ArrayList<>();
        realms.add(loginRealm);
        realms.add(jwtRealm);
        manager.setRealms(realms);
        // close Shiro's built-in session
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        manager.setSubjectDAO(subjectDAO);
        return manager;
    }
    // --<7>
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, Filter> filter = new LinkedHashMap<>(1);
        filter.put("jwt", new JwtFilter());
        shiroFilterFactoryBean.setFilters(filter);
        Map<String, String> filterMap = new HashMap<>();
        filterMap.put("/login/**", "anon");
        filterMap.put("/v1/**", "jwt");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }
}
  • 開啟shiro注解支持,具體原理請參考springboot中shiro使用自定義注解屏蔽接口鑒權實現(xiàn)
  • 配置shiro登錄驗證的密碼加密方式:Shiro 提供了用于加密密碼驗證密碼服務的 CredentialsMatcher 接口,HashedCredentialsMatcher 正是 CredentialsMatcher 的一個實現(xiàn)類。
  • LoginRealm:自定義的Realm,用于處理用戶登錄驗證的Realm,在shiro中驗證及授權等信息會在Realm中配置,詳細解釋請參考shiro簡介
  • JwtRealm:自定義的Realm,用戶在登錄后訪問服務時做token的校驗,用戶權限的校驗等。
  • 配置DataAuthModularRealmAuthenticator:是在項目中存在多個Realm時,根據項目的認證策略可以選擇匹配需要的Realm。
  • SecurityManager:Shiro的核心組件,管理著認證、授權、會話管理等,在這里我把所有的自定義的Realm等資源加入到SecurityManager中
  • Shiro的過濾器:定制項目的path過濾規(guī)則,并將我們自定義的Filter加入到Shiro中的shiroFilterFactoryBean中

2.創(chuàng)建自定義Realm

2.1 LoginRealm用于處理用戶登錄

public class LoginRealm extends AuthorizingRealm {
    public LoginRealm(String name) {
        setName(name);
    }
    // 獲取user相關信息的service類
    @Autowired
    private UserLoginService userLoginService;
    // supports方法必須重寫,這是shiro處理流程中的一部分,他會通過此方法判斷realm是否匹配的正確
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof LoginDataAutoToken;
    }
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        LoginDataAutoToken token = (LoginDataAutoToken) auth;
        serviceLog.info(token.getUsername() + "password auth start...");
        User user = userLoginService.selectUserByName(token.getUsername());
        if (user == null) throw new UnknownAccountException();
        Object credentials = user.getPassword();
        // save username and role to Attribute
        ServletUtils.userNameRoleTo.accept(user.getUserName(), (int) user.getUserType());
        return new SimpleAuthenticationInfo(user, credentials, super.getName());
    }
}

2.2  JwtRealm用于在登錄之后,用戶的token是否正確以及給當前用戶授權等

public class JwtRealm extends AuthorizingRealm {
    public JwtRealm(String name) {
        setName(name);
    }
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtDataAutoToken;
    }
    // 給當前用戶授權,只有在訪問的接口上配置了shiro的權限相關的注解的時候才會進入此方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserEnum.Type userEnum = EnumValue.dataValueOf(
                UserEnum.Type.class,
                ServletUtils.userNameRoleFrom.get().getUserRole()
        );
        Set<String> roles = new HashSet<>();
        roles.add(userEnum.getDesc());
        // 授權角色如果有其他的權限則都已此類的方式授權
        authorizationInfo.setRoles(roles);
        return authorizationInfo;
    }
    // 驗證此次request攜帶的token是否正確,如果正確解析當前token,并存入上下文中
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        // verify token
        String token = (String) auth.getCredentials();
        TokenUtils.verify(token);
        TupleNameRole tupleNameRole = TokenUtils.tokenDecode(token);
        ServletUtils.userNameRoleTo.accept(tupleNameRole.getUsername(), tupleNameRole.getUserRole());
        return new SimpleAuthenticationInfo(token, token, ((JwtDataAutoToken) auth).getName());
    }
}

2.3 OurModularRealmAuthenticator用于匹配的相應的Realm

public class DataAuthModularRealmAuthenticator extends ModularRealmAuthenticator {
    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        assertRealmsConfigured();
        DataAutoToken dataAutoToken = (DataAutoToken) authenticationToken;
        Realm realm = getRealm(dataAutoToken);
        return doSingleRealmAuthentication(realm, authenticationToken);
    }
    private Realm getRealm(DataAutoToken dataAutoToken) {
        for (Realm realm : getRealms()) {
            // 根據定義的realm的name和dataAutoToken的name匹配相應的realm 
            if (realm.getName().contains(dataAutoToken.getName())) {
                return realm;
            }
        }
        return null;
    }
}

2.4 DataAutoToken及實現(xiàn)類

DataAuthModularRealmAuthenticator的doSingleRealmAuthentication(realm, authenticationToken)做檢驗的時候需要兩個參數,一個是Realm另一個是我們定義的儲存驗證信息的AuthenticationToken或者它的實現(xiàn)類。

DataAutoToken:

public interface DataAutoToken {
    String getName();
}

 LoginDataAutoToken :

public class LoginDataAutoToken extends UsernamePasswordToken implements DataAuthToken {
    public LoginDataAuthToken(final String username, final String password) {
        super(username, password);
    }
    @Override
    public String getName() {
        return LOGIN;
    }
}

 JwtDataAutoToken:

public class JwtDataAutoToken implements AuthenticationToken, DataAuthToken {
    private final String token;
    public JwtDataAuthToken(String token) {
        this.token = token;
    }
    @Override
    public Object getPrincipal() {
        return token;
    }
    @Override
    public Object getCredentials() {
        return token;
    }
    @Override
    public String getName() {
        return JWT;
    }
}

2.5 JwtFilter處理在shiro配置的自定義的Filter

此類用于處理不在登錄下必須攜帶發(fā)行的Token訪問接口,如果Token存在,則使用shiro subject做token的和訪問權限的校驗。

public class JwtFilter extends BasicHttpAuthenticationFilter {
    private final BiConsumer<ServletResponse, ErrorMessage> writeResponse = (response, message) ->
            Utils.renderString.accept(
                    (HttpServletResponse) response,
                    JSON.toJSONString(ResponseResult.fail(message), SerializerFeature.WriteMapNullValue)
            );
    /**
     * @param request     ServletRequest
     * @param response    ServletResponse
     * @param mappedValue mappedValue
     * @return 是否成功
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        //input request to request log file
        requestLog.info(
                "path:{}, method:{}",
                httpServletRequest.getServletPath(),
                httpServletRequest.getMethod()
        );
        String token = httpServletRequest.getHeader(Constant.TOKEN);
        if (token != null) {
            return executeLogin(request, response);
        } else {
            writeResponse.accept(response, ErrorMessage.TOKEN_NOT_EXIST);
            return false;
        }
    }
    /**
     * execute login
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader(Constant.TOKEN);
        try {
            JwtDataAuthToken jwtToken = new JwtDataAuthToken(token);
            // validate user permission
            getSubject(request, response).login(jwtToken);
            return true;
        } catch (AuthenticationException e) {
            Throwable throwable = e.getCause();
            if (throwable instanceof TokenExpiredException) {
                writeResponse.accept(response, ErrorMessage.TOKEN_HAS_EXPIRED);
            } else {
                writeResponse.accept(response, ErrorMessage.TOKEN_INVALID);
            }
        }
        return false;
    }
    /**
     * support across domains
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

2.6 controller層登錄和其他接口

@RestController
public class AuthController {
    @Autowired
    private UserService userService;
    @PostMapping("/login")
    public ResponseResult<String> login(@RequestBody UserReqDto userReqDto) {
        userService.login(userLoginReqDto.getUsername(), userReqDto.getPassword());
        return ResponseResult.success();
    }
    // shiro角色注解,admin才可以訪問此接口
    @RequiresRoles("admin")
    @PostMapping("/v1/user")
    public ResponseResult<String> addUser(@RequestBody UserAddReqDto userAddReqDto) {
        userService.add(userAddReqDto);
        return ResponseResult.success();
    }
    @PostMapping("/v1/token/verify")
    public ResponseResult<String> verify() {
        return ResponseResult.success(false);
    }
    @PostMapping("/v1/token/refresh")
    public ResponseResult<String> refresh() {
        return ResponseResult.success();
    }
}

2.7 service層

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void login(String username, String password) {
        // Use shiro to verify the username and password
        Subject subject = SecurityUtils.getSubject();
        LoginDataAutoToken token = new LoginDataAutoToken(username, password);
        subject.login(token);
    }
    @Transactional
    @Override
    public void add(UserAddReqDto dto) {
        User user = getUserByName.apply(dto.getUsername());
        if (user != null) {
            throw new DataAuthException(ErrorMessage.USER_ALREADY_EXISTS);
        } else {
            User newUser = new User();
            // 設置user的信息
            post(newUser); // insert user to database
        }
}

2.8 jwt工具類

public final class TokenUtils {
    private TokenUtils() {
    }
    /**
     * @param username username
     * @param role     user role
     * @return The encrypted token
     */
    public static String createToken(String username, int role) {
        Date date = new Date(System.currentTimeMillis() + Constant.TOKEN_EXPIRE_TIME);
        Algorithm algorithm = Algorithm.HMAC256(username);
        return JWT.create()
                .withClaim(Constant.USER_NAME, username)
                .withClaim(Constant.USER_ROLE, role)
                .withExpiresAt(date)
                .sign(algorithm);
    }
    /**
     * @param username username
     * @param role     user role
     * @return The encrypted token
     */
    public static String refreshToken(String username, int role) {
        return createToken(username, role);
    }
    /**
     * refresh token and add to header
     */
    public static void refreshToken() {
        TupleNameRole tupleNameRole = ServletUtils.userNameRoleFrom.get();
        ServletUtils.addHeader.accept(
                Constant.TOKEN,
                createToken(tupleNameRole.getUsername(), tupleNameRole.getUserRole())
        );
    }
    /**
     * verify token
     *
     * @param token jwtToken
     */
    public static void verify(String token) {
        try {
            TupleNameRole tupleNameRole = tokenDecode(token);
            Algorithm algorithm = Algorithm.HMAC256(tupleNameRole.getUsername());
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim(Constant.USER_NAME, tupleNameRole.getUsername())
                    .withClaim(Constant.USER_ROLE, tupleNameRole.getUserRole())
                    .build();
            verifier.verify(token);
        } catch (JWTVerificationException e) {
            serviceLog.error("token verify fail.", e);
            throw e;
        }
    }
    /**
     * @param token token
     * @return user name and role
     */
    public static TupleNameRole tokenDecode(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return new TupleNameRole(
                    jwt.getClaim(Constant.USER_NAME).asString(),
                    jwt.getClaim(Constant.USER_ROLE).asInt()
            );
        } catch (JWTDecodeException e) {
            serviceLog.error("Token decode happen exception.", e);
            throw e;
        }
    }
}

2.9 其他的一些工具類

ServletUtils:與spring context中有關的一些方法
public final class ServletUtils {
    private ServletUtils() {
    }
    private static final int SCOPE = RequestAttributes.SCOPE_REQUEST;
    private static final Supplier<ServletRequestAttributes> servletRequestAttributes = () ->
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    private static final Supplier<HttpServletRequest> request = () -> servletRequestAttributes.get().getRequest();
    private static final Supplier<HttpServletResponse> response = () -> servletRequestAttributes.get().getResponse();
    private static final Consumer<String> saveUsernameToAttribute = (name) ->
            servletRequestAttributes.get().setAttribute(Constant.USER_NAME, name, SCOPE);
    private static final Supplier<String> usernameFromAttribute = () ->
            (String) servletRequestAttributes.get().getAttribute(Constant.USER_NAME, SCOPE);
    private static final Consumer<Integer> saveUserRoleToAttribute = (role) ->
            servletRequestAttributes.get().setAttribute(Constant.USER_ROLE, role, SCOPE);
    private static final Supplier<Integer> userRoleFromAttribute = () ->
            (Integer) servletRequestAttributes.get().getAttribute(Constant.USER_ROLE, SCOPE);
    /**
     * get token form current request
     */
    public static Supplier<String> tokenFromRequest = () -> request.get().getHeader(Constant.TOKEN);
    /**
     * save current user name and role to attribute
     */
    public static BiConsumer<String, Integer> userNameRoleTo = (name, role) -> {
        saveUsernameToAttribute.accept(name);
        saveUserRoleToAttribute.accept(role);
    };
    /**
     * get user name and role from attribute
     */
    public static Supplier<TupleNameRole> userNameRoleFrom = () ->
            new TupleNameRole(usernameFromAttribute.get(), userRoleFromAttribute.get());
    /**
     * add message to response header
     */
    public static BiConsumer<String, String> addHeader = (key, value) -> response.get().addHeader(key, value);
}

Utils:提供與shiro相同的密碼加密方式、獲取uuid、shiro的Filter層出錯不能使用全局異常處理時的返回信息定制等。

public final class Utils {
    private Utils() {
    }
    /**
     * use sha256 encrypt
     */
    public static Function<String, String> encryptPassword = (password) -> new Sha256Hash(password).toString();
    /**
     * get uuid
     */
    public static Supplier<String> uuid = () -> UUID.randomUUID().toString().replace("-", "");
    /**
     * writer message to response
     */
    public static BiConsumer<HttpServletResponse, String> renderString = (response, body) -> {
        response.setStatus(HttpStatus.OK.value());
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=UTF-8");
        try (PrintWriter writer = response.getWriter()) {
            writer.print(body);
        } catch (IOException e) {
            serviceLog.error("response error.", e);
        }
    };
}

2.10 返回結果定義

@Data
public class ResponseResult<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    private final String code;
    @JSONField(ordinal = 1)
    private final String msg;
    @JSONField(ordinal = 2)
    private T data;
    private ResponseResult(String code, String msg) {
        this.code = code;
        this.msg = msg;
        log();
    }
    private static <T> ResponseResult<T> create(String code, String msg) {
        return new ResponseResult<>(code, msg);
    }
    /**
     * No data returned successfully
     *
     * @return ResponseResult<String>
     */
    public static <T> ResponseResult<T> success() {
        return success(true);
    }
    /**
     * No data returned successfully
     *
     * @param refreshToken Whether to refresh token
     * @return ResponseResult<String>
     */
    public static <T> ResponseResult<T> success(boolean refreshToken) {
        if (refreshToken) TokenUtils.refreshToken();
        return create(ErrorMessage.SUCCESS.code(), ErrorMessage.SUCCESS.msg());
    }
    public static <T> ResponseResult<T> success(T data) {
        return success(data, true);
    }
    /**
     * Data returned successfully
     *
     * @param data         data
     * @param <T>          T
     * @param refreshToken Whether to refresh token
     * @return ResponseResult<T>
     */
    public static <T> ResponseResult<T> success(T data, boolean refreshToken) {
        ResponseResult<T> responseResult = success(refreshToken);
        responseResult.setData(data);
        return responseResult;
    }
    /**
     * @param e DCException
     * @return ResponseResult<String>
     */
    public static ResponseResult<String> fail(DataAuthException e) {
        return create(e.getCode(), e.getMsg());
    }
    /**
     * @param errorMessage ErrorMessage
     * @return ResponseResult<String>
     */
    public static ResponseResult<String> fail(ErrorMessage errorMessage) {
        return create(errorMessage.code(), errorMessage.msg());
    }
    /**
     * @param errorMessage DCException
     * @return ResponseResult<String>
     */
    public static ResponseResult<String> fail(ErrorMessage errorMessage, Object[] detailMessage) {
        return create(errorMessage.code(), errorMessage.msg() + Arrays.toString(detailMessage));
    }
    // Output the information returned
    private void log() {
        requestLog.info("code:{}, msg:{}", this.getCode(), this.getMsg());
    }
}

到此這篇關于springboot項目中集成shiro+jwt詳解+完整實例的文章就介紹到這了,更多相關springboot 集成shiro jwt內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • @Schedule?如何解決定時任務推遲執(zhí)行

    @Schedule?如何解決定時任務推遲執(zhí)行

    這篇文章主要介紹了@Schedule?如何解決定時任務推遲執(zhí)行問題。具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Java多線程中的CountDownLatch解析

    Java多線程中的CountDownLatch解析

    這篇文章主要介紹了Java多線程中的CountDownLatch解析,CountDownLatch是一個阻塞部分線程直到其他線程執(zhí)行完成后喚醒的同步計數器,核心是其內部類Sync繼承于AQS,同時也是利用的AQS的同步原理,也稱之為閉鎖,需要的朋友可以參考下
    2023-11-11
  • 將對象轉化為字符串的java實例

    將對象轉化為字符串的java實例

    這篇文章主要介紹了將對象轉化為字符串的java實例,有需要的朋友可以參考一下
    2013-12-12
  • Java中的鎖ReentrantLock詳解

    Java中的鎖ReentrantLock詳解

    這篇文章主要介紹了Java中的鎖ReentrantLock詳解,ReentantLock是java中重入鎖的實現(xiàn),一次只能有一個線程來持有鎖,包含三個內部類,Sync、NonFairSync、FairSync,需要的朋友可以參考下
    2023-09-09
  • 基于Spark實現(xiàn)隨機森林代碼

    基于Spark實現(xiàn)隨機森林代碼

    這篇文章主要為大家詳細介紹了基于Spark實現(xiàn)隨機森林代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • 八種Java中的基本數據類型詳解

    八種Java中的基本數據類型詳解

    在Java編程中,基本數據類型是必不可少的一部分,對于初學者而言,理解這些基本數據類型是非常重要的,下面我們就來學習一下Java中的八種基本數據類型,以及它們的使用方法吧
    2023-08-08
  • java實現(xiàn)猜拳游戲

    java實現(xiàn)猜拳游戲

    這篇文章主要為大家詳細介紹了java實現(xiàn)猜拳游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • 流式圖表拒絕增刪改查之kafka核心消費邏輯上篇

    流式圖表拒絕增刪改查之kafka核心消費邏輯上篇

    這篇文章主要為大家介紹了流式圖表拒絕增刪改查之kafka核心消費邏輯詳解的上篇,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • Java簡單實現(xiàn)UDP和TCP的示例

    Java簡單實現(xiàn)UDP和TCP的示例

    下面小編就為大家?guī)硪黄狫ava簡單實現(xiàn)UDP和TCP的示例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • 基于Java代碼實現(xiàn)數字在數組中出現(xiàn)次數超過一半

    基于Java代碼實現(xiàn)數字在數組中出現(xiàn)次數超過一半

    這篇文章主要介紹了基于Java代碼實現(xiàn)數字在數組中出現(xiàn)次數超過一半的相關資料,需要的朋友可以參考下
    2016-02-02

最新評論