【原】通过AOP实现MyBatis多数据源的动态切换
【环境参数】
1、开发框架:Spring + SpringMVC + MyBatis
2、数据库A的URL:jdbc.url=jdbc:mysql://172.16.17.164:3306/ test?characterEncoding=UTF-8&useUnicode=TRUE&autoReconnect=true&failOverReadOnly=false
3、数据库B的URL:bakdb.jdbc.url=jdbc:mysql://172.16.17.68:3306/bakDB?characterEncoding=UTF-8&useUnicode=TRUE&autoReconnect=true&failOverReadOnly=false
【需求描述】
(1)当用户调用X方法“之前”,系统会首先切换当前数据源为A数据源(bakDb数据库),之后再去调用方法X。
(2)当用户调用Y方法“之前”,系统会首先切换当前的数据源为B数据源(testDb数据库),之后再去调用方法Y。
(3)X方法和Y方法所在的包名
X方法:该方法位于com.zjrodger.bakdata.service包下其子包下。
Y方法:该方法位于com.zjrodger.datatobank.service或者com.zjrodger.zxtobank.service包及其子包下。
【具体步骤】
1、编写动态数据源相关代码。
(1) 编写DynamicDataSource类。
DynamicDataSource的主要作用是以Map<Object, Object>的形式,来存储多个数据源。
因为该类继承了父类AbstractRoutingDataSource,在父类中,多数据源的实例是被存放在一个名为“targetDataSource”的Map类型的成员变量中。
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDbType();
}
}
DynamicDataSource
(2) 编写DatabaseContextHolder类。
public class DatabaseContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setDbType(String dataSourceType) {
contextHolder.set(dataSourceType);
} public static String getDbType() {
return contextHolder.get();
} public static void clearDbType() {
contextHolder.remove();
}
}
DatabaseContextHolder
2、编写切换数据源的拦截器。
public class DataSourceInterceptor { /** 数据源切换常量 */
public static final String DATASOURCE_TEST_DB="dataSourceKey4TestDb";
public static final String DATASOURCE_BAK_DB="dataSourceKey4BakDb"; /**
* 设置数据源为test数据库所对应的数据源。
* @param jp
*/
public void setdataSourceTestDb(JoinPoint jp) {
DatabaseContextHolder.setDbType(DATASOURCE_TEST_DB);
} /**
* 设置数据源为bak数据库所对应的数据源。
* @param jp
*/
public void setdataSourceBakDb(JoinPoint jp) {
DatabaseContextHolder.setDbType(DATASOURCE_BAK_DB);
}
}
DataSourceInterceptor
3、在Spring配置文件中进行相关配置。
(1)配置两个数据源
A.第一个数据源:
<bean id="c3p0DataSource4BakDb" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close" depends-on="propertyConfigurer">
<property name="driverClass" value="${bakdb.jdbc.driverclass}" />
<property name="jdbcUrl" value="${bakdb.jdbc.url}" />
<property name="user" value="${bakdb.jdbc.username}" />
<property name="password" value="${bakdb.jdbc.password}" /> <!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize" value="10" />
<!-- 连接池中保留的最小连接数。 -->
<property name="minPoolSize" value="5" />
<!-- 连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="100" />
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="5" />
<!-- 最大空闲时间,10秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="10" />
<!-- JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 -->
<property name="maxStatements" value="0" />
<!-- 连接池用完时客户调用getConnection()后等待获取连接的时间,单位:毫秒。超时后会抛出 SQLEXCEPTION,如果设置0,则无限等待。Default:0 -->
<property name="checkoutTimeout" value="30000" />
</bean>
BakDb数据库的数据源
B.第二个数据源:
<bean id="c3p0DataSource4TestDb" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close" depends-on="propertyConfigurer">
<property name="driverClass" value="${jdbc.driverclass}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" /> <!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize" value="10" />
<!-- 连接池中保留的最小连接数。 -->
<property name="minPoolSize" value="5" />
<!-- 连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="100" />
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="5" />
<!-- 最大空闲时间,10秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="10" />
<!-- JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 -->
<property name="maxStatements" value="0" />
<!-- 连接池用完时客户调用getConnection()后等待获取连接的时间,单位:毫秒。超时后会抛出 SQLEXCEPTION,如果设置0,则无限等待。Default:0 -->
<property name="checkoutTimeout" value="30000" />
</bean>
TestDb数据库的数据源
(2)两个数据源所对应的properties属性文件
# =========== Test数据库相关信息 ============
jdbc.url=jdbc:mysql://172.16.17.164:3306/ test?characterEncoding=UTF-8&useUnicode=TRUE&autoReconnect=true&failOverReadOnly=false
jdbc.username=root
jdbc.password=123456
jdbc.driverclass=com.mysql.jdbc.Driver
jdbc.ip=172.16.5.64
jdbc.dbname=test # =========== BakDB数据库相关信息 ============
bakdb.jdbc.url=jdbc:mysql://172.16.17.68:3306/bakDB?characterEncoding=UTF-8&useUnicode=TRUE&autoReconnect=true&failOverReadOnly=false
bakdb.jdbc.username=root
bakdb.jdbc.password=123456
bakdb.jdbc.driverclass=com.mysql.jdbc.Driver
bakdb.jdbc.ip=172.16.17.68
bakdb.jdbc.dbname=bakDB
两个数据库对应的属性文件
(3)配置DynamicDataSource这个Bean(关键)。
该DynamicDataSource的主要作用是以Map<Object, Object>的形式,来存储多个数据源。
<!-- 配置可以存储多个数据源的Bean -->
<bean id="dataSource" class="com.beebank.pub.datasource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="dataSourceKey4TestDb" value-ref="c3p0DataSource4TestDb" />
<entry key="dataSourceKey4BakDb" value-ref="c3p0DataSource4BakDb" />
</map>
</property>
<property name="defaultTargetDataSource" ref="c3p0DataSource4HuihangDb" />
</bean>
配置dataSource这个Bean
(4)配置DataSourceInterceptor这个Bean(关键)。
<!-- 配置切换数据源Key的拦截器 -->
<bean id="dataSourceInterceptor" class="com.zjrodger.pub.datasource.DataSourceInterceptor"></bean>
Bean—dataSourceInterceptor
(5)利用AOP,配置控制数据源在特定条件下切换的切面(关键,重要)。
注意要添加aop名字空间。
<!-- 1.配置Spring框架自身提供的切面类 -->
<tx:advice id="userTxAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="delete*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" no-rollback-for="java.lang.RuntimeException" />
<tx:method name="insert*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="update*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="find*" propagation="SUPPORTS" />
<tx:method name="get*" propagation="SUPPORTS" />
<tx:method name="select*" propagation="SUPPORTS" />
</tx:attributes>
</tx:advice> <!-- 2.配置用户自定义的切面,用于切换数据源Key -->
<bean id="dataSourceInterceptor" class="com.zjrodger.pub.datasource.DataSourceInterceptor"></bean> <!-- 3.(重要)配置Spring事务切面和自定义切面类,动态切换数据源,注意两切面的执行顺序 -->
<aop:config>
<!-- (1) Spring框架自身提供的切面 -->
<aop:advisor advice-ref="userTxAdvice" pointcut="execution(public * com.zjrodger.*.service..*.*(..))" order="2"/> <!-- (2) 用户自定义的切面,根据切入点,动态切换数据源。 -->
<aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor" order="1">
<aop:before method="setdataSourceBakDb" pointcut="execution(* com.zjrodger.bakdata.service..*.*(..))"/>
<aop:before method="setdataSourceTestDb" pointcut="execution(* com.zjrodger.datatobank.service..*.*(..))"/>
</aop:aspect>
</aop:config>
(重要)配置Spring事务切面和自定义切面类,动态切换数据源,注意两切面的执行顺序
注意:
A.注意上述两个切面中的order属性的配置。
B.自定义切面类和Spring自带事务切面类(即<aop:advisor>元素)的执行的先后顺序要配置正确,否则就会导致导致数据源不能动态切换。
在AOP中,当执行同一个切入点时,不同切面的执行先后顺序是由“每个切面的order属性”而定的,order越小,则该该切面中的通知越先被执行。
上述<aop:config>元素中,引用了两个切面类:“userTxAdvice类”和“dataSourceAspect类”,其中<aop:advisor>是Spring框架自定义的切面标签。
根据两个切面类order属性的定义,当程序执行时并且触发切入点后(即调用com.zjrodger.bakdata.service包及其子包下的方法),dataSourceAspect切面类中的setdatasourceBakDb()方通知法首先执行,之后才会执行userTxAdvice事务类中的相关通知方。
说明
切面类“DataSourceInterceptor”中有两个方法:setdataSourceTestDb()方法和setdataSourceBakDb()。
1)当用户调用“com.zjrodger.bakdata.service”包及其子包下的方法X“之前”,系统会首先去调用setdataSourceBakDb()方法,设置当前数据源为bakDb的数据源,之后再去调用方法X。
2)当用户调用“com.zjrodger.datatobank.service”或者“com.zjrodger.zxtobank.service”包及其子包下的方法Y之前,系统会首先去调调用setdataSourceTestDb()方法,设置当前的数据源为testDb数据库的数据源,之后再去调用方法Y。
(6)完整的Spring配置文档
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 该配置为自动扫描配置的包下所有使用@Controller注解的类 -->
<context:component-scan base-package="com.zjrodger" /> <bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:properties/dbconfig.properties</value>
</property>
<property name="fileEncoding" value="utf-8" />
</bean> <!-- 备份库数据库数据源 -->
<bean id="c3p0DataSource4BakDb" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close" depends-on="propertyConfigurer">
<property name="driverClass" value="${bakdb.jdbc.driverclass}" />
<property name="jdbcUrl" value="${bakdb.jdbc.url}" />
<property name="user" value="${bakdb.jdbc.username}" />
<property name="password" value="${bakdb.jdbc.password}" /> <!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize" value="10" />
<!-- 连接池中保留的最小连接数。 -->
<property name="minPoolSize" value="5" />
<!-- 连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="100" />
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="5" />
<!-- 最大空闲时间,10秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="10" />
<!-- JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 -->
<property name="maxStatements" value="0" />
<!-- 连接池用完时客户调用getConnection()后等待获取连接的时间,单位:毫秒。超时后会抛出 SQLEXCEPTION,如果设置0,则无限等待。Default:0 -->
<property name="checkoutTimeout" value="30000" />
</bean> <!--Test数据库数据源 -->
<bean id="c3p0DataSource4TestDb" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close" depends-on="propertyConfigurer">
<property name="driverClass" value="${jdbc.driverclass}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" /> <!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize" value="10" />
<!-- 连接池中保留的最小连接数。 -->
<property name="minPoolSize" value="5" />
<!-- 连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="100" />
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="5" />
<!-- 最大空闲时间,10秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="10" />
<!-- JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 -->
<property name="maxStatements" value="0" />
<!-- 连接池用完时客户调用getConnection()后等待获取连接的时间,单位:毫秒。超时后会抛出 SQLEXCEPTION,如果设置0,则无限等待。Default:0 -->
<property name="checkoutTimeout" value="30000" />
</bean> <!-- 配置可以存储多个数据源的Bean -->
<bean id="dataSource" class="com.zjrodger.pub.datasource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="dataSourceKey4TestDb" value-ref="c3p0DataSource4TestDb" />
<entry key="dataSourceKey4BakDb" value-ref="c3p0DataSource4BakDb" />
</map>
</property>
<property name="defaultTargetDataSource" ref="c3p0DataSource4TestDb" />
</bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- <property name="dataSource" ref="c3p0DataSource" /> -->
<property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath*:com/zjrodger/**/dao/xml/*.xml" />
<!-- 添加分页插件 -->
<property name="plugins">
<list>
<bean class="com.github.pagehelper.PageHelper">
<property name="properties">
<props>
<prop key="dialect">mysql</prop>
<prop key="offsetAsPageNum">true</prop>
<prop key="rowBoundsWithCount">true</prop>
<prop key="pageSizeZero">true</prop>
<prop key="reasonable">true</prop>
</props>
</property>
</bean>
</list>
</property> </bean> <bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- <property name="dataSource" ref="c3p0DataSource" /> -->
<property name="dataSource" ref="dataSource" />
</bean> <!-- 注解驱动,使spring的controller全部生效 -->
<mvc:annotation-driven />
<!-- 注解驱动,是spring的task全部生效 -->
<task:annotation-driven /> <aop:aspectj-autoproxy expose-proxy="true" />
<tx:annotation-driven transaction-manager="transactionManager"/> <!-- Spring声明式事务切面 -->
<tx:advice id="userTxAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="delete*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" no-rollback-for="java.lang.RuntimeException" />
<tx:method name="insert*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="update*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="find*" propagation="SUPPORTS" />
<tx:method name="get*" propagation="SUPPORTS" />
<tx:method name="select*" propagation="SUPPORTS" />
</tx:attributes>
</tx:advice> <aop:config>
<!-- Spring框架自身提供的切面 -->
<aop:advisor advice-ref="userTxAdvice" pointcut="execution(public * com.zjrodger.*.service..*.*(..))" order="2"/> <!-- 用户自定义的切面,根据切入点,动态切换数据源。 -->
<aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor" order="1">
<aop:before method="setdataSourceBakDb" pointcut="execution(* com.zjrodger.bakdata.service..*.*(..))"/>
<aop:before method="setdataSourceTestDb" pointcut="execution(* com.zjrodger.datatobank.service..*.*(..))"/>
<aop:before method="setdataSourceTestDb" pointcut="execution(* com.zjrodger.zxtobank.service..*.*(..))"/>
</aop:aspect>
</aop:config> <!-- 配置切换数据源Key的拦截器 -->
<bean id="dataSourceInterceptor" class="com.zjrodger.pub.datasource.DataSourceInterceptor"></bean> <!-- mybatis配置 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zjrodger.pub.dao,com.zjrodger.zxtobank.dao,com.zjrodger.bakdata.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
</beans>
applicationContext.xml
至此,MyBatis多数据源的配置完毕,之后在自己的环境下进行测试,结果测试通过。
要特别注意自定义AOP切面与Spring自带的事务切面的执行顺序,即注意<aop:config>中的配置部分,否则,很容易会出现动态切换数据源失败的现象。
【同专题博客内连接】
1、Order属性决定了不同切面类中通知执行的先后顺序
http://www.cnblogs.com/zjrodger/p/5633922.html
2、不定义Order属性,通过切面类的定义顺序来决定通知执行的先后顺序
http://www.cnblogs.com/zjrodger/p/5633951.html
【其他参考连接】
1、《Spring中事务与aop的先后顺序问题》http://my.oschina.net/HuifengWang/blog/304188
【原】通过AOP实现MyBatis多数据源的动态切换的更多相关文章
- spring+mybatis多数据源,动态切换
有时我们项目中需要配置多个数据源,不同的业务使用的数据库不同 实现思路:配置多个dataSource ,再配置多个sqlSessionFactory,和dataSource一一对应.重写SqlSess ...
- Spring多数据源的动态切换
Spring多数据源的动态切换 目前很多项目中只能配置单个数据源,那么如果有多个数据源肿么办?Spring提供了一个抽象类AbstractRoutingDataSource,为我们很方便的解决了这个问 ...
- Spring简单实现数据源的动态切换
Spring简单实现数据源的动态切换: 1. 创建一个数据源切换类: 2. 继承AbstractRoutingDataSource,创建多数据源路由类,并注入到spring的配置文件中: 3. AOP ...
- Spring配置多个数据源,并实现数据源的动态切换转载)
1.首先在config.properties文件中配置两个数据库连接的基本数据.这个省略了 2.在spring配置文件中配置这两个数据源: 数据源1 <!-- initialSize初始化时建立 ...
- springboot mybatis 多数据源配置支持切换以及一些坑
一 添加每个数据源的config配置,单个直接默认,多个需要显示写出来 @Configuration @MapperScan(basePackages ="com.zhuzher.*.map ...
- SpringBoot整合Mybatis多数据源 (AOP+注解)
SpringBoot整合Mybatis多数据源 (AOP+注解) 1.pom.xml文件(开发用的JDK 10) <?xml version="1.0" encoding=& ...
- springboot 双数据源+aop动态切换
# springboot-double-dataspringboot-double-data 应用场景 项目需要同时连接两个不同的数据库A, B,并且它们都为主从架构,一台写库,多台读库. 多数据源 ...
- springboot+mybatis实现动态切换数据源
前几天有个需求,需要使用不同的数据源,例如某业务要用A数据源,另一个业务要用B数据源.我上网收集了一些资料整合了一下,虽然最后这个需求不了了之了,但是多数据源动态切换还是蛮好用的,所以记录一下,或许以 ...
- mybatis 多数据源动态切换
笔者主要从事c#开发,近期因为项目需要,搭建了一套spring-cloud微服务框架,集成了eureka服务注册中心. gateway网关过滤.admin服务监控.auth授权体系验证,集成了redi ...
随机推荐
- ios线程和GCD和队列同步异步的关系
1.什么是进程? 进程是指在系统中正在运行的一个应用程序.比如同时打开QQ.Xcode,系统就会分别启动2个进程.截图 2.什么是线程? 1).一个进程要想执行任务,必须得有线程(每一个进程至少要有一 ...
- python全栈开发day65-templates:tags、母版和继承、组件、静态文件相关、simple_tag和inclusion_tag
一.昨日内容回顾 1.MVC和MTV框架 MVC: model 模型 存写数据 view 视图 给用户展示页面 control 控制器 负责调度 传递指令 MTV: M:model 模型 OR ...
- c_数据结构_栈的实现
#include<stdio.h> #include<stdlib.h> #define STACK_INIT_SIZE 100 #define STACKINCREMENT ...
- day8.登陆注册简单实现
username = input('请输入注册的用户名:') password = input('请输入注册名的密码:') with open('list_of_info',mode='w',enco ...
- seafile+glusterfs 安装部署
今天在虚拟机上搭一下seafile,用于测试环境.此处安装的是社区免费版本的,可以使用一键自动安装(MySQL适用). 官方文档:https://manual-cn.seafile.com/ 1.一键 ...
- awk介绍
awk 是一个强大的文本处理工具,它将文本逐行读入,并进行切片,默认以空白格为分割符,对单个切片进行分析,处理. 用法: awk '{pattern + action}' {filenames} 尽管 ...
- axios中文文档
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中. Features 从浏览器中创建 XMLHttpRequests 从 node.js 创建 http ...
- HDU3488 Tour KM
原文链接http://www.cnblogs.com/zhouzhendong/p/8284304.html 题目传送门 - HDU3488 题意概括 给一个n的点m条边的有向图. 然后让你把这个图分 ...
- SQL注入——SQL Injection
本文部分内容转自:https://www.cnblogs.com/rush/archive/2011/12/31/2309203.html http://www.diybl.com/course/7_ ...
- 20165235 祁瑛 2018-4 《Java程序设计》第九周学习总结
20165235 祁瑛 2018-4 <Java程序设计>第九周学习总结 教材学习内容总结 URL类 UR类是java.net包中的一个重要类,使用URL创建的对象的应用程序称作称作客户端 ...