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

Spring Security基于數(shù)據(jù)庫的ABAC屬性權(quán)限模型實戰(zhàn)開發(fā)教程

 更新時間:2025年04月08日 15:18:01   作者:Micro麥可樂  
這篇文章主要介紹了Spring Security基于數(shù)據(jù)庫的ABAC屬性權(quán)限模型實戰(zhàn)開發(fā)教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

1. 前言

今天博主又抽空來給小伙伴更新 Spring Security 教程啦,上個章節(jié)中我們講解了如何通過數(shù)據(jù)庫實現(xiàn)基于數(shù)據(jù)庫的動態(tài)用戶認(rèn)證,大家可能發(fā)現(xiàn)了,項目中是基于RBAC角色模型的權(quán)限控制,雖然能滿足大多數(shù)場景,但在面對復(fù)雜、細(xì)粒度的權(quán)限需求時可能會力不從心?;趯傩缘脑L問控制(ABAC)模型則通過評估用戶、資源、環(huán)境等多種屬性,實現(xiàn)更加靈活的權(quán)限控制。

例如,某個菜單的訪問可能不僅取決于用戶角色,還取決于用戶的部門、時間或其他屬性。因此,需要在權(quán)限驗證時動態(tài)獲取這些屬性,并進(jìn)行評估。那么本章節(jié)我們就來講解基于數(shù)據(jù)庫的ABAC屬性權(quán)限模型實戰(zhàn)開發(fā)

2. 權(quán)限決策依據(jù)

既然談到了 RBACABAC 兩個模型,就大家介紹下兩者間的區(qū)別:

RBAC

  • 核心思想:以角色作為權(quán)限管理的核心,每個用戶被賦予一個或多個角色,而角色與權(quán)限之間存在固定的映射關(guān)系。
  • 決策依據(jù):當(dāng)用戶請求訪問資源時,系統(tǒng)根據(jù)用戶所屬角色所擁有的權(quán)限進(jìn)行校驗。
  • 粒度:粒度相對較粗,因為權(quán)限是綁定在角色上的,無法針對單個請求條件進(jìn)行動態(tài)決策。

ABAC

  • 核心思想:以屬性(Attribute)為基礎(chǔ),利用用戶屬性、資源屬性、環(huán)境屬性等多個維度的條件進(jìn)行權(quán)限判斷。
  • 決策依據(jù):權(quán)限決策是基于各種屬性之間的邏輯表達(dá)式和策略規(guī)則來動態(tài)確定是否允許訪問。
  • 粒度:支持非常細(xì)粒度的控制,可以針對具體屬性制定規(guī)則,實現(xiàn)精準(zhǔn)的權(quán)限控制。

綜合對比

對比維度RBACABAC
決策依據(jù)用戶所屬角色與預(yù)定義權(quán)限映射關(guān)系用戶、資源及環(huán)境屬性和策略規(guī)則
靈活性固定、靜態(tài)權(quán)限模型動態(tài)、可擴(kuò)展的權(quán)限決策模型
管理難度管理較簡單,但角色關(guān)系復(fù)雜時易混亂規(guī)則管理復(fù)雜,但擴(kuò)展靈活
粒度較粗,難以細(xì)化至個性化條件非常細(xì)粒度,可實現(xiàn)精確權(quán)限控制
適用場景企業(yè)內(nèi)部、權(quán)限固定的系統(tǒng)復(fù)雜、多變、動態(tài)決策的業(yè)務(wù)系統(tǒng)

3. 數(shù)據(jù)庫表結(jié)構(gòu)說明

上一個章節(jié)RBAC角色模型我們使用了五張表,sys_user 、 sys_role、 sys_user_rolesys_menu 、 sys_role_menu ,需要數(shù)據(jù)表結(jié)構(gòu)的小伙伴可以查閱上一章內(nèi)容!本章節(jié)不再贅述

現(xiàn)在我們在傳統(tǒng)RBAC模型基礎(chǔ)上,加入ABAC屬性權(quán)限模型 ABAC(Attribute-Based Access Control)引入了更細(xì)粒度的動態(tài)控制維度:

為什么要增加ABAC屬性權(quán)限模型?
需求1:請求某個方法除了要驗證用戶角色或菜單資源,我還要判斷用戶屬性部門=IT,國家是ZH
需求2:請求某個方法除了要驗證用戶角色或菜單資源,我還要限制訪問時間段

而如果使用ABAC屬性權(quán)限模型動態(tài)策略就可以很輕松解決這樣的問題!

基于上述的需求,我們來擴(kuò)展我們的數(shù)據(jù)庫表,sys_user_attr 為用戶相關(guān)屬性,sys_policy 為策略表(ABAC規(guī)則存儲)

-- 擴(kuò)展用戶屬性表(新增)
CREATE TABLE sys_user_attr (
    user_id BIGINT NOT NULL,
    attr_key VARCHAR(50) NOT NULL,
    attr_value VARCHAR(100) NOT NULL,
    PRIMARY KEY (user_id, attr_key)
);
-- 示例數(shù)據(jù)
INSERT INTO sys_user_attr VALUES 
(1, 'department', 'IT'),
(2, 'department', 'HR'),
(3, 'security_level', '3');
(1, 'country', 'zh'),

權(quán)限策略表設(shè)計:
存儲 ABAC 策略,每條策略包含一個條件表達(dá)式(基于 SpEL 編寫)

CREATE TABLE sys_policy (
    policy_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    policy_name VARCHAR(50) NOT NULL,
    target_resource VARCHAR(64) NOT NULL,
    condition_expression VARCHAR(255) NOT NULL
);
INSERT INTO sys_policy VALUES
(1, 'IT部門訪問策略', 'admin:menu', "#user.attrs['department'] == 'IT'"),
(2, '高安全級別策略', 'developers:menu', "T(Integer).parseInt(#user.attrs['security_level']) >= 3");
(3, 'IT部門訪問策略', 'admin:menu', "#user.attrs['country'] == 'zh'");

整體數(shù)據(jù)庫結(jié)構(gòu)如下:

4. 實戰(zhàn)開始

接下來在之前的 Maven 項目中,我們復(fù)用上個章節(jié)的子模塊并命名 abac-spring-security

由于涉及數(shù)據(jù)庫操作以及整合mybatis-plus,上一章節(jié)博主已經(jīng)進(jìn)行了配置的詳解這里就簡單貼出代碼供大家參考:

<!--Lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <!--使用 HikariCP 連接池-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- mysql驅(qū)動 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.9</version>
        </dependency>

配置yml文件,運行項目確保項目能正常鏈接數(shù)據(jù)庫且啟動成功

server:
  port: 8084
spring:
  application:
    name: db-spring-security #最新Spring Security實戰(zhàn)教程(六)基于數(shù)據(jù)庫的ABAC屬性權(quán)限模型實戰(zhàn)開發(fā)
  datasource:
    url: jdbc:mysql://localhost:3306/slave_db?useSSL=false&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 5
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true # 開啟駝峰轉(zhuǎn)換
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL
    cache-enabled: true # 開啟二級緩存
  global-config:
    db-config:
      logic-delete-field: delFlag # 邏輯刪除字段
      logic-delete-value: 1 # 刪除值
      logic-not-delete-value: 0 # 未刪除值

5. MyBatis-Plus實體定義

接下來我們開始編寫業(yè)務(wù)代碼

? 用戶實體(實現(xiàn)UserDetails)

@Data
@TableName("sys_user")
public class SysUser implements UserDetails {
    @TableId(type = IdType.AUTO)
    private Long userId;
    @TableField("login_name")
    private String username; // Spring Security認(rèn)證使用的字段
    private String password;
    private String status; // 狀態(tài)(0正常 1鎖定)
    private String delFlag; // 刪除標(biāo)志(0代表存在 1代表刪除)
    @TableField(exist = false)
    private List<SysRole> roles;
    @TableField(exist = false)
    private Map<String, String> attrs; // 用戶的屬性集合,用于 ABAC 動態(tài)權(quán)限評估
    // 實現(xiàn)UserDetails接口
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // 組裝 GrantedAuthority 集合,將角色和菜單權(quán)限都加入
        Set<GrantedAuthority> authorities = new HashSet<>();
        authorities.addAll(roles.stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRoleKey()))
                .collect(Collectors.toList()));
        authorities.addAll(roles.stream()
                .flatMap(role -> role.getMenus().stream())
                .map(menu -> new SimpleGrantedAuthority(menu.getPerms()))
                .collect(Collectors.toList()));
        return authorities;
    }
    @Override
    public boolean isAccountNonExpired() { return true; }
    @Override
    public boolean isAccountNonLocked() {
        return "0".equals(status);
    }
    @Override
    public boolean isCredentialsNonExpired() { return true; }
    @Override
    public boolean isEnabled() {
        return "0".equals(delFlag);
    }
}

? 角色實體

@Data
@TableName("sys_role")
public class SysRole {
 @TableId(type = IdType.AUTO)
 private Long roleId;
 private String roleName;
 private String roleKey;
 @TableField(exist = false)
 private List<SysMenu> menus;
}

? 菜單實體

@Data
@TableName("sys_menu")
public class SysMenu {
    @TableId(type = IdType.AUTO)
    private Long menuId;
    private String menuName;
    private String perms;
}

? 用戶屬性實體

@Data
public class SysUserAttr {
    private Long userId;
    private String attrKey;
    private String attrValue;
}

? 決策表實體

@Data
@TableName("sys_policy")
public class SysPolicy {
    @TableId(type = IdType.AUTO)
    private Long policyId;
    private String policyName;
    private String targetResource;
    private String conditionExpression;
}

6. MyBatis-Plus Mapper配置

除了 UserMapper 增加 selectUserAttrByUserId 方法以及新增 SysPolicyMapper,其余代碼與上個章節(jié)一致!

UserMapper接口 : 主要是通過用戶角色中間表獲取角色信息(角色信息中又包含了菜單信息)

@Mapper
public interface UserMapper extends BaseMapper<SysUser> {
    @Select("SELECT r.* FROM sys_role r " +
            "JOIN sys_user_role ur ON r.role_id = ur.role_id " +
            "WHERE ur.user_id = #{userId}")
    @Results({
            @Result(property = "roleId", column = "role_id"),
            @Result(property = "menus", column = "role_id",
                    many = @Many(select = "com.toher.springsecurity.demo.abac.mapper.MenuMapper.selectByUserId"))
    })
    List<SysRole> selectRolesByUserId(Long userId);
    /**
     * 獲取用戶屬性
     * @param userId
     * @return
     */
    @Select("SELECT * FROM sys_user_attr WHERE user_id = #{userId}")
    List<SysUserAttr> selectUserAttrByUserId(Long userId);
}

RoleMapper接口 : 主要是通過角色菜單中間表獲取菜單信息

@Mapper
public interface RoleMapper extends BaseMapper<SysRole> {
    @Select("SELECT m.* FROM sys_menu m " +
            "JOIN sys_role_menu rm ON m.menu_id = rm.menu_id " +
            "WHERE rm.role_id = #{roleId}")
    List<SysMenu> selectMenusByRoleId(Long roleId);
}

RoleMapper接口 : 主要是通過角色菜單中間表獲取菜單信息

@Mapper
public interface MenuMapper extends BaseMapper<SysMenu> {
 @Select("SELECT DISTINCT m.* FROM sys_menu m " +
         "JOIN sys_role_menu rm ON m.menu_id = rm.menu_id " +
         "JOIN sys_user_role ur ON rm.role_id = ur.role_id " +
         "WHERE ur.user_id = #{userId}")
 List<SysMenu> selectByUserId(Long userId);
}

SysPolicyMapper接口

@Mapper
public interface SysPolicyMapper extends BaseMapper<SysPolicy> {
}

7. 自定義UserDetailsService實現(xiàn)

自定義 UserDetailsService 繼承 UserDetailsService,重寫 loadUserByUsername 方法,注入 UserMapper 以及 roleMapper通過用戶名查詢數(shù)據(jù)庫數(shù)據(jù),同時將用戶的角色、菜單資源集合、用戶屬性集合 一并賦值;

@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
    private final UserMapper userMapper;
    private final RoleMapper roleMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1. 查詢基礎(chǔ)用戶信息
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUser::getUsername, username);
        SysUser user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new UsernameNotFoundException("用戶不存在");
        }
        // 2. 加載角色和權(quán)限
        List<SysRole> roles = userMapper.selectRolesByUserId(user.getUserId());
        roles.forEach(role ->
                role.setMenus(roleMapper.selectMenusByRoleId(role.getRoleId()))
        );
        user.setRoles(roles);
        // 3. 檢查賬戶狀態(tài)
        if (!user.isEnabled()) {
            throw new DisabledException("用戶已被禁用");
        }
        // 4. 用戶的屬性集合,用于 ABAC 動態(tài)權(quán)限評估
        List<SysUserAttr> attrs = userMapper.selectUserAttrByUserId(user.getUserId());
        // 轉(zhuǎn)成map集合
        user.setAttrs(attrs.stream()
                .collect(Collectors.toMap(s -> s.getAttrKey(), s -> s.getAttrValue())));
        return user;
    }
}

8. 實現(xiàn)方式一:自定義MethodSecurityExpressionHandler

編寫策略決策引擎

@Component
public class AbacDecisionEngine {
    private final SpelExpressionParser parser = new SpelExpressionParser();
    @Autowired
    private SysPolicyMapper sysPolicyMapper;
    public boolean check(Authentication authentication, String resource) {
        SysUser userDetails = (SysUser) authentication.getPrincipal();
        // 加載策略集
        LambdaQueryWrapper<SysPolicy> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysPolicy::getTargetResource, resource);
        List<SysPolicy> policies = sysPolicyMapper.selectList(queryWrapper);
        if (policies.isEmpty()) {
            return false;
        }
        // 構(gòu)建評估上下文
        EvaluationContext context = new StandardEvaluationContext();
        // 將用戶傳入表達(dá)式上下文 如:#user.attrs['department'] == 'IT'
        // 其中user前綴就是我們傳入的user
        context.setVariable("user", userDetails);
        return policies.stream().allMatch(policy ->
                parser.parseExpression(policy.getConditionExpression()).getValue(context, Boolean.class)
        );
    }
}

重寫PermissionEvaluator

@RequiredArgsConstructor
public class AbacPermissionEvaluator implements PermissionEvaluator {
    private final AbacDecisionEngine abacEngine;
    @Override
    public boolean hasPermission(Authentication auth, Object targetDomainObject, Object permission) {
        return abacEngine.check(auth, (String)permission);
    }
    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        // 本示例僅實現(xiàn) hasPermission(Authentication, Object, Object)
        return false;
    }
}

9. 實現(xiàn)方式二:自定義注解

使用自定義注解,Spring Security 將在每次方法調(diào)用時調(diào)用該 bean 上給定的方法

@Component("authz")
@RequiredArgsConstructor
public class AuthorizationLogic {
    private final SpelExpressionParser parser = new SpelExpressionParser();
    private final SysPolicyMapper sysPolicyMapper;
    public boolean check(MethodSecurityExpressionOperations operations, String permission) {
        SysUser userDetails = (SysUser) operations.getAuthentication().getPrincipal();
        // 加載策略集
        LambdaQueryWrapper<SysPolicy> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysPolicy::getTargetResource, permission);
        List<SysPolicy> policies = sysPolicyMapper.selectList(queryWrapper);
        if (policies.isEmpty()) {
            return false;
        }
        // 構(gòu)建評估上下文
        EvaluationContext context = new StandardEvaluationContext();
        // 將用戶傳入表達(dá)式上下文 如:#user.attrs['department'] == 'IT'
        // 其中user前綴就是我們傳入的user
        context.setVariable("user", userDetails);
        return policies.stream().allMatch(policy ->
                parser.parseExpression(policy.getConditionExpression()).getValue(context, Boolean.class)
        );
    }
}

10. Spring Security配置文件

@Configuration
//開啟方法級的安全控制
@EnableMethodSecurity
@RequiredArgsConstructor
public class AbacSecurityConfig {
    private final UserDetailsServiceImpl userDetailsService;
    private final AbacDecisionEngine abacEngine;
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.
                authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/setPassword").permitAll()
                        //配置形式ADMIN角色可以訪問/admin/view
                        .requestMatchers("/admin/view").hasRole("ADMIN")
                        .anyRequest().authenticated())
                        .userDetailsService(userDetailsService)
                        .formLogin(withDefaults())
                        .logout(withDefaults())
                ;
        return http.build();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    /**
     * 配置 Method Security Expression Handler,使用自定義的 PermissionEvaluator
     */
    @Bean
    public MethodSecurityExpressionHandler methodSecurityExpressionHandler(AbacDecisionEngine abacEngine) {
        DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
        handler.setPermissionEvaluator(new AbacPermissionEvaluator(abacEngine));
        return handler;
    }

11. controller測試文件

新增一個 AdminController 作為ABAC屬性權(quán)限模型的測試

@RestController
@RequestMapping("/api")
public class AdminController {
    /**
     * MethodSecurityExpressionHandler方式
     *
     * @return
     */
    @PreAuthorize("hasPermission(null, 'admin:menu')")
    @GetMapping("/admin")
    public ResponseEntity<?> getAdminData() {
        return ResponseEntity.ok("MethodSecurityExpressionHandler方式");
    }
    /**
     * 自定義注解的方式
     * @return
     */
    @PreAuthorize("@authz.check(#root, 'admin:menu')")
    @GetMapping("/authz")
    public ResponseEntity<?> authz() {
        return ResponseEntity.ok("自定義注解的方式");
    }
    /**
     * 以下RBAC角色 + ABAC屬性的混合校驗 可以復(fù)制測試
     * @PreAuthorize("hasAuthority('admin:menu') and @abacDecisionEngine.check(authentication, 'admin:menu')")
     * 
     * @PreAuthorize("hasRole('ADMIN') and @authz.check(#root, 'admin:menu')")
     *
     * @return
     */
    @PreAuthorize("hasRole('ADMIN') and @abacDecisionEngine.check(authentication, 'admin:menu')")
    @GetMapping("/admin/test")
    public ResponseEntity<?> test() {
        return ResponseEntity.ok("RBAC角色 + ABAC屬性的混合校驗");
    }
}

小伙伴們可以根據(jù)博主的代碼編寫完成后,進(jìn)行運行測試,新增用戶屬性并可以加入更多的策略來測試,如:

這里博主順便整理一些常見的策略以供大家參考:

場景描述SpEL表達(dá)式
時間段訪問控制T(java.time.LocalTime).now().isBetween('09:00', '17:00')
安全等級驗證attrs['securityLevel'] >= 3 && authentication.isAuthenticated()
地理位置限制attrs['country'] == 'CN' && attrs['ipRegion'] == 'Shanghai'
多因素認(rèn)證驗證attrs['mfaEnabled'] == true && authentication.getAuthorities().contains('MFA_VERIFIED')

12. 完整工作流程

請求到達(dá):GET /api/admin

身份認(rèn)證:通過 UserDetailsService 加載用戶信息

屬性加載:從sys_user_attr 表獲取用戶屬性

策略匹配:查詢sys_policy 表中 target_resource admin:menu 的策略

表達(dá)式評估:使用SpEL評估 #user.attrs['department'] == 'IT'

訪問決策:所有策略滿足即允許訪問

14. 總結(jié)

通過本章節(jié)相信大家對ABAC屬性權(quán)限模型的開發(fā)已經(jīng)能掌握了,值得一提的是在實際開發(fā)中,我們?yōu)榱吮苊鈹?shù)據(jù)庫壓力建議還要對用戶信息、策略信息等采用緩存處理,相關(guān)用戶屬性、決策也可以按照自身需求進(jìn)行拓展!

通過RBAC角色模型 + ABAC屬性權(quán)限模型這種設(shè)計,你可以靈活地根據(jù)業(yè)務(wù)變化調(diào)整權(quán)限策略,實現(xiàn)更細(xì)粒度的安全控制。希望這篇實戰(zhàn)文章能夠為你的項目開發(fā)提供參考與啟發(fā)!

到此這篇關(guān)于Spring Security基于數(shù)據(jù)庫的ABAC屬性權(quán)限模型實戰(zhàn)開發(fā)教程的文章就介紹到這了,更多相關(guān)Spring Security ABAC權(quán)限模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java實現(xiàn)收藏名言語句臺詞的app

    java實現(xiàn)收藏名言語句臺詞的app

    本文給大家分享的是使用java制作的記錄名人名言臺詞等等讓你難忘的語句的APP的代碼,非常的實用,有需要的小伙伴可以參考下。
    2015-04-04
  • Java8使用Function讀取文件

    Java8使用Function讀取文件

    這篇文章主要為大家詳細(xì)介紹了Java8如何使用Function讀取文件,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,有需要的小伙伴可以參考一下
    2024-12-12
  • Netty實現(xiàn)簡易版的RPC框架過程詳解

    Netty實現(xiàn)簡易版的RPC框架過程詳解

    這篇文章主要為大家介紹了Netty實現(xiàn)簡易版的RPC框架過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Java8的Stream()與ParallelStream()的區(qū)別說明

    Java8的Stream()與ParallelStream()的區(qū)別說明

    這篇文章主要介紹了Java8的Stream()與ParallelStream()的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 關(guān)于Idea創(chuàng)建Java項目并引入lombok包的問題(lombok.jar包免費下載)

    關(guān)于Idea創(chuàng)建Java項目并引入lombok包的問題(lombok.jar包免費下載)

    很多朋友遇到當(dāng)idea創(chuàng)建java項目時,命名安裝了lombok插件卻不能使用注解,原因有兩個大家可以參考下本文,本文對每種原因分析給出了解決方案,需要的朋友參考下吧
    2021-06-06
  • Java基于socket實現(xiàn)簡易聊天室實例

    Java基于socket實現(xiàn)簡易聊天室實例

    這篇文章主要介紹了Java基于socket實現(xiàn)簡易聊天室的方法,實例分析了java基于socket實現(xiàn)聊天室服務(wù)端與客戶端的相關(guān)技巧,需要的朋友可以參考下
    2015-05-05
  • java實現(xiàn)小型局域網(wǎng)群聊功能(C/S模式)

    java實現(xiàn)小型局域網(wǎng)群聊功能(C/S模式)

    這篇文章主要介紹了java利用TCP協(xié)議實現(xiàn)小型局域網(wǎng)群聊功能(C/S模式) ,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-08-08
  • 深入IDEA Debug問題透析詳解

    深入IDEA Debug問題透析詳解

    這篇文章主要為大家介紹了深入IDEA Debug問題透析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • 詳解Java注解實現(xiàn)自己的ORM

    詳解Java注解實現(xiàn)自己的ORM

    這篇文章主要介紹了Java注解實現(xiàn)自己的ORM知識,本文通過示例代碼給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2022-10-10
  • Spring的自定義擴(kuò)展標(biāo)簽NamespaceHandler解析

    Spring的自定義擴(kuò)展標(biāo)簽NamespaceHandler解析

    這篇文章主要介紹了Spring的自定義擴(kuò)展標(biāo)簽NamespaceHandler解析,在很多情況下,我們需要為系統(tǒng)提供可配置化支持,簡單的做法可以直接基于Spring的標(biāo)準(zhǔn)Bean來配置,Spring提供了可擴(kuò)展Schema的支持,這是一個不錯的折中方案,需要的朋友可以參考下
    2023-12-12

最新評論