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

SpringBoot集成Mybatis-Plus多租戶架構(gòu)實現(xiàn)

 更新時間:2021年09月03日 11:20:47   作者:小東啊  
本文主要介紹了SpringBoot集成Mybatis-Plus多租戶架構(gòu)實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

目前公司產(chǎn)品就是對外企業(yè)服務(wù),入職后了解到SaaS模式和私有部署,當(dāng)我第一次聽到SaaS時,我不是很理解。經(jīng)過查閱資料,以及在后續(xù)研發(fā)功能時,不斷的加深了對多租戶的理解。

那么接下來讓我們問自己幾個問題:
1.什么是多租戶架構(gòu)?
2.多租戶架構(gòu)方案以及各自的優(yōu)缺點?
3.多租戶架構(gòu)的適用場景?

一. 什么是多租戶

多租戶技術(shù)或稱多重租賃技術(shù),簡稱SaaS,是一種軟件架構(gòu)技術(shù),是實現(xiàn)如何在多用戶環(huán)境下(多用戶一般是面向企業(yè)用戶)共用相同的系統(tǒng)或程序組件,并且可確保各用戶間數(shù)據(jù)的隔離性。簡單講:在一臺服務(wù)器上運行單個應(yīng)用實例,它為多個租戶(客戶)提供服務(wù)。從定義中我們可以理解:多租戶是一種架構(gòu),目的是為了讓多用戶環(huán)境下使用同一套程序,且保證用戶間數(shù)據(jù)隔離。那么重點就很淺顯易懂了,多租戶的重點就是同一套程序下實現(xiàn)多用戶數(shù)據(jù)的隔離。

二. 多租戶架構(gòu)以及數(shù)據(jù)隔離方案

多租戶在數(shù)據(jù)存儲上主要存在三種方案,分別是:

1. 獨立數(shù)據(jù)庫

即一個租戶一個數(shù)據(jù)庫,這種方案的用戶數(shù)據(jù)隔離級別最高,安全性最好,但成本較高。

  • 優(yōu)點:為不同的租戶提供獨立的數(shù)據(jù)庫,有助于簡化數(shù)據(jù)模型的擴(kuò)展設(shè)計,滿足不同租戶的獨特需求;如果出現(xiàn)故障,恢復(fù)數(shù)據(jù)比較簡單。
  • 缺點:增多了數(shù)據(jù)庫的安裝數(shù)量,隨之帶來維護(hù)成本和購置成本的增加。

2. 共享數(shù)據(jù)庫,獨立 Schema

也就是說 共同使用一個數(shù)據(jù)庫 使用表進(jìn)行數(shù)據(jù)隔離
多個或所有租戶共享Database,但是每個租戶一個Schema(也可叫做一個user)。底層庫比如是:DB2、ORACLE等,一個數(shù)據(jù)庫下可以有多個SCHEMA。

優(yōu)點:為安全性要求較高的租戶提供了一定程度的邏輯數(shù)據(jù)隔離,并不是完全隔離;每個數(shù)據(jù)庫可支持更多的租戶數(shù)量。

缺點:如果出現(xiàn)故障,數(shù)據(jù)恢復(fù)比較困難,因為恢復(fù)數(shù)據(jù)庫將牽涉到其他租戶的數(shù)據(jù);

3. 共享數(shù)據(jù)庫,共享 Schema,共享數(shù)據(jù)表

也就是說 共同使用一個數(shù)據(jù)庫一個表 使用字段進(jìn)行數(shù)據(jù)隔離

即租戶共享同一個Database、同一個Schema,但在表中增加TenantID多租戶的數(shù)據(jù)字段。這是共享程度最高、隔離級別最低的模式。

簡單來講,即每插入一條數(shù)據(jù)時都需要有一個客戶的標(biāo)識。這樣才能在同一張表中區(qū)分出不同客戶的數(shù)據(jù),這也是我們系統(tǒng)目前用到的(tenant_id)

  • 優(yōu)點:三種方案比較,第三種方案的維護(hù)和購置成本最低,允許每個數(shù)據(jù)庫支持的租戶數(shù)量最多。
  • 缺點:隔離級別最低,安全性最低,需要在設(shè)計開發(fā)時加大對安全的開發(fā)量;數(shù)據(jù)備份和恢復(fù)最困難,需要逐表逐條備份和還原。

三.多租戶架構(gòu)適用場景?

衡量三種模式主要考慮的因素是隔離還是共享。
1.成本角度因素
隔離性越好,設(shè)計和實現(xiàn)的難度和成本越高,初始成本越高。共享性越好,同一運營成本下支持的用戶越多,運營成本越低。

2.安全因素
要考慮業(yè)務(wù)和客戶的安全方面的要求。安全性要求越高,越要傾向于隔離。

3.從租戶數(shù)量上考慮
主要考慮下面一些因素

  • 系統(tǒng)要支持多少租戶?上百?上千還是上萬?可能的租戶越多,越傾向于共享。
  • 平均每個租戶要存儲數(shù)據(jù)需要的空間大小。存貯的數(shù)據(jù)越多,越傾向于隔離。
  • 每個租戶的同時訪問系統(tǒng)的最終用戶數(shù)量。需要支持的越多,越傾向于隔離。
  • 是否想針對每一租戶提供附加的服務(wù),例如數(shù)據(jù)的備份和恢復(fù)等。這方面的需求越多, 越傾向于隔離

4.技術(shù)儲備
共享性越高,對技術(shù)的要求越高。

四. 技術(shù)實現(xiàn)

技術(shù)選型: Mybatis-Plus
這里我們選用了第三種方案(共享數(shù)據(jù)庫,共享 Schema,共享數(shù)據(jù)表)來實現(xiàn),也就意味著,每個數(shù)據(jù)表都需要有一個租戶標(biāo)識(tenant_id)

現(xiàn)在有數(shù)據(jù)庫表(user)如下:

字段名 字段類型 描述
id int(11) 主鍵
name varchar(30) 姓名
tenant_id int(11) 多租戶id

將tenant_id視為租戶ID,用來隔離租戶與租戶之間的數(shù)據(jù),如果要查詢當(dāng)前租戶的用戶,SQL大致如下:

SELECT * FROM user WHERE tenant_id = 1;

試想一下,除了一些系統(tǒng)共用的表以外,其他租戶相關(guān)的表,我們都需要加上AND tenant_id = ?查詢條件,數(shù)據(jù)表多的情況時就會漏加導(dǎo)致數(shù)據(jù)泄露。
幸虧有mybatis-plus這個插件,可以極為方便的實現(xiàn)多租戶SQL解析器,官方文檔如下:
多租戶 SQL 解析器

正式進(jìn)入主題

環(huán)境搭建演示

1. 創(chuàng)建Spring Boot項目

pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.xd</groupId>
    <artifactId>mybatis-plus-multi-tenancy</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mybatis-plus-multi-tenancy</name>
    <description>基于Spring Boot Mybatis-Plus的多租戶架構(gòu)</description>


    <properties>
        <java.version>1.8</java.version>
        <mybatis-plus.version>3.1.2</mybatis-plus.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--Mybatis-Plus依賴-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!--測試相關(guān)依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.0.M1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

application.properties

# 數(shù)據(jù)源配置
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=3
spring.datasource.hikari.maximum-pool-size=10
# 不能小于30秒,否則默認(rèn)回到1800秒
spring.datasource.hikari.max-lifetime=30000
spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/multi?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root

logging.level.com.xd.mybatisplusmultitenancy=debug

對應(yīng)的SQL數(shù)據(jù)庫初始化schema文件

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `name` varchar(30) DEFAULT NULL COMMENT '姓名',
  `tenant_id` int(11) NOT NULL COMMENT '多租戶ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

SET FOREIGN_KEY_CHECKS = 1;

MybatisPlusConfig
核心配置:TenantSqlParser
多租戶處理器

package com.xd.mybatisplusmultitenancy.config;

import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.NullValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @Classname PreTenantHandler
 * @Description 租戶處理器 -主要實現(xiàn)mybatis-plus https://mp.baomidou.com/guide/tenant.html
 * @Author Created by Lihaodong (alias:小東啊) lihaodongmail@163.com
 * @Date 2019-08-09 23:34
 * @Version 1.0
 */
@Slf4j
@Component
public class MyTenantHandler implements TenantHandler {

    /**
     * 多租戶標(biāo)識
     */
    private static final String SYSTEM_TENANT_ID = "tenant_id";

    /**
     * 需要過濾的表
     */
    private static final List<String> IGNORE_TENANT_TABLES = new ArrayList<>();

    @Autowired
    private MyContext apiContext;


    /**
     * 租戶Id
     *
     * @return
     */
    @Override
    public Expression getTenantId() {
        // 從當(dāng)前系統(tǒng)上下文中取出當(dāng)前請求的服務(wù)商ID,通過解析器注入到SQL中。
        Long tenantId = apiContext.getCurrentTenantId();
        log.debug("當(dāng)前租戶為{}", tenantId);
        if (tenantId == null) {
            return new NullValue();
        }
        return new LongValue(tenantId);
    }

    /**
     * 租戶字段名
     *
     * @return
     */
    @Override
    public String getTenantIdColumn() {
        return SYSTEM_TENANT_ID;
    }

    /**
     * 根據(jù)表名判斷是否進(jìn)行過濾
     * 忽略掉一些表:如租戶表(sys_tenant)本身不需要執(zhí)行這樣的處理
     *
     * @param tableName
     * @return
     */
    @Override
    public boolean doTableFilter(String tableName) {
        return IGNORE_TENANT_TABLES.stream().anyMatch((e) -> e.equalsIgnoreCase(tableName));
    }
}

MybatisPlus的配置

package com.xd.mybatisplusmultitenancy.config;

import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;

import java.util.ArrayList;
import java.util.List;

/**
 * @Classname MybatisPlusConfig
 * @Description TODO
 * @Author Created by Lihaodong (alias:小東啊) lihaodongmail@163.com
 * @Date 2019-08-09 22:44
 * @Version 1.0
 */
@Configuration
@MapperScan("com.xd.mybatisplusmultitenancy.mapper")
public class MybatisPlusConfig {


    @Autowired
    private MyTenantHandler myTenantHandler;

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();

        // SQL解析處理攔截:增加租戶處理回調(diào)。

        List<ISqlParser> sqlParserList = new ArrayList<>();
        // 攻擊 SQL 阻斷解析器、加入解析鏈
        sqlParserList.add(new BlockAttackSqlParser());
        // 多租戶攔截
        TenantSqlParser tenantSqlParser = new TenantSqlParser();
        tenantSqlParser.setTenantHandler(myTenantHandler);
        sqlParserList.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(sqlParserList);
        return paginationInterceptor;
    }

    /**
     * 性能分析攔截器,不建議生產(chǎn)使用
     * 用來觀察 SQL 執(zhí)行情況及執(zhí)行時長
     */
    @Bean(name = "performanceInterceptor")
    public PerformanceInterceptor performanceInterceptor() {
        return new PerformanceInterceptor();
    }
}

自定義系統(tǒng)的上下文

package com.xd.mybatisplusmultitenancy.config;


import org.springframework.stereotype.Component;


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @Classname ApiContext
 * @Description 當(dāng)前系統(tǒng)的上下文
 * @Author Created by Lihaodong (alias:小東啊) lihaodongmail@163.com
 * @Date 2019-08-09 22:47
 * @Version 1.0
 */
@Component
public class MyContext {


    private static final String KEY_CURRENT_TENANT_ID = "KEY_CURRENT_PROVIDER_ID";
    private static final Map<String, Object> M_CONTEXT = new ConcurrentHashMap<>();


    public void setCurrentTenantId(Long tenantId) {
        M_CONTEXT.put(KEY_CURRENT_TENANT_ID, tenantId);
    }


    public Long getCurrentTenantId() {
        return (Long) M_CONTEXT.get(KEY_CURRENT_TENANT_ID);
    }
}

Entity、Mapper 省略...

2. 單元測試

package com.xd.mybatisplusmultitenancy.test;


import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xd.mybatisplusmultitenancy.MybatisPlusMultiTenancyApplication;
import com.xd.mybatisplusmultitenancy.config.MyContext;
import com.xd.mybatisplusmultitenancy.entity.User;
import com.xd.mybatisplusmultitenancy.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;


import java.sql.Wrapper;


/**
 * @Classname MybatisPlusMultiTenancyApplicationTests
 * @Description TODO
 * @Author Created by Lihaodong (alias:小東啊) lihaodongmail@163.com
 * @Date 2019-08-09 22:50
 * @Version 1.0
 */
@Slf4j
@RunWith(SpringRunner.class)
@FixMethodOrder(MethodSorters.JVM)
@SpringBootTest(classes = MybatisPlusMultiTenancyApplication.class)
public class MybatisPlusMultiTenancyApplicationTests {




    @Autowired
    private MyContext apiContext;


    @Autowired
    private UserMapper userMapper;


    /**
     * 模擬當(dāng)前系統(tǒng)的多租戶Id
     */
    @Before
    public void before() {
        // 在上下文中設(shè)置當(dāng)前多租戶id
        apiContext.setCurrentTenantId(1L);
    }


    @Test
    public void insert() {
        // 新增數(shù)據(jù)
        User user = new User().setName("小明");
        //判斷一個條件是true還是false
        Assert.assertTrue(userMapper.insert(user) > 0);
        user = userMapper.selectById(user.getId());
        log.info("插入數(shù)據(jù):{}", user);
        // 判斷是否相等
        Assert.assertEquals(apiContext.getCurrentTenantId(), user.getTenantId());
    }


    @Test
    public void selectList() {
        userMapper.selectList(null).forEach((e) -> {
            log.info("查詢數(shù)據(jù){}", e);
            Assert.assertEquals(apiContext.getCurrentTenantId(), e.getTenantId());
        });
    }
}

運行結(jié)果

插入數(shù)據(jù)

2019-08-23 22:32:52.755 INFO 77902 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-08-23 22:32:53.210 INFO 77902 --- [ main] .MybatisPlusMultiTenancyApplicationTests : Started MybatisPlusMultiTenancyApplicationTests in 5.181 seconds (JVM running for 6.86)
2019-08-23 22:32:53.613 DEBUG 77902 --- [ main] c.x.m.config.MyTenantHandler : 當(dāng)前租戶為1
2019-08-23 22:32:53.614 DEBUG 77902 --- [ main] c.x.m.mapper.UserMapper.insert : ==> Preparing: INSERT INTO user (name, tenant_id) VALUES (?, 1)
2019-08-23 22:32:53.648 DEBUG 77902 --- [ main] c.x.m.mapper.UserMapper.insert : ==> Parameters: 小明(String)
2019-08-23 22:32:53.701 DEBUG 77902 --- [ main] c.x.m.mapper.UserMapper.insert : <== Updates: 1
Time:64 ms - ID:com.xd.mybatisplusmultitenancy.mapper.UserMapper.insert
Execute SQL:INSERT INTO user (name, tenant_id) VALUES ('小明', 1)

2019-08-23 22:32:53.720 DEBUG 77902 --- [ main] c.x.m.config.MyTenantHandler : 當(dāng)前租戶為1
2019-08-23 22:32:53.722 DEBUG 77902 --- [ main] c.x.m.mapper.UserMapper.selectById : ==> Preparing: SELECT id, name, tenant_id FROM user WHERE user.tenant_id = 1 AND id = ?
2019-08-23 22:32:53.726 DEBUG 77902 --- [ main] c.x.m.mapper.UserMapper.selectById : ==> Parameters: 1(Long)
2019-08-23 22:32:53.745 DEBUG 77902 --- [ main] c.x.m.mapper.UserMapper.selectById : <== Total: 1
Time:20 ms - ID:com.xd.mybatisplusmultitenancy.mapper.UserMapper.selectById
Execute SQL:SELECT id, name, tenant_id FROM user WHERE user.tenant_id = 1 AND id = 1

2019-08-23 22:32:53.746 INFO 77902 --- [ main] .MybatisPlusMultiTenancyApplicationTests : 插入數(shù)據(jù):User(id=1, name=小明, tenantId=1)
2019-08-23 22:32:53.762 INFO 77902 --- [ Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2019-08-23 22:32:53.764 INFO 77902 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2019-08-23 22:32:53.777 INFO 77902 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.

查詢數(shù)據(jù)

2019-08-23 22:34:26.700  INFO 77922 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-08-23 22:34:27.100  INFO 77922 --- [           main] .MybatisPlusMultiTenancyApplicationTests : Started MybatisPlusMultiTenancyApplicationTests in 4.521 seconds (JVM running for 6.268)
2019-08-23 22:34:27.412 DEBUG 77922 --- [           main] c.x.m.config.MyTenantHandler             : 當(dāng)前租戶為1
2019-08-23 22:34:27.414 DEBUG 77922 --- [           main] c.x.m.mapper.UserMapper.selectList       : ==>  Preparing: SELECT id, name, tenant_id FROM user WHERE user.tenant_id = 1
2019-08-23 22:34:27.442 DEBUG 77922 --- [           main] c.x.m.mapper.UserMapper.selectList       : ==> Parameters:
2019-08-23 22:34:27.464 DEBUG 77922 --- [           main] c.x.m.mapper.UserMapper.selectList       : <==      Total: 1
 Time:22 ms - ID:com.xd.mybatisplusmultitenancy.mapper.UserMapper.selectList
Execute SQL:SELECT id, name, tenant_id FROM user WHERE user.tenant_id = 1

2019-08-23 22:34:27.467  INFO 77922 --- [           main] .MybatisPlusMultiTenancyApplicationTests : 查詢數(shù)據(jù)User(id=1, name=小明, tenantId=1)
2019-08-23 22:34:27.480  INFO 77922 --- [       Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2019-08-23 22:34:27.482  INFO 77922 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2019-08-23 22:34:27.492  INFO 77922 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

從打印的日志不難看出,目前這個方案還是比較完美的,僅需簡單的配置,讓開發(fā)者極大方便的進(jìn)行開發(fā),同時又最大程度的保證了數(shù)據(jù)的安全性

源碼下載: https://github.com/LiHaodong888/SpringBootLearn

具體項目:https://gitee.com/li_haodong/pre

參考資料

https://www.cnblogs.com/pingfan21/p/7478242.html
https://segmentfault.com/a/1190000017197768

到此這篇關(guān)于SpringBoot集成Mybatis-Plus多租戶架構(gòu)實現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot集成Mybatis-Plus多租戶內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Tree組件實現(xiàn)支持50W數(shù)據(jù)方法剖析

    Tree組件實現(xiàn)支持50W數(shù)據(jù)方法剖析

    這篇文章主要為大家介紹了Tree組件實現(xiàn)支持50W數(shù)據(jù)的方法剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • String s = new String(''a '') 到底產(chǎn)生幾個對象

    String s = new String(''a '') 到底產(chǎn)生幾個對象

    這篇文章主要介紹了String s = new String(" a ") 到底產(chǎn)生幾個對象,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • Java集合List相關(guān)面試題整理大全

    Java集合List相關(guān)面試題整理大全

    這篇文章主要給大家介紹了關(guān)于Java集合List相關(guān)面試題整理的相關(guān)資料,下面將提供一些常見的Java集合類面試題及其解答,幫助讀者更好地準(zhǔn)備面試,需要的朋友可以參考下
    2024-01-01
  • Spring Boot簡介與快速搭建詳細(xì)步驟

    Spring Boot簡介與快速搭建詳細(xì)步驟

    SpringBoot其本身沒有添加什么新的技術(shù),就是整合了一些現(xiàn)有的框架,并提供了一些默認(rèn)的配置,就是這些默認(rèn)的配置,極大的提高了我們的開發(fā)效率。這篇文章主要介紹了Spring Boot簡介與快速搭建,需要的朋友可以參考下
    2021-05-05
  • Java的break語句最佳實踐

    Java的break語句最佳實踐

    break 是 Java 等主流語言中的控制流語句,用于立即終止當(dāng)前執(zhí)行塊并跳轉(zhuǎn)到后續(xù)代碼,本文給大家介紹Java的break語句最佳實踐記錄,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • Java反轉(zhuǎn)鏈表測試過程介紹

    Java反轉(zhuǎn)鏈表測試過程介紹

    這篇文章主要介紹了Java反轉(zhuǎn)鏈表測試過程,學(xué)習(xí)過數(shù)據(jù)結(jié)構(gòu)的小伙伴們,對鏈表想來是并不陌生。本篇文章將為大家介紹幾種在Java語言當(dāng)中,實現(xiàn)鏈表反轉(zhuǎn)的幾種方法,以下是具體內(nèi)容
    2023-04-04
  • SpringBoot熔斷機(jī)制之CircuitBreaker詳解

    SpringBoot熔斷機(jī)制之CircuitBreaker詳解

    這篇文章主要介紹了SpringBoot熔斷機(jī)制之CircuitBreaker詳解,SpringBoot的熔斷機(jī)制在微服務(wù)架構(gòu)中扮演著重要角色,其中CircuitBreaker是其核心機(jī)制之一,用于防止服務(wù)的異常狀態(tài)影響到整個系統(tǒng)的運作,需要的朋友可以參考下
    2023-10-10
  • SpringBoot實現(xiàn)WebSocket的示例代碼

    SpringBoot實現(xiàn)WebSocket的示例代碼

    這篇文章主要為大家詳細(xì)介紹了SpringBoot實現(xiàn)WebSocket的相關(guān)知識,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-11-11
  • spring控制事務(wù)的三種方式小結(jié)

    spring控制事務(wù)的三種方式小結(jié)

    這篇文章主要介紹了spring控制事務(wù)的三種方式小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java實現(xiàn)簡單修改文件名的方法分析

    Java實現(xiàn)簡單修改文件名的方法分析

    這篇文章主要介紹了Java實現(xiàn)簡單修改文件名的方法,結(jié)合具體實例分析了2種比較常用的java文件名修改方法,需要的朋友可以參考下
    2017-09-09

最新評論

江川县| 榆社县| 瓮安县| 平安县| 浦北县| 饶平县| 宁阳县| 饶平县| 裕民县| 嘉黎县| 巩义市| 苍南县| 临沂市| 江津市| 辽阳县| 浪卡子县| 武乡县| 宜城市| 南通市| 盐山县| 古浪县| 临潭县| 固始县| 洪洞县| 万盛区| 呈贡县| 四平市| 花莲县| 新田县| 宣汉县| 黄龙县| 丰宁| 宣威市| 襄樊市| 盖州市| 马尔康县| 思南县| 安远县| 浦城县| 瑞金市| 东乡族自治县|