Mybatis连接数据库部分源码分析
1、JDBC连接数据库的四部曲
public static void main(String[] args)throws Exception {
//1. 加载驱动
Class.forName("sql.jdbc.Driver");
String url ="jdbc:mysql://localhost:3306/test";
String ur ="root";
String password ="123456";
//2. 获取连接
Connection connection = Connection(url,ur,password);
String sql =" lect id,name from ur ";
Statement statement = ateStatement();
//3. 执⾏sql
ResultSet rs = uteQuery(sql);
//4. 解析结果
()){
int id = rs.getInt(1);
String name = rs.getString(2);
System.out.println("id:"+id+",name:"+name);
}
rs.clo();
statement.clo();
connection.clo();
}
2、mybatis连接数据库源码分析
mybatis基于jdbc,本质上不过是对jdbc进⾏封装,这⾥分析⼀下源码是如何连接到数据库的,即对应jdbc的加载驱动和获取连接;
public static void main(String[] args)throws InterruptedException {
Reader reader = ResourceAsReader("l");
SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
}
这⾥简单测试获取到sqlSession,拿到它就可以进⾏数据库操作。
调试时注意在第2⾏打上断点,⼀路debug下去发现build⽅法主要是在解析mybatis的xml⽂件,当解析执⾏到
到org.apache.l.XMLConfigBuilder#environmentsElement要注意,下图284⾏是解析数据源配置,
跟踪进⼊org.apache.l.XMLConfigBuilder#dataSourceElement,注意330⾏
此处明显是⽤了反射来实例化对象,这⾥直接在idea调试⼯具中看不出来resolveClass后得到的是什么类型,此处可以⽤idea⾃带的调试⼯具打印出来看看就知道了,如下图:
打印结果:class org.apache.ibatis.datasource.pooled.PooledDataSourceFactory,此处的PooledDataSourceFactory对应配置⽂件中的
type=“pooled”
<dataSource type="pooled">
<!-- 配置与数据库交互的4个必要属性 -->
<property name="driver"value="${oracle.driver}"/>
<property name="url"value="${oracle.url}"/>
<property name="urname"value="${oracle.urname}"/>
<property name="password"value="${oracle.password}"/>
</dataSource>
知道反射结果的类型后,直接找到这个类,在他的构造函数打个断点,注意public class PooledDataSourceFactory extends UnpooledDataSourceFactory,构造函数执⾏时需要先调⽤其⽗
类的构造⽅法,所以我们要在其⽗类构造⽅法也打断点观察,
其⽗类构造函数
public UnpooledDataSourceFactory(){
this.dataSource =new UnpooledDataSource();
}
调⽤org.apache.ibatis.datasource.unpooled.UnpooledDataSource的构造⽅法,所以进⼊UnpooledDataSource类,此类就⽐较重点了,它有个静态代码块:
static{
Enumeration<Driver> drivers = Drivers();
while(drivers.hasMoreElements()){
Driver driver = Element();
registeredDrivers.Class().getName(), driver);
}
}
静态代码快有个Drivers();,这⾥就是加载驱动,此函数中的操作就类似我们在jdbc中的Class.forName(xxxx),跟踪进⼊java.sql.DriverManager看看,此类也有个静态代码块:
/**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then u the {@code ServiceLoader} mechanism
*/
static{
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
重点来了,有个loadInitialDrivers(),这⾥就是经典的java的spi实现,第20⾏会读取mysql驱动包下的META-INF/rvices/java.sql.Driver⽂件,此⽂件中只有⼀⾏sql.cj.jdbc.Driver,代表java.sql.Driver接⼝的实现类是sql.cj.jdbc.Driver,此处需要了解java的SPI机制
private static void loadInitialDrivers(){
String drivers;
try{
drivers = AccessController.doPrivileged(new PrivilegedAction<String>(){
public String run(){
Property("jdbc.drivers");
}
});
}catch(Exception ex){
drivers = null;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
/
/ expod as a java.sql.Driver.class rvice.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>(){
public Void run(){
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
/* Load the drivers, so that they can be instantiated.
* It may be the ca that the driver class may not be there
* i.e. there may be a packaged driver with the rvice class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that ca a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the rvice.
*
* Adding a try catch block to catch tho runtime errors
* if driver not available in classpath but it's
* packaged as rvice and that rvice is there in classpath.
*/
try{
while(driversIterator.hasNext()){
//进⼊此⽅法继续跟踪
<();
}
}catch(Throwable t){
// Do nothing
}
return null;
}
});
继续跟踪进⼊();会到达java.util.Iterator#next,最后肯定会到java.util.ServiceLoader.LazyIterator#nextService,如下代码,第8⾏调⽤了反射来加载之前读取到的sql.cj.jdbc.Driver,这⾥就完成了SPI,就加载了驱动
private S nextService(){
if(!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try{
c = Class.forName(cn,fal, loader);
}catch(ClassNotFoundException x){
fail(rvice,
"Provider "+ cn +" not found");
}
if(!rvice.isAssignableFrom(c)){
fail(rvice,
"Provider "+ cn +" not a subtype");
}
try{
S p = rvice.wInstance());
providers.put(cn, p);
return p;
}catch(Throwable x){
fail(rvice,
"Provider "+ cn +" could not be instantiated",
x);
}
throw new Error();// This cannot happen
}
加载驱动后,需要注册驱动,加载sql.cj.jdbc.Driver同样会执⾏静态代码块,同时完成驱动注册
static{
try{
java.isterDriver(new Driver());
}catch(SQLException E){
throw new RuntimeException("Can't register driver!");
}
}
后⾯不再分析了。。。。