jdbc为什么不用写Class.forName来加载驱动了?

jdbc不用显示地写Class.forName(driver);

在这之前,有一个问题就是,在java程序中,是可以不用显式地加载的,但是在tomcat部署web应用时,必须要显式加载,我在tomcat的lib目录中加入了mysql的jar包也不行,而且debug发现程序也找到了对应的文件并加载了驱动,但还是抛出了异常,表示找不到合适的驱动来加载,希望有大佬能解释一下。

/*getConnection为DriverManager的静态方法,在执行方法之前
	DriverManager会先进行初始化
*/
DriverManager.getConnection(url,username,password);
/*
	初始化时会执行静态代码块中的代码
*/
static {
    //此方法会加载初始化的驱动
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

private static void loadInitialDrivers() {
    String drivers;
    try {
        drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
            public String run() {
                //从系统设置中获取到drivers的字符串,为驱动类的全限定名
                return System.getProperty("jdbc.drivers");
            }
        });
    } catch (Exception ex) {
        drivers = null;
    }
   
    //AccessController.doPrivileged 访问控制,获得特权,不用检查
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            //由当前线程获取到上下文类加载器
            //通过ServiceLoad从META-INF.services目录下加载类
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            try{
                //这一步放到后面讲
                while(driversIterator.hasNext()) {
                    driversIterator.next();
                }
            } catch(Throwable t) {
                //什么都不做
            }
            return null;
        }
    });

    println("DriverManager.initialize: jdbc.drivers = " + drivers);
    //为空就返回
    if (drivers == null || drivers.equals("")) {
        return;
    }
    //根据":"分割字符串,获取到每个驱动的全限定名
    String[] driversList = drivers.split(":");
    println("number of Drivers:" + driversList.length);
    for (String aDriver : driversList) {
        try {
            println("DriverManager.Initialize: loading " + aDriver);
            //加载驱动类
            Class.forName(aDriver, true,
                          ClassLoader.getSystemClassLoader());
        } catch (Exception ex) {
            println("DriverManager.Initialize: load failed: " + ex);
        }
    }
}

//驱动类被加载后会执行静态代码块中的方法,将创建一个驱动实例,并注册到DriverManager中
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

public static synchronized void registerDriver(java.sql.Driver driver,
                                               DriverAction da)
    throws SQLException {

   	//注册驱动
    if(driver != null) {
        //addIfAbsent
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
    }

    println("registerDriver: " + driver);

}
...
try{
    //这一步放到后面讲
    while(driversIterator.hasNext()) {
        driversIterator.next();
    }
} catch(Throwable t) {
    //什么都不做
}
...

在获取到迭代的之后,进入到一个while循环,调用hasNext()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1KZDVRvO-1660806023847)(C:\Users\郑蒙恩\AppData\Roaming\Typora\typora-user-images\image-20220818140503627.png)]

//进入ServiceLoader中的懒加载器LazyIterator类中的方法
public boolean hasNext() {
    if (knownProviders.hasNext())//false
        return true;
    return lookupIterator.hasNext();
}
//进入到lookuoIterator.hasNext() ,
public boolean hasNext() {
    if (acc == null) { //true
        return hasNextService();
    } else {
        PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
            public Boolean run() { return hasNextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}
//进入hasNextService()
private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        try {
            /*
            	private static final String PREFIX = "META-INF/services/";
            	service就是java.sql.Driver
            */
            String fullName = PREFIX + service.getName();
            if (loader == null)//false
                //如果loader为空,类加载器会去加载META-INF/services/的java.sql.Driver文件到configs
                configs = ClassLoader.getSystemResources(fullName);
            else
                configs = loader.getResources(fullName);//configs是Enumeration<URL>类
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
        //如果configs没有更多的元素,返回false
        if (!configs.hasMoreElements()) {
            return false;
        }
        pending = parse(service, configs.nextElement());
    }
    nextName = pending.next();
    return true;
}
//进入到configs.hasMoreElements()
//public class CompoundEnumeration<E> implements Enumeration<E> 下的next方法
private boolean next() {
    //根据enums的长度遍历,又下图可知enums为2
    while(this.index < this.enums.length) {
        //如果index位置不为空,且该位置的复合枚举有更多的元素,返回true
        /*
        	index[0]没有更多的元素
        */
        if (this.enums[this.index] != null && this.enums[this.index].hasMoreElements()) {
            return true;
        }
        ++this.index;
    }

    return false;
}
//进入index[1]的hasMoreElements() 
//进入到URLClassLoader的next()方法
private boolean next() {
    if (url != null) {
        return true;
    }
    do {
        URL u = AccessController.doPrivileged(
            new PrivilegedAction<URL>() {
                public URL run() {
                    /*
                    	final Enumeration<URL> e = ucp.findResources(name, true);
                    	ucp是URLClassLoader对象
                    */
                    //e是一个Enumeration<URL>对象,e具体的内容看下图
                    //进入到e.hasMoreElements
                    if (!e.hasMoreElements())
                        return null;
                    return e.nextElement();
                }
            }, acc);
        if (u == null)
            break;
        url = ucp.checkURL(u);
    } while (url == null);
    return url != null;
}

//
private boolean next() {
    if (this.url != null) {
        return true;
    } else {
        do {
            URLClassPath.Loader var1x;
            //遍历所有的URLClassPath,直到遍历完
            if ((var1x = URLClassPath.this.getNextLoader(this.cache, this.index++)) == null) {
                return false;
            }
			//将找到的资源赋值给url
            this.url = var1x.findResource(var1, var2);
        } while(this.url == null);//如果加载器都没有找到这个资源,就循环,直到找到。

        return true;
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-izvgLfJR-1660806023848)(C:\Users\郑蒙恩\AppData\Roaming\Typora\typora-user-images\image-20220818141749554.png)]

index[0]的hasMoreElements()为false

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MdWPme0Y-1660806023849)(C:\Users\郑蒙恩\AppData\Roaming\Typora\typora-user-images\image-20220818142301045.png)]

URLClassPath

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yZtH66FR-1660806023850)(C:\Users\郑蒙恩\AppData\Roaming\Typora\typora-user-images\image-20220818142730609.png)]

30个类加载器对应加载的类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nL7PczRG-1660806023852)(C:\Users\郑蒙恩\AppData\Roaming\Typora\typora-user-images\image-20220818143507636.png)]

循环到了mysql的jar,找到了对应的META-INF/service/java.sql.Driver,退出循环,返回true

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j5jheNjt-1660806023854)(C:\Users\郑蒙恩\AppData\Roaming\Typora\typora-user-images\image-20220818143825940.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JZH5dQgN-1660806023855)(C:\Users\郑蒙恩\AppData\Roaming\Typora\typora-user-images\image-20220818143843772.png)]

然后将结果返回个URL的对象u

URL u = AccessController.doPrivileged(
    new PrivilegedAction<URL>() {
        public URL run() {
            if (!e.hasMoreElements())
                return null;
            return e.nextElement();
        }
    }, acc);

如果u不为空,就检查这个URL并赋值给url

if (u == null)
    break;//如果u为空,则退出while循环,返回false
url = ucp.checkURL(u);

回到开始进入到configs.hasMoreElements()的if判断的语句

while ((pending == null) || !pending.hasNext()) {
    if (!configs.hasMoreElements()) {//false
        return false;
    }
    //解析获取到的元素,并赋值给pending
    pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;

pending是一个迭代器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6QElC9VP-1660806023856)(C:\Users\郑蒙恩\AppData\Roaming\Typora\typora-user-images\image-20220818144804521.png)]

调用pending的next(),得到com.sql.cj.jdbc.Driver并赋值给nextName

然后返回true

进入driversIterator.next()

while(driversIterator.hasNext()) {
    driversIterator.next();
}

接着debug进入,到达ServiceLoader的懒加载类中的nextService方法

private S nextService() {
    if (!hasNextService())
        throw new NoSuchElementException();
    String cn = nextName;//拿到之前赋值过的nextName
    nextName = null;
    Class<?> c = null;
    try {
        c = Class.forName(cn, false, loader);//加载这个类,到这里就知道为什么不用自己加载驱动了
    } 
    ...
}

以上的过程,只是大概的流程,请debug跟一遍就清楚了。

nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;//拿到之前赋值过的nextName
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);//加载这个类,到这里就知道为什么不用自己加载驱动了
}

}


以上的过程,只是大概的流程,请debug跟一遍就清楚了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值