Vue2响应式与依赖收集原理

响应式

Vue2的响应式依赖ES5Object.defineProperty,主要使用这个API改写对象的GetterSetter,用来依赖收集与触发更新

依赖收集原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

// 工具函数用来判断obj是不是一个object
function isObject (obj) {
return typeof obj === 'object'
&& !Array.isArray(obj)
&& obj !== null
&& obj !== undefined
}

// `observe`函数用来把一个普通的对象转为响应式对象
function observe (obj) {
if (!isObject(obj)) {
throw new TypeError()
}

Object.keys(obj).forEach(key => {
let internalValue = obj[key]
// 每一个属性都有一个独立的依赖收集器
let dep = new Dep()
Object.defineProperty(obj, key, {
get () {
// 这里用来收集依赖
dep.depend()
return internalValue
},
set (newValue) {
const isChanged = internalValue !== newValue
if (isChanged) {
internalValue = newValue
// 如果修改了值,这里用来通知修改
dep.notify()
}
}
})
})
}

window.Dep = class Dep {
constructor () {
this.subscribers = new Set()
}

depend () {
if (activeUpdate) {
// register the current active update as a subscriber
this.subscribers.add(activeUpdate)
}
}

notify () {
// run all subscriber functions
this.subscribers.forEach(subscriber => subscriber())
}
}

// activeUpdate是一个运行函数
let activeUpdate

// 这只是依赖收集的简单示例,示例代码还是存在不少问题的
function autorun (update) {
// 函数wrappedUpdate这里使用闭包,保存了update方法
function wrappedUpdate () {
activeUpdate = wrappedUpdate
update()
activeUpdate = null
}
wrappedUpdate()
}

Maven生命周期与插件

maven生命周期

Maven拥有三套相互独立的生命周期,它们分别为clean、default和site。clean生命周期的目的是清理项目,default生命周期的目的是构建项目,而site生命周期的目的是建立项目站点
每个生命周期下面有不同的阶段

clean生命周期

clean生命周期的目的是清理项目,它包含三个阶段:

  • pre-clean执行一些清理前需要完成的工作。
  • clean清理上一次构建生成的文件。
  • post-clean执行一些清理后需要完成的工作。

default生命周期

  • validate
  • initialize
  • generate-sources
  • process-sources处理项目主资源文件。一般来说,是对src/main/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中。
  • generate-resources
  • process-resources
  • compile编译项目的主源码。一般来说,是编译src/main/java目录下的Java文件至项目输出的主classpath目录中。
  • process-classes
  • generate-test-sources
  • process-test-sources处理项目测试资源文件。一般来说,是对src/test/resources目录的内容进行变量替换等工作后,复制到项目输出的测试classpath目录中。
  • generate-test-resources
  • process-test-resources
  • test-compile编译项目的测试代码。一般来说,是编译src/test/java目录下的Java文件至项目输出的测试classpath目录中。
  • process-test-classes
  • test使用单元测试框架运行测试,测试代码不会被打包或部署。
  • prepare-package
  • package接受编译好的代码,打包成可发布的格式,如JAR。
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify
  • install将包安装到Maven本地仓库,供本地其他Maven项目使用。
  • deploy将最终的包复制到远程仓库,供其他开发人员和Maven项目使用。

site生命周期

site生命周期的目的是建立和发布项目站点,Maven能够基于POM所包含的信息,自动生成一个友好的站点,方便团队交流和发布项目信息。该生命周期包含如下阶段:

  • pre-site执行一些在生成项目站点之前需要完成的工作。
  • site生成项目站点文档。
  • post-site执行一些在生成项目站点之后需要完成的工作。
  • site-deploy将生成的项目站点发布到服务器上。

插件与指令绑定

插件远程仓库

插件的远程仓库使用pluginRepositories和pluginRepository配置,
在POM中配置插件的时候,如果该插件是Maven的官方插件(即如果其groupId为org.apache.maven.plugins),就可以省略groupId配置

maven插件版本

Maven在超级POM中为所有核心插件设定了版本,超级POM是所有Maven项目的父POM,所有项目都继承这个超级POM的配置,因此,即使用户不加任何配置,Maven使用核心插件的时候,它们的版本就已经确定了

解析插件前缀

通过配置settings.xml让Maven检查其他groupId上的插件仓库元数据

1
2
3
4
5
<settings>
<pluginGroups>
<pluginGroup>com.your.plugins</pluginGroup>
</pluginGroups>
</settings>

指令绑定

用户通过命令行调用生命周期阶段的时候,对应的插件目标就会执行相应的任务

自定义绑定

用户还能够自己选择将某个插件目标绑定到生命周期的某个阶段上
有很多插件的目标在编写时已经定义了默认绑定阶段
当多个插件目标绑定到同一个阶段的时候,这些插件声明的先后顺序决定了目标的执行顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<!-- 每个execution子元素可以用来配置执行一个任务 -->
<execution>
<id>attach-sources</id>
<!-- phrase配置,将其绑定到verify生命周期阶段 -->
<phase>verify</phase>
<goals>
<!-- 通过goals配置指定要执行的插件目标 -->
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
1
2
3
4
5
6
7
# 使用maven-help-plugin查看插件详细信息,了解插件目标的默认绑定阶段
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin
# 其他命令
mvn help:describe -Dcmd=install
mvn help:describe -Dcmd=help:describe
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-help-plugin
mvn help:describe -DgroupId=org.apache.maven.plugins -DartifactId=maven-help-plugin

参考

1.《Maven实战》

Maven远程仓库配置与依赖

不常用的命令

  • mvn help
1
2
# 显示配置信息
mvn help:system
  • mvn dependency
1
2
3
4
5
# 显示依赖
mvn dependency:tree
mvn dependency:list
# 依赖分析
mvn dependency:analyse

配置文件区别

  • $M2_HOME/conf/settings.xml是全局配置
  • ~/.m2/settings.xml是用户配置

远程仓库配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<project>
<repositories>
<repository>
<id>jboss</id>
<name>JBoss Repository</name>
<url>http://repository.jboss.com/maven2/</url>
<releases>
<!-- 发布版本下载支持 -->
<enabled>true</enabled>
<!-- 远程仓库检查更新的频率,never—从不检查更新;always—每次构建都检查更新;interval:X—每隔X分钟检查一次更新(X为任意整数);daily,表示Maven每天检查一次 -->
<updatePolicy>daily</updatePolicy>
<!-- Maven检查检验和文件的策略,warn时,Maven会在执行构建时输出警告信息;fail—Maven遇到校验和错误就让构建失败;ignore—使Maven完全忽略校验和错误 -->
<checksumPolicy>ignore</checksumPolicy>
</releases>
<snapshots>
<!-- 快照版本的下载支持 -->
<enabled>false</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
<layout>default</layout>
</repository>
</repositories>
</project>

远程仓库认证

1
2
3
4
5
6
7
8
9
<settings>
<servers>
<server>
<id>my-proj</id>
<username>repo-user</username>
<password>repo-pwd</password>
</server>
</servers>
</settings>

settings.xml中server元素的id必须与POM中需要认证的repository元素的id完全一致,正是这个id将认证信息与仓库配置联系在了一起

部署至远程仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<project>
<distributionManagement>
<repository>
<id>proj-releases</id>
<name>Proj Release Repository</name>
<url>http://192.168.1.100/content/repositories/proj-releases</url>
</repository>
<snapshotRepository>
<id>proj-snapshots</id>
<name>Proj Snapshot Repository</name>
<url>http://192.168.1.100/content/repositories/proj-snapshots</url>
</snapshotRepository>
</distributionManagement>
</project>

distributionManagement:
repository和snapshotRepository子元素,前者表示发布版本构件的仓库,后者表示快照版本的仓库。关于发布版本和快照版本

maven镜像

1
2
3
4
5
6
7
8
9
10
<settings>
<mirrors>
<mirror>
<id>maven.net.cn</id>
<name>one of the central mirrors in China</name>
<url>http://maven.net.cn/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>
  • <mirrorOf>的值为central,表示该配置为中央仓库的镜像,任何对于中央仓库的请求都会转至该镜像
  • <mirrorOf>*</mirrorOf>:匹配所有远程仓库。
  • <mirrorOf>external:*</mirrorOf>:匹配所有远程仓库,使用localhost的除外,使用file://协议的除外。也就是说,匹配所有不在本机上的远程仓库。
  • <mirrorOf>repo1,repo2</mirrorOf>:匹配仓库repo1和repo2,使用逗号分隔多个远程仓库。
  • <mirrorOf>*,!repo1</mirrorOf>:匹配所有远程仓库,repo1除外,使用感叹号将仓库从匹配中排除

依赖范围

  • compile: 默认,对于编译,运行,测试三种classpath都有效
    例如:spring-core,在程序编译,运行,测试都有效果

  • test: 编译测试代码和运行测试代码才需要
    例如: junit

  • provided: 以提供依赖,对于编译,测试有效,运行无效
    例如: sevelet-api

  • runtime: 运行时依赖,运行测试有效,编译无效
    例如: jdbc驱动程序
    runtime不会造成依赖继承,

    Setting dependency to runtime ensure that there isn’t an accidental dependency on the code, and also keeps the dependency from being transitive. So that, for example, if module A has a runtime dependency on library X, and module B depends on module A, it does not inherit the dependency on library X. Using “provided” or “compile” would cause B to depend on X.

  • system: 与provided一致,但需使用systemPath指定引用包路径

  • import: 导入依赖范围

传递性依赖和依赖范围

假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖

  • 当第二直接依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致;
  • 当第二直接依赖的范围是test的时候,依赖不会得以传递;
  • 当第二直接依赖的范围是provided的时候,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为provided;
  • 当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime

依赖调解

当一个项目同时经过不同的路径依赖于同一个组件时,会选择其深度最短的对应组件进行依赖。当深度一样的时候Maven会根据申明的依赖顺序来进行选择,先申明的会被作为依赖包

可选依赖

可选依赖不会被传递,例如框架引用的数据库驱动,如果使用了可选依赖,不会被传递到使用该框架的项目上

参考

1.《Maven实战》

MyBatis核心组件

Configuration

MyBatis的主配置信息

MappedStatement

Mapper中的SQL配置信息

SqlSession

面向用户操作的api
SqlSession是Executor组件的外观,目的是为用户提供更友好的数据库操作接口,这是设计模式中外观(门面)模式的典型应用

Executor

执行器

StatementHandler

封装了JDBC Statement的操作

ParameterHandler

当MyBatis框架使用的Statement类型为CallableStatement和PreparedStatement时,ParameterHandler用于为Statement对象参数占位符设置值

ResultSetHandler

封装了对JDBC中的ResultSet对象操作

TypeHandler

Mybatis类型处理器

参考

  1. MyBatis 3源码深度解析/江荣波

MyBatis常用工具类学习

ScriptRunner

执行SQL脚本

SqlRunner

执行SQL

MetaObject

获取和设置对象的属性值
可以使用SystemMetaObject.forObject(Object object)创建

MetaClass

获取类相关的信息

ObjectFactory

用于创建对象
List、Map、Set接口对应的实现分别为ArrayList、HashMap、HashSet
应用:MyBatis提供的一种扩展机制,在得到映射结果之前我们需要处理一些逻辑,或者在执行该类的有参构造方法时,在传入参数之前,要对参数进行一些处理,这时我们可以通过自定义ObjectFactory来实现

ProxyFactory

代理工厂,主要用于创建动态代理对象
两种实现:CglibProxyFactory和JavassistProxyFactory
应用:ProxyFactory主要用于实现MyBatis的懒加载功能。当开启懒加载后,MyBatis创建Mapper映射结果对象后,会通过ProxyFactory创建映射结果对象的代理对象。当我们调用代理对象的Getter方法获取数据时,会执行CglibProxyFactory或JavassistProxyFactory中定义的拦截逻辑,然后执行一次额外的查询

参考

  1. MyBatis 3源码深度解析/江荣波

JDBC事务学习

自动提交

启用自动提交后,会在每个SQL语句执行完毕后自动提交事务

事务隔离级别

事务隔离级别

保存点

一旦设置保存点,事务就可以回滚到保存点,而不影响保存点之前的操作

DatabaseMetaData接口提供了supportsSavepoints()方法,用于判断JDBC驱动是否支持保存点

Connection接口中提供了setSavepoint()方法用于在当前事务中设置保存点,如果setSavepoint()方法在事务外调用,则调用该方法后会在setSavepoint()方法调用处开启一个新的事务。setSavepoint()方法的返回值是一个Savepoint对象,该对象可作为Connection对象rollback()方法的参数,用于回滚到对应的保存点

Connection对象中提供了一个releaseSavepoint()方法,接收一个Savepoint对象作为参数,用于释放当前事务中的保存点

事务中创建的所有保存点在事务提交或完成回滚之后会自动释放,事务回滚到某一保存点后,该保存点之后创建的保存点会自动释放

参考

  1. MyBatis 3源码深度解析/江荣波

DatabaseMetaData学习

DatabaseMetaData接口

  • 获取数据源信息
  • 确定数据源是否支持某一特性或功能
  • 获取数据源的限制
  • 确定数据源包含哪些SQL对象以及这些对象的属性
  • 获取数据源对事务的支持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
DatabaseMetaData metaData = connection.getMetaData();
pringln(metaData.getURL(),"获取数据库URL");
pringln(metaData.getUserName(),"获取数据库已知的用户");
pringln(metaData.getDatabaseProductName(),"获取数据库产品名");
pringln(metaData.getDatabaseProductVersion(),"获取数据库产品版本");
pringln(metaData.getDriverMajorVersion(),"获取驱动主版本");
pringln(metaData.getDriverMinorVersion(),"获取驱动副版本");
pringln(metaData.getSchemaTerm(),"获取数据库供应商用于Schema的首选术语");
pringln(metaData.getCatalogTerm(),"获取数据库供应商用于Catalog的首选术语");
pringln(metaData.getProcedureTerm(),"获取数据库供应商用于Procedure的首选术语");
pringln(metaData.nullsAreSortedHigh(),"获取null值是否高排序");
pringln(metaData.nullsAreSortedLow(),"获取null值是否低排序");
pringln(metaData.usesLocalFiles(),"获取数据库是否将表存储在本地文件中");
pringln(metaData.usesLocalFilePerTable(),"获取数据库是否为每个表使用一个文件");
pringln(metaData.getSQLKeywords(),"获取数据库SQL关键字");
pringln(metaData.supportsAlterTableWithDropColumn(),"检索此数据源是否支持带有删除列的ALTER TABLE语句");
pringln(metaData.supportsBatchUpdates(),"检索此数据源是否支持批量更新");
pringln(metaData.supportsTableCorrelationNames(),"检索此数据源是否支持表相关名称");
pringln(metaData.supportsPositionedDelete(),"检索此数据源是否支持定位的DELETE语句");
pringln(metaData.supportsFullOuterJoins(),"检索此数据源是否支持完整地嵌套外部连接");
pringln(metaData.supportsStoredProcedures(),"检索此数据源是否存储过程");
pringln(metaData.supportsMixedCaseQuotedIdentifiers(),"检索此数据源是否将用双引号引起来的大小写混合的SQL标识符视为区分大小写,并以混合大小写方式存储它们");
pringln(metaData.supportsANSI92EntryLevelSQL(),"检索此数据源是否支持ANSI92入门级SQL语法");
pringln(metaData.supportsCoreSQLGrammar(),"检索此数据源是否支持ODBC核心SQL语法");
pringln(metaData.getMaxRowSize(),"获取最大行数");
pringln(metaData.getMaxStatementLength(),"获取此数据库在SQL语句中允许的最大字符数");
pringln(metaData.getMaxTablesInSelect(),"获取此数据库在SELECT语句中允许的最大表数");
pringln(metaData.getMaxConnections(),"获取此数据库支持的最大连接数");
pringln(metaData.getMaxCharLiteralLength(),"获取数据库支持的字符串字面量长度");
pringln(metaData.getMaxColumnsInTable(),"获取数据库表中允许的最大列数");
//pringln(metaData.getSchemas(),"获取Schema信息");//返回ResultSet
//pringln(metaData.getCatalogs(),"获取Catalog信息");//返回ResultSet
//pringln(metaData.getTables(catalog, schemaPattern,tableNamePattern, types),"获取表信息");
//pringln(metaData.getPrimaryKeys(catalog,schema,table),"获取主键信息");//返回ResultSet
//pringln(metaData.getProcedures(catalog,schemaPattern,procedureNamePattern),"获取存储过程信息");//返回ResultSet
//pringln(metaData.getProcedureColumns(catalog,schemaPattern,procedureNamePattern,columnNamePattern),"获取给定类别的存储过程参数和结果列的信息");//返回ResultSet
//pringln(metaData.getUDTs(catalog, schemaPattern,tableNamePattern, types),"获取用户自定义数据类型");//返回ResultSet
//pringln(metaData.getFunctions(catalog,schemaPattern,functionNamePattern),"获取函数信息");//返回ResultSet
//pringln(metaData.getFunctionColumns(catalog,schemaPattern,functionNamePattern,columnNamePattern),"获取给定类别的函数参数和结果列的信息");//返回ResultSet
//pringln(metaData.supportsTransactionIsolationLevel(level),"是否支持某一事务隔离级别");
pringln(metaData.supportsTransactions(),"是否支持事务");
pringln(metaData.getDefaultTransactionIsolation(),"获取默认的事务隔离级别");
pringln(metaData.supportsMultipleTransactions(),"是否支持同时开启多个事务");

参考

  1. MyBatis 3源码深度解析/江荣波

ResultSet学习

ResultSet类型

  • 主要体现:

    • 游标可操作的方式
    • ResultSet对象的修改对数据库的影响
  • 类型:

    • TYPE_FORWARD_ONLY
      ResultSet不可滚动,游标只能向前移动,从第一行到最后一行,不允许向后移动

    • TYPE_SCROLL_INSENSITIVE
      ResultSet是可滚动的,它的游标可以相对于当前位置向前或向后移动,也可以移动到绝对位置
      ResultSet没有关闭时,ResultSet对象的修改不会影响对应的数据库中的记录

    • TYPE_SCROLL_SENSITIVE
      ResultSet是可滚动的,它的游标可以相对于当前位置向前或向后移动,也可以移动到绝对位置
      ResultSet没有关闭时,对ResultSet对象的修改会直接影响数据库中的记录

  • 判断

    • DatabaseMetaData接口中提供了一个supportsResultSetType()方法,用于判断数据库驱动是否支持某种类型的ResultSet对象
    • 当Statement对象执行时,产生的ResultSet对象可以通过ResultSet对象的getType()方法确定它的类型

ResultSet并行性

  • 类型

    • CONCUR_READ_ONLY
      为ResultSet对象设置这种属性后,只能从ResulSet对象中读取数据,但是不能更新ResultSet对象中的数据

    • CONCUR_UPDATABLE
      该属性表明,既可以从ResulSet对象中读取数据,又能更新ResultSet中的数据

  • 判断

    • DatabaseMetaData接口中提供了一个supportsResultSetConcurrency()方法,用于判断JDBC驱动是否支持某一级别的并行性
    • 在应用程序中,可以调用ResultSet对象的getConcurrency()方法获取ResultSet的并行性级别

ResultSet可保持性

ResultSet对象的holdability属性使得应用程序能够在Connection对象的commit()方法调用后控制ResultSet对象是否关闭

  • 类型:

    • HOLD_CURSORS_OVER_COMMIT
      当调用Connection对象的commit()方法时,不关闭当前事务创建的ResultSet对象。

    • CLOSE_CURSORS_AT_COMMIT
      当前事务创建的ResultSet对象在事务提交后会被关闭,对一些应用程序来说,这样能够提升系统性能。

  • 判断

    • DatabaseMetaData接口中提供了getResultSetHoldability()方法用于获取JDBC驱动的默认可保持性
    • 应用程序可以调用ResultSet对象的getHoldability()方法获取ResultSet的可保持性

属性设置

ResultSet的类型、并行性和可保持性等属性可以在调用Connection对象的createStatement()、prepareStatement()或prepareCall()方法创建Statement对象时设置

ResultSet游标

ResultSet对象中维护了一个游标,游标指向当前数据行
当ResultSet对象第一次创建时,游标指向数据的第一行

关闭ResultSet对象

  • ResultSet对象在下面两种情况下会显式地关闭:

    • 调用ResultSet对象的close()方法
    • 创建ResultSet对象的Statement或者Connection对象被显式地关闭
  • 在下面两种情况下ResultSet对象会被隐式地关闭:

    • 相关联的Statement对象重复执行时
    • 可保持性为CLOSE_CURSORS_AT_COMMIT的ResultSet对象在当前事务提交后会被关闭

参考

  1. MyBatis 3源码深度解析/江荣波

Statement 学习

Statement接口

  • 使用Statement对象执行完SQL后也需要关闭

PreparedStatement接口

  • 在Statement接口的基础上增加了参数占位符功能,可以为占位符设置值
  • PreparedStatement的实例表示可以被预编译的SQL语句,执行一次后,后续多次执行时效率会比较高
  • 参数占位符的位置(从1开始)
  • PreparedStatement对象设置的参数在执行后不能被重置,需要显式地调用clearParameters()方法清除先前设置的值,再为参数重新设置值即可

CallableStatement接口

  • 继承自PreparedStatement,在此基础上增加了调用存储过程以及检索存储过程调用结果的方法
  • 使用setXXX()方法为参数占位符设置值时,下标必须从1开始

参考

  1. MyBatis 3源码深度解析/江荣波

Connection学习

说明

一个Connection对象表示通过JDBC驱动与数据源建立的连接,这里的数据源可以是关系型数据库管理系统(DBMS)、文件系统(所以也可以通过Connection访问文件)或者其他通过JDBC驱动访问的数据

  • 如何获取Connection对象:
    • DriverManager
    • DataSource
  • Connection必须显示关闭
    Connection关闭后,由该Connection对象创建的所有Statement对象都会被关闭
  • 通过isClose关闭Connection
  • 为了更好的兼容性,通过isValid关闭Connection

JDBC驱动类型

  • JDBC-ODBC Bridge Driver
    利用现成的ODBC架构将JDBC调用转换为ODBC调用,并非所有功能都能直接转换并正常调用,而多层调用转换对性能也有一定的影响
  • Native API Driver
    直接调用数据库提供的原生链接库或客户端,没有中间过程,访问速度通常表现良好,驱动程序与数据库和平台绑定无法达到JDBC跨平台的基本目的
  • JDBC-Net Driver
    将JDBC调用转换为独立于数据库的协议,然后通过特定的中间组件或服务器转换为数据库通信协议,主要目的是获得更好的架构灵活性,通过中间服务器转换会对性能有一定影响
  • Native Protocol Driver
    驱动程序把JDBC调用转换为数据库特定的网络通信协议

java.sql.Driver接口

所有的JDBC驱动都必须实现Driver接口,而且实现类必须包含一个静态初始化代码块(调用DriverManager的registerDriver方法)注册驱动
在使用Class.forName方法加载驱动时,驱动需要提供一个无参构造方法

注册方式:

  • 驱动实现类静态代码块主动注册
  • 在DriverManager类初始化时,会试图加载所有jdbc.drivers属性指定的驱动类,因此可以通过指定jdbc.drivers属性来注册驱动
    1
    java -Djdbc.drivers=xxx ExampleApplication
  • Java的SPI机制加载驱动
    必须存在一个META-INF/services/java.sql.Driver文件,在java.sql.Driver文件中必须指定Driver接口的实现类

Java SPI机制简介

当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services目录中创建一个以服务接口命名的文件,这个文件中的内容就是这个接口具体的实现类。当其他的程序需要这个服务的时候,就可以查找这个JAR包中META-INF/services目录的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名加载服务实现类,然后就可以使用该服务了

1
2
3
4
ServiceLoader<Driver> drivers = ServiceLoader.load(java.sql.Driver.class);
for (Driver driver : drivers ) {
System.out.println(driver.getClass().getName());
}

java.sql.DriverAction接口

JDBC驱动可以通过实现DriverAction接口来监听DriverManager类的deregisterDriver()方法的调用

java.sql.DriverManager类

DriverManager类通过Driver接口为JDBC客户端管理一组可用的驱动实现

javax.sql.DataSource接口

  • 通过连接池提高系统性能和伸缩性
  • 通过XADataSource接口支持分布式事务

DataSource接口的实现必须包含一个无参构造方法

JNDI

为应用程序提供了一种通过网络访问远程服务的方式,例如数据库配置放在tomcat的配置中,通过JNDI获取数据库连接

参考

  1. MyBatis 3源码深度解析/江荣波