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

SpringSecurity多表多端賬戶登錄的實(shí)現(xiàn)

 更新時(shí)間:2024年05月14日 09:48:43   作者:石添的編程哲學(xué)  
本文主要介紹了SpringSecurity多表多端賬戶登錄的實(shí)現(xiàn)

需求:針對公司員工,普通用戶等各類型用戶,將其分別存儲(chǔ)在不同的用戶表中,基于SpeingSecurity實(shí)現(xiàn)用戶認(rèn)證,也就是登錄功能

流程

  • 首先做數(shù)據(jù)庫設(shè)計(jì)
  • 基于SpringBoot創(chuàng)建一個(gè)項(xiàng)目
  • 項(xiàng)目中做相關(guān)的實(shí)現(xiàn)
  • 通過apifox接口測試工具進(jìn)行測試
  • 分別測試不同用戶的登錄方法,是否調(diào)用了對應(yīng)的登錄邏輯【登錄也稱為認(rèn)證】

注意:權(quán)限這塊并沒有涉及,僅僅是用戶數(shù)據(jù)這塊

數(shù)據(jù)表設(shè)計(jì)

本文先不涉及權(quán)限,表設(shè)計(jì)就是兩張用戶表

員工表

CREATE TABLE `ums_sys_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用戶ID',
  `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用戶賬號',
  `nickname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用戶昵稱',
  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '用戶郵箱',
  `mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '手機(jī)號碼',
  `sex` int DEFAULT '0' COMMENT '用戶性別(0男 1女 2未知)',
  `avatar` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '頭像地址',
  `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '密碼',
  `status` int DEFAULT '0' COMMENT '帳號狀態(tài)(0正常 1停用)',
  `creator` bigint DEFAULT '1' COMMENT '創(chuàng)建者',
  `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `updater` bigint DEFAULT '1' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新時(shí)間',
  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '備注',
  `deleted` tinyint DEFAULT '0',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='后臺(tái)用戶表';

客戶表

CREATE TABLE `ums_site_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用戶ID',
  `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用戶賬號',
  `nickname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用戶昵稱',
  `openid` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '微信openid',
  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '用戶郵箱',
  `mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '手機(jī)號碼',
  `sex` int DEFAULT '0' COMMENT '用戶性別(0男 1女 2未知)',
  `avatar` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '頭像地址',
  `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '密碼',
  `status` int DEFAULT '0' COMMENT '帳號狀態(tài)(0正常 1停用)',
  `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `updater` bigint DEFAULT '1' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新時(shí)間',
  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '備注',
  `deleted` tinyint DEFAULT '0',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='外部用戶表';

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

登錄功能,使用非常簡單的三層架構(gòu),技術(shù)選型有:

  • SpringBoot 3.1.X
  • SpringSecurity 6.1.X
  • Mybatis Plus
  • lombok【簡化實(shí)體類】,可以通過注解生成getter、setter方法,構(gòu)造方法,toString方法等
  • maven

pom文件

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

application.yml文件配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/spring-security?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: stt123456

創(chuàng)建三層架構(gòu)

Controller

@RestController
@RequestMapping("/auth")
public class AuthController {

    private final ISysUserService sysUserService;
    private final ISiteUserService siteUserService;

    public AuthController(ISysUserService sysUserService, ISiteUserService siteUserService) {
        this.sysUserService = sysUserService;
        this.siteUserService = siteUserService;
    }

    /**
     * 后端管理系統(tǒng)登錄
     * 返回值:token
     */
    @PostMapping("sys_login")
    public String sysLogin(@RequestBody LoginParam loginParam) {

        return "后臺(tái)用戶登錄======》" +sysUserService.sysLogin(loginParam);
    }

    @PostMapping("site_login")
    public String siteLogin(@RequestBody LoginParam loginParam) {

        return "APP用戶登錄======》" + siteUserService.siteLogin(loginParam);
    }

}

SysUserServiceImpl

@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    @Autowired
    @Qualifier("sysUserAuthenticationManager")
    private AuthenticationManager authenticationManager;

    /**
     * 登錄是SpringSecurity實(shí)現(xiàn)的,我們就是去告訴SpringSecurity現(xiàn)在要登錄
     * SpringSecirity登錄是通過 AuthticationManager 實(shí)現(xiàn)的
     * 將AuthticationManager引入到service中,調(diào)用他的認(rèn)證方法就可以了
     * @param loginParam
     * @return
     */
    @Override
    public String sysLogin(LoginParam loginParam) {
        // 通過authenticationManager 的認(rèn)證方法實(shí)現(xiàn)登錄,該方法需要傳入 Authentication 對象 就是一個(gè)認(rèn)證對象
        // Authenticationl里邊存儲(chǔ)的就是用戶的認(rèn)證信息,權(quán)限,用戶名,密碼的等信息,其實(shí)就是loadUserByUsername方法返回的UserDetails
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginParam.getUsername(), loginParam.getPassword());

        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        // 獲取用戶信息
        SysUser sysUser = (SysUser) authenticate.getPrincipal();
        log.info("sysUser==========》{}",sysUser);
        // 返回的是token
        return sysUser.getUsername();
    }
}

SiteUserServiceImpl

@Service
@Slf4j
public class SiteUserServiceImpl extends ServiceImpl<SiteUserMapper, SiteUser> implements ISiteUserService {

    /**
     * 將AuthenticationManager注入
     */
    @Autowired
    @Qualifier("siteUserAuthenticationManager")
    private AuthenticationManager authenticationManager;

    @Override
    public String siteLogin(LoginParam loginParam) {
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginParam.getMobile(), loginParam.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        // 強(qiáng)轉(zhuǎn)為用戶類型
        SiteUser siteUser = (SiteUser) authenticate.getPrincipal();
        log.info("siteUser===========>{}",siteUser);
        return siteUser.getUsername();
    }
}

實(shí)現(xiàn)login功能

項(xiàng)目中引入SpringSecurity,SpringSecurity在實(shí)現(xiàn)用戶登錄【認(rèn)證】時(shí)需要使用到兩個(gè)接口

  • UserDetailsService:是一個(gè)接口 ,提供了一個(gè)方法loadUserByUsername();
  • UserDetails:是一個(gè)接口,用來存儲(chǔ)用戶權(quán)限,狀態(tài)【是否禁用,超時(shí)等】

通過UserDetailsService查詢用戶,將用戶信息放到UserDetails中,剩下的就交給SpringSecurity的AuthenticationManager做判斷,判斷用戶是否允許登錄

分兩步走

創(chuàng)建UserDetailsService接口實(shí)現(xiàn)類

查詢用戶,分別為客戶和用后臺(tái)系統(tǒng)用戶創(chuàng)建對應(yīng)的查詢用戶的實(shí)現(xiàn)類

// 系統(tǒng)用戶的DetailsService
@Service
public class SysUserDetailsService implements UserDetailsService {

    private final SysUserMapper sysUserMapper;

    public SysUserDetailsService(SysUserMapper sysUserMapper) {
        this.sysUserMapper = sysUserMapper;
    }

    /**
     * 此方法從數(shù)據(jù)庫中查詢用戶
     * 返回一個(gè) UserDetails
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("后臺(tái)系統(tǒng)用戶登錄=============》");
        // 根據(jù)用戶名查詢用戶
        SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, username));
        // 有權(quán)限的話,需要查詢該用戶對應(yīng)的權(quán)限
        if(sysUser == null) {
            throw new UsernameNotFoundException("用戶或密碼不正確");
        }
        return sysUser;
    }
}
// APP用戶的DetailsService
@Slf4j
public class SiteUserDetailsService implements UserDetailsService {

    private final SiteUserMapper siteUserMapper;

    public SiteUserDetailsService(SiteUserMapper siteUserMapper) {
        this.siteUserMapper = siteUserMapper;
    }

    @Override
    public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
        log.info("APP用戶登錄===================》");
        SiteUser siteUser = siteUserMapper.selectOne(new LambdaQueryWrapper<SiteUser>().eq(SiteUser::getMobile, mobile));
        if(siteUser == null) {
            throw new UsernameNotFoundException("用戶名或密碼錯(cuò)誤!");
        }
        return siteUser;
    }
}

創(chuàng)建UserDetails接口實(shí)現(xiàn)類

存儲(chǔ)用戶信息,同樣的創(chuàng)建兩個(gè)實(shí)現(xiàn)類,存儲(chǔ)不同的用戶信息,再實(shí)體類上直接修改

// 后臺(tái)管理系統(tǒng)用戶類
@TableName("ums_sys_user")
@Data
public class SysUser implements Serializable, UserDetails {

    private Long id;
    private String username;
    private String nickname;
    private String email;
    private String mobile;
    private Integer sex;
    private String avatar;
    @JsonIgnore
    private String password;
    private Integer status;
    private Long creator;
    private Long updater;
    private String remark;
    @TableLogic
    private Integer deleted;

    private LocalDateTime createTime;
    private LocalDateTime updateTime;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
// APP用戶實(shí)體類
@Data
@TableName("ums_site_user")
public class SiteUser implements Serializable, UserDetails {

    private Long id;
    private String username;
    private String nickname;
    private String openid;
    private String email;
    private String mobile;
    private Integer sex;
    private String avatar;
    @JsonIgnore
    private String password;
    private Integer status;
    private Long updater;
    private String remark;
    @TableLogic
    private Integer deleted;

    private LocalDateTime createTime;
    private LocalDateTime updateTime;

    /**
     * 權(quán)限。現(xiàn)在并沒有查詢權(quán)限
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

關(guān)聯(lián)

將SpringSecurity的AuthenticationManager 【認(rèn)證管理器,管登錄的組件】,與我們寫的登錄邏輯關(guān)聯(lián)起來【loadUserByUsername方法】,實(shí)現(xiàn)方式就是在SpringSecurity的配置類中實(shí)現(xiàn)

/**
 * 現(xiàn)在使用的是SpringSecurity 6.1.5版本,開啟SpringSecurity的自定義配置,
 * 需要使用 @EnableWebSecurity注解,而不再是繼承Adpater
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private SysUserDetailsService sysUserDetailsService;

    @Autowired
    private SiteUserDetailsService siteUserDetailsService;

    // 配置SpringSecurity的過濾器鏈
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 設(shè)置登錄接口放行
        http.authorizeHttpRequests(auth -> auth.requestMatchers("/auth/sys_login","/auth/site_login").permitAll().anyRequest().authenticated());
        // 關(guān)閉csrf
        http.csrf(csrf -> csrf.disable());
        return http.build();
    }


    // 配置AuthenticationManager,配置兩個(gè)。一個(gè)管理后臺(tái)用戶
    @Primary
    @Bean("sysUserAuthenticationManager")
    public AuthenticationManager sysUserAuthenticationManager(PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        // 關(guān)聯(lián)UserDetailsService
        authenticationProvider.setUserDetailsService(sysUserDetailsService);
        // 關(guān)聯(lián)密碼管理器
        authenticationProvider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(authenticationProvider);
    }


    // 配置AuthenticationManager,管理APP用戶
    @Bean("siteUserAuthenticationManager")
    public AuthenticationManager siteUserAuthenticationManager(PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        // 關(guān)聯(lián)UserDetailsService
        authenticationProvider.setUserDetailsService(siteUserDetailsService);
        // 關(guān)聯(lián)密碼管理器
        authenticationProvider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(authenticationProvider);
    }

    /**
     * 密碼管理器,會(huì)將明文密碼轉(zhuǎn)換成密文,加密,而且不能解碼
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

密碼

需要對數(shù)據(jù)庫中存儲(chǔ)的密碼進(jìn)行編碼,因?yàn)镾pringSecurity進(jìn)行密碼匹配時(shí),會(huì)對用戶輸入的密碼先編碼,再驗(yàn)證,先通過PasswordEncoder生成加密后的密碼

@SpringBootTest
public class MyTestApplication {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Test
    public void test() {
		// 密碼加密
        String encode = passwordEncoder.encode("123456");
        System.out.println(encode);

    }
}

SpringSecurity配置

創(chuàng)建兩個(gè)AuthenticationManager,一定要設(shè)置一個(gè)主AuthenticationManager否則將會(huì)報(bào)錯(cuò),即在任意一個(gè)Bean上添加@Primary注解標(biāo)記

@Primary
@Bean("sysAuthenticationManager")
public AuthenticationManager sysAuthenticationManager(PasswordEncoder passwordEncoder) {
    DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
    authenticationProvider.setUserDetailsService(sysUserDetailsService);
    authenticationProvider.setPasswordEncoder(passwordEncoder);
    ProviderManager providerManager = new ProviderManager(authenticationProvider);
    providerManager.setEraseCredentialsAfterAuthentication(false);
    return providerManager;
}

/**
 * 外部用戶驗(yàn)證管理器
 * @param passwordEncoder
 * @return
 */
@Bean("siteAuthenticationManager")
public AuthenticationManager siteAuthenticationManager(PasswordEncoder passwordEncoder) {
    DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
    authenticationProvider.setUserDetailsService(siteUserDetailsService);
    authenticationProvider.setPasswordEncoder(passwordEncoder);
    ProviderManager providerManager = new ProviderManager(authenticationProvider);
    providerManager.setEraseCredentialsAfterAuthentication(false);
    return providerManager;
}

數(shù)據(jù)庫配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springsecurity?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: stt123456

AuthenticationManager

AuthenticationManager用于定義SpringSecurity如何進(jìn)行身份認(rèn)證,之后將認(rèn)證信息封裝在Authentication對象上,設(shè)置到SecurityContextHolder上,AuthenticationManager常用的實(shí)現(xiàn)是ProviderManager,你也可以對其做自定義實(shí)現(xiàn)。

到此這篇關(guān)于SpringSecurity多表多端賬戶登錄的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringSecurity多表多端賬戶登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 實(shí)例代碼講解JAVA多線程

    實(shí)例代碼講解JAVA多線程

    這篇文章主要介紹講解JAVA多線程的有關(guān)知識(shí),文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • 詳解JDK 5 Annotation 注解之@Target的用法介紹

    詳解JDK 5 Annotation 注解之@Target的用法介紹

    這篇文章主要介紹了詳解JDK 5 Annotation 注解之@Target的用法介紹,需要的朋友可以參考下
    2016-02-02
  • Java?垃圾回收超詳細(xì)講解記憶集和卡表

    Java?垃圾回收超詳細(xì)講解記憶集和卡表

    卡表就是記憶集的一種具體實(shí)現(xiàn),它定義了記憶集的記錄精度、與堆內(nèi)存的映射關(guān)系等。?關(guān)于卡表與記憶集的關(guān)系,不妨按照J(rèn)ava語言中HashMap與Map的關(guān)系來類比理解。記憶集是一種用于記錄從非收集區(qū)域指向收集區(qū)域的指針集合的抽象數(shù)據(jù)結(jié)構(gòu)
    2022-04-04
  • SpringBoot中的自定義FailureAnalyzer詳解

    SpringBoot中的自定義FailureAnalyzer詳解

    這篇文章主要介紹了SpringBoot中的自定義FailureAnalyzer詳解,FailureAnalyzer是一種很好的方式在啟動(dòng)時(shí)攔截異常并將其轉(zhuǎn)換為易讀的消息,并將其包含在FailureAnalysis中, Spring Boot為應(yīng)用程序上下文相關(guān)異常、JSR-303驗(yàn)證等提供了此類分析器,需要的朋友可以參考下
    2023-12-12
  • 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
  • request如何獲取完整url(包括域名、端口、參數(shù))

    request如何獲取完整url(包括域名、端口、參數(shù))

    這篇文章主要介紹了request如何獲取完整url(包括域名、端口、參數(shù))問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • httpclient模擬post請求json封裝表單數(shù)據(jù)的實(shí)現(xiàn)方法

    httpclient模擬post請求json封裝表單數(shù)據(jù)的實(shí)現(xiàn)方法

    下面小編就為大家?guī)硪黄猦ttpclient模擬post請求json封裝表單數(shù)據(jù)的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-12-12
  • javaWEB實(shí)現(xiàn)相冊管理的簡單功能

    javaWEB實(shí)現(xiàn)相冊管理的簡單功能

    這篇文章主要介紹了javaWEB實(shí)現(xiàn)相冊管理的簡單功能,包括圖片的上傳、統(tǒng)一瀏覽、單個(gè)下載、單個(gè)刪除,還有一個(gè)功能只能刪除自己上傳的文件,感興趣的小伙伴們可以參考一下
    2015-11-11
  • 解析Neatbeans(常見錯(cuò)誤) build-impl.xml:305: Compile failed

    解析Neatbeans(常見錯(cuò)誤) build-impl.xml:305: Compile failed

    本篇文章是對Neatbeans(常見錯(cuò)誤) build-impl.xml:305: Compile failed的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-07-07
  • Java并發(fā)編程之線程安全性

    Java并發(fā)編程之線程安全性

    這篇文章主要介紹了Java并發(fā)編程之線程安全性,文章基于Java的相關(guān)內(nèi)容詳細(xì)的展開詳細(xì)介紹,需要的小伙伴可以參考一下
    2022-04-04

最新評論