广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Spring Boot 优雅整合多数据源
  • 144
分享到

Spring Boot 优雅整合多数据源

2024-04-02 19:04:59 144人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录何时用到多数据源整合单一的数据源整合mybatis多数据源如何整合?什么是动态数据源?数据源切换如何保证线程隔离?如何构造一个动态数据源?定义一个注解如何与Mybatis整合?演

前言:

什么是多数据源?最常见的单一应用中最多涉及到一个数据库,即是一个数据源(Datasource)。那么顾名思义,多数据源就是在一个单一应用中涉及到了两个及以上的数据库了。

其实在配置数据源的时候就已经很明确这个定义了,如以下代码:

@Bean(name = "dataSource")
    public DataSource dataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setPassword(passWord);
        return druidDataSource;
    }

url、username、password这三个属性已经唯一确定了一个数据库了,DataSource则是依赖这三个创建出来的。则多数据源即是配置多个DataSource(暂且这么理解)。

何时用到多数据源

正如前言介绍到的一个场景,相信大多数做过医疗系统的都会和HIS打交道,为了简化护士以及医生的操作流程,必须要将必要的信息从HIS系统对接过来,据我了解的大致有两种方案如下:

HIS提供视图,比如医护视图、患者视图等,而此时其他系统只需要定时的从HIS视图中读取数据同步到自己数据库中即可。

  • HIS提供接口,无论是WEBService还是Http形式都是可行的,此时其他系统只需要按照要求调接口即可。
  • 很明显第一种方案涉及到了至少两个数据库了,一个是HIS数据库,一个自己系统的数据库,在单一应用中必然需要用到多数据源的切换才能达到目的。

当然多数据源的使用场景还是有很多的,以上只是简单的一个场景。

整合单一的数据源

本文使用阿里的数据库连接池druid,添加依赖如下:

<!--druid连接池-->
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid-spring-boot-starter</artifactId>
   <version>1.1.9</version>
</dependency>

阿里的数据库连接池非常强大,比如数据监控、数据库加密等等内容,本文仅仅演示与Spring Boot整合的过程,一些其他的功能后续可以自己研究添加。

Druid连接池的starter的自动配置类是DruidDataSourceAutoConfigure,类上标注如下一行注解:

@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})

@EnableConfigurationProperties这个注解使得配置文件中的配置生效并且映射到指定类的属性。

”DruidStatProperties中指定的前缀是spring.datasource.druid,这个配置主要是用来设置连接池的一些参数。

DataSourceProperties中指定的前缀是spring.datasource,这个主要是用来设置数据库的url、username、password等信息。

因此我们只需要在全局配置文件中指定数据库的一些配置以及连接池的一些配置信息即可,前缀分别是spring.datasource.druid、spring.datasource,以下是个人随便配置的(application.properties):

spring.datasource.url=jdbc:Mysql://120.26.101.xxx:3306/xxx?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=xxxx
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#初始化连接大小
spring.datasource.druid.initial-size=0
#连接池最大使用连接数量
spring.datasource.druid.max-active=20
#连接池最小空闲
spring.datasource.druid.min-idle=0
#获取连接最大等待时间
spring.datasource.druid.max-wait=6000
spring.datasource.druid.validation-query=SELECT 1
#spring.datasource.druid.validation-query-timeout=6000
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
spring.datasource.druid.test-while-idle=true
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.druid.time-between-eviction-runs-millis=60000
#置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.druid.min-evictable-idle-time-millis=25200000
#spring.datasource.druid.max-evictable-idle-time-millis=
#打开removeAbandoned功能,多少时间内必须关闭连接
spring.datasource.druid.removeAbandoned=true
#1800秒,也就是30分钟
spring.datasource.druid.remove-abandoned-timeout=1800
#<!-- 1800秒,也就是30分钟 -->
spring.datasource.druid.log-abandoned=true
spring.datasource.druid.filters=mergeStat

在全局配置文件application.properties文件中配置以上的信息即可注入一个数据源到Spring Boot中。其实这仅仅是一种方式,下面介绍另外一种方式。

”在自动配置类中DruidDataSourceAutoConfigure中有如下一段代码:

@Bean(initMethod = "init")
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        LOGGER.info("Init DruidDataSource");
        return new DruidDataSourceWrapper();
    }

@ConditionalOnMissingBean和@Bean这两个注解的结合,意味着我们可以覆盖,只需要提前在ioc中注入一个DataSource类型的Bean即可。

”因此我们在自定义的配置类中定义如下配置即可:


    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource dataSource(){
        //做一些其他的自定义配置,比如密码加密等......
        return new DruidDataSource();
    }

以上介绍了两种数据源的配置方式,第一种比较简单,第二种适合扩展,按需选择。

整合Mybatis

Spring Boot 整合Mybatis其实很简单,简单的几步就搞定,首先添加依赖:

<dependency>
     <groupId>org.mybatis.spring.boot</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>2.0.0</version>
</dependency>

第二步找到自动配置类MybatisAutoConfiguration,有如下一行代码:

@EnableConfigurationProperties(MybatisProperties.class)

老套路了,全局配置文件中配置前缀为mybatis的配置将会映射到该类中的属性。

”可配置的东西很多,比如XML文件的位置、类型处理器等等,如下简单的配置:

mybatis.type-handlers-package=com.demo.typehandler
mybatis.configuration.map-underscore-to-camel-case=true

如果需要通过包扫描的方式注入Mapper,则需要在配置类上加入一个注解:@MapperScan,其中的value属性指定需要扫描的包。

直接在全局配置文件配置各种属性是一种比较简单的方式,其实的任何组件的整合都有不少于两种的配置方式,下面来介绍下配置类如何配置。

MybatisAutoConfiguration自动配置类有如下一断代码:

@Bean
  @ConditionalOnMissingBean
  public sqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {}

@ConditionalOnMissingBean和@Bean真是老搭档了,意味着我们又可以覆盖,只需要在IOC容器中注入SqlSessionFactory(Mybatis六剑客之一生产者)。

在自定义配置类中注入即可,如下:


    @Bean("sqlSessionFactory1")
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper*.xml"));
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        // 自动将数据库中的下划线转换为驼峰格式
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setDefaultFetchSize(100);
        configuration.setDefaultStatementTimeout(30);
        sqlSessionFactoryBean.setConfiguration(configuration);
        return sqlSessionFactoryBean.getObject();
    }

以上介绍了配置Mybatis的两种方式,其实在大多数场景中使用第一种已经够用了,至于为什么介绍第二种呢?当然是为了多数据源的整合而做准备了。

”在MybatisAutoConfiguration中有一行很重要的代码,如下:

@ConditionalOnSingleCandidate(DataSource.class)

@ConditionalOnSingleCandidate这个注解的意思是当IOC容器中只有一个候选Bean的实例才会生效。

这行代码标注在Mybatis的自动配置类中有何含义呢?下面介绍,哈哈哈~

多数据源如何整合?

上文留下的问题:为什么的Mybatis自动配置上标注如下一行代码:

@ConditionalOnSingleCandidate(DataSource.class)

以上这行代码的言外之意:当IOC容器中只有一个数据源DataSource,这个自动配置类才会生效。

”哦?照这样搞,多数据源是不能用Mybatis吗?

可能大家会有一个误解,认为多数据源就是多个的DataSource并存的,当然这样说也不是不正确。

多数据源的情况下并不是多个数据源并存的,Spring提供了AbstractRoutingDataSource这样一个抽象类,使得能够在多数据源的情况下任意切换,相当于一个动态路由的作用,作者称之为动态数据源。因此Mybatis只需要配置这个动态数据源即可。

什么是动态数据源?

动态数据源简单的说就是能够自由切换的数据源,类似于一个动态路由的感觉,Spring 提供了一个抽象类AbstractRoutingDataSource,这个抽象类中哟一个属性,如下:

private Map<Object, Object> targetDataSources;

targetDataSources是一个Map结构,所有需要切换的数据源都存放在其中,根据指定的KEY进行切换。当然还有一个默认的数据源。

AbstractRoutingDataSource这个抽象类中有一个抽象方法需要子类实现,如下:

protected abstract Object determineCurrentLookupKey();

determineCurrentLookupKey()这个方法的返回值决定了需要切换的数据源的KEY,就是根据这个KEY从targetDataSources取值(数据源)。

数据源切换如何保证线程隔离?

数据源属于一个公共的资源,在多线程的情况下如何保证线程隔离呢?不能我这边切换了影响其他线程的执行。

说到线程隔离,自然会想到ThreadLocal了,将切换数据源的KEY(用于从targetDataSources中取值)存储在ThreadLocal中,执行结束之后清除即可。

”单独封装了一个DataSourceHolder,内部使用ThreadLocal隔离线程,代码如下:


public class DataSourceHolder {
    //线程  本地环境
    private static final ThreadLocal<String> dataSources = new InheritableThreadLocal();

    //设置数据源
    public static void setDataSource(String datasource) {
        dataSources.set(datasource);
    }
    //获取数据源
    public static String getDataSource() {
        return dataSources.get();
    }

    //清除数据源
    public static void clearDataSource() {
        dataSources.remove();
    }
}

如何构造一个动态数据源?

上文说过只需继承一个抽象类AbstractRoutingDataSource,重写其中的一个方法determineCurrentLookupKey()即可。

代码如下:


public class DynamicDataSource extends AbstractRoutingDataSource {

    
    @Override
    protected Object determineCurrentLookupKey() {
        //从ThreadLocal中取出KEY
        return DataSourceHolder.getDataSource();
    }

    
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        //默认的数据源,可以作为主数据源
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        //目标数据源
        super.setTargetDataSources(targetDataSources);
        //执行afterPropertiesSet方法,完成属性的设置
        super.afterPropertiesSet();
    }
}

上述代码很简单,分析如下:

  • 一个多参的构造方法,指定了默认的数据源和目标数据源。
  • 重写determineCurrentLookupKey()方法,返回数据源对应的KEY,这里是直接从ThreadLocal中取值,就是上文封装的DataSourceHolder。

定义一个注解

为了操作方便且低耦合,不能每次需要切换的数据源的时候都要手动调一下接口吧,可以定义一个切换数据源的注解,如下:


@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface SwitchSource {
    
    String DEFAULT_NAME = "hisDataSource";

    
    String value() default DEFAULT_NAME;
}

注解中只有一个value属性,指定了需要切换数据源的KEY。

有注解还不行,当然还要有切面,代码如下:

@Aspect
//优先级要设置在事务切面执行之前
@Order(1)
@Component
@Slf4j
public class DataSourceAspect {
    @Pointcut("@annotation(SwitchSource)")
    public void pointcut() {
    }
    
    @Before(value = "pointcut()")
    public void beforeOpt(JoinPoint joinPoint) {
        
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        SwitchSource switchSource = method.getAnnotation(SwitchSource.class);
        log.info("[Switch DataSource]:" + switchSource.value());
        DataSourceHolder.setDataSource(switchSource.value());
    }

    
    @After(value = "pointcut()")
    public void afterOpt() {
        DataSourceHolder.clearDataSource();
        log.info("[Switch Default DataSource]");
    }
}

这个ASPECT很容易理解,beforeOpt()在方法之前执行,取值@SwitchSource中value属性设置到ThreadLocal中;afterOpt()方法在方法执行之后执行,清除掉ThreadLocal中的KEY,保证了如果不切换数据源,则用默认的数据源。

如何与Mybatis整合?

单一数据源与Mybatis整合上文已经详细讲解了,数据源DataSource作为参数构建了SqlSessionFactory,同样的思想,只需要把这个数据源换成动态数据源即可。注入的代码如下:


    @Primary
    @Bean("sqlSessionFactory2")
    public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper*.xml"));
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setDefaultFetchSize(100);
        configuration.setDefaultStatementTimeout(30);
        sqlSessionFactoryBean.setConfiguration(configuration);
        return sqlSessionFactoryBean.getObject();
    }

与Mybatis整合很简单,只需要把数据源替换成自定义的动态数据源DynamicDataSource。

”那么动态数据源如何注入到IOC容器中呢?看上文自定义的DynamicDataSource构造方法,肯定需要两个数据源了,因此必须先注入两个或者多个数据源到IOC容器中,如下:


    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean("dataSource")
    public DataSource dataSource(){
        return new DruidDataSource();
    }

    
    @Bean(name = SwitchSource.DEFAULT_NAME)
    @ConfigurationProperties(prefix = "spring.datasource.his")
    public DataSource hisDataSource() {
        return DataSourceBuilder.create().build();
    }

以上构建的两个数据源,一个是默认的数据源,一个是需要切换到的数据源(targetDataSources),这样就组成了动态数据源了。数据源的一些信息,比如url,username需要自己在全局配置文件中根据指定的前缀配置即可,代码不再贴出。

动态数据源的注入代码如下:


    @Primary
    @Bean("sqlSessionFactory2")
    public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDataSource);
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setDefaultFetchSize(100);
        configuration.setDefaultStatementTimeout(30);
        sqlSessionFactoryBean.setConfiguration(configuration);
        return sqlSessionFactoryBean.getObject();
    }

这里还有一个问题:IOC中存在多个数据源了,那么事务管理器怎么办呢?它也懵逼了,到底选择哪个数据源呢?因此事务管理器肯定还是要重新配置的。

”事务管理器此时管理的数据源将是动态数据源DynamicDataSource,配置如下:


    @Primary
    @Bean(value = "transactionManager2")
    public PlatfORMTransactionManager annotationDrivenTransactionManager(DynamicDataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

至此,Mybatis与多数据源的整合就完成了。

演示

使用也是很简单,在需要切换数据源的方法上方标注@SwitchSource切换到指定的数据源即可,如下:

//不开启事务
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    //切换到HIS的数据源
    @SwitchSource
    @Override
    public List<DeptInfo> list() {
        return hisDeptInfoMapper.listDept();
    }

这样只要执行到这方法将会切换到HIS的数据源,方法执行结束之后将会清除,执行默认的数据源。

总结

文章讲了Spring Boot与单数据源、Mybatis、多数据源之间的整合,希望这篇文章能够帮助读者理解多数据源的整合,虽说用的不多,但是在有些领域仍然是比较重要的。

到此这篇关于Spring Boot 优雅整合多数据源的文章就介绍到这了,更多相关Spring Boot 整合数据源内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Spring Boot 优雅整合多数据源

本文链接: https://www.lsjlt.com/news/149624.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Spring Boot 优雅整合多数据源
    目录何时用到多数据源整合单一的数据源整合Mybatis多数据源如何整合什么是动态数据源数据源切换如何保证线程隔离如何构造一个动态数据源定义一个注解如何与Mybatis整合演示总结前言...
    99+
    2022-11-13
  • Spring Boot怎么整合多数据源
    本篇内容主要讲解“Spring Boot怎么整合多数据源”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring Boot怎么整合多数据源”吧!前言:什么是多数据源最常见的...
    99+
    2023-06-30
  • 详解Spring Boot整合Mybatis实现 Druid多数据源配置
    一、多数据源的应用场景目前,业界流行的数据操作框架是 Mybatis,那 Druid 是什么呢?Druid 是 Java 的数据库连接池组件。Druid 能够提供强大的监控和扩展功能。比如可以监控 SQL ,在监控业务可以查询慢查询 SQL...
    99+
    2023-05-31
    spring 多数据源 spring boo
  • spring boot整合MySQL数据库
    spring boot整合MySQL数据库 spring boot整合MySQL数据库可以说很简单,只需要添加MySQL依赖和在配置文件中添加数据库配置信息,利用spring-boot-starter...
    99+
    2023-08-31
    数据库 mysql spring boot
  • spring boot只需两步优雅整合activiti示例解析
    目录前言spring boot整合activiti只需两步1.添加boot的activiti模块2.添加你的流程图文件增强你的activiti,发布restful管理activiti...
    99+
    2022-11-13
  • Spring Boot整合 NoSQL 数据库 Redis详解
    目录引言一、环境准备二、构建Spring Boot项目三、引入Redis依赖四、Reds相关配置五、添加Redis配置类六、测试一下引言 在日常的开发中,除了使用 Spring Bo...
    99+
    2022-11-13
  • Spring boot中如何配置Mybatis多数据源
    本篇文章为大家展示了Spring boot中如何配置Mybatis多数据源,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。环境准备实验环境:JDK 1.8Spring...
    99+
    2022-10-18
  • Spring Boot整合阿里开源中间件Canal实现数据增量同步
    目录前言Canal是什么?Canal数据如何传输?数据同步还有其他中间件吗?Canal服务端安装1、打开MySQL的binlog日志2、设置MySQL的配置3、设置RabbitMQ的...
    99+
    2022-11-13
  • Spring Boot配置多数据源的四种方式
    1、导读 在日常开发中我们都是以单个数据库进行开发,在小型项目中是完全能够满足需求的。 但是,当我们牵扯到像淘宝、京东这样的大型项目的时候,单个数据库就难以承受用户的CRUD操作。 那么此时,我们就需要使用多个数据源进行读写分离的操作,这种...
    99+
    2023-08-20
    spring boot java 数据库 mybatis 后端
  • Spring Boot 2.7.5 HikariCP 连接池多数据源配置
    一. 引言 当前项目遇到需要连接多个数据库的场景,此时需要引入多数据源了. 还有一些诸如以下的场景: 与第三方对接时,有些合作方并不会为了你的某些需求而给你开发一个功能,他们可以提供给你一个可以访...
    99+
    2023-08-31
    spring boot mybatis java
  • Spring Boot 整合JPA 数据模型关联使用操作(一对一、一对多、多对多)
    目录表关联一对一一对多和多对一多对多表关联 上一篇介绍了JPA的简单使用,这一篇介绍JPA在表关联上的使用 一对一 配置参数JPA对于数据实体一对一映射使用的是@OneToOne注解...
    99+
    2022-11-13
  • springboot整合druid及多数据源配置
    前言 本篇主要分两部分 ①springboot整合druid的代码配置,以及druid的监控页面演示;②对实际场景中多数据源的配置使用进行讲解。 一、springboot整合druid的演示demo 可以用idea快速生成一个可运行的dem...
    99+
    2023-10-24
    spring boot java spring
  • javaweb怎么整合多数据源搜索
    要实现JavaWeb中多数据源的搜索,可以按照以下步骤进行整合:1. 配置多个数据源:在application.properties...
    99+
    2023-09-21
    javaweb
  • springboot整合多数据源配置方式
    目录简介一、表结构二、多数据源整合1. springboot+mybatis使用分包方式整合1.1 主要依赖包1.2 application.yml 配置文件1.3 建立连接数据源的...
    99+
    2022-11-12
  • springboot整合mybatis-plus、clickhouse、mysql多数据源
    springboot的多数据源有多种方式,本文按照指定不同dao/mapper.xml的方式来实现访问不同的数据源。这样的好处是不用注解去切换数据源。 1、引入驱动 com.alibaba...
    99+
    2023-09-05
    mybatis spring boot mysql clickhouse
  • Spring整合多数据源实现动态切换的实例讲解
    在实际项目中时常需要连接多个数据库,而且不同的业务需求在实现过程当中往往需要访问不同的数据库。jdbc.properties配置文件,配置多个dataSource##########################MySQL########...
    99+
    2023-05-31
    多数据源 动态切换 spring
  • Spring Boot多数据源处理事务实例分析
    这篇“Spring Boot多数据源处理事务实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Spring&n...
    99+
    2023-06-30
  • 解决spring boot 1.5.4 配置多数据源的问题
    spring boot 已经支持多数据源配置了,无需网上好多那些编写什么类的,特别麻烦,看看如下解决方案,官方的,放心!1.首先定义数据源配置#=====================multiple database config===...
    99+
    2023-05-31
    springboot 多数据源 配置
  • 多数据源怎么利用spring boot进行配置
    本篇文章给大家分享的是有关多数据源怎么利用spring boot进行配置,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。多数据源配置创建一个Spring配置类,定义两个DataS...
    99+
    2023-05-31
    springboot 多数据源
  • 详解基于Spring Boot与Spring Data JPA的多数据源配置
    由于项目需要,最近研究了一下基于spring Boot与Spring Data JPA的多数据源配置问题。以下是传统的单数据源配置代码。这里使用的是Spring的Annotation在代码内部直接配置的方式,没有使用任何XML文件。@Con...
    99+
    2023-05-31
    spring boot 多数据源
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作