一篇文章吃透:为什么加载数据库驱动要用Class.forName()

简介: 一篇文章吃透:为什么加载数据库驱动要用Class.forName(

1、Class.forName()和ClassLoader.loadClass()和new XX的区别

Class.forName():将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static代码块。


ClassLoader.loadClass():只会将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。



《深入理解Java虚拟机》p214讲类的加载过程:加载、验证、准备、解析和初始化。


其中


加载:主要在内存中生成class文件对应的Class对象,作为方法区这个类各种数据的访问入口。


验证:验证Class文件的字节流中的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。


4444.png备:为类变量分配内存并设置类变量的初始值。


解析:常量池中的符号引用替换为直接引用。


初始化:初始化阶段才开始执行类中定义的Java程序代码。


我们看Class.forName源码


   @CallerSensitive

   public static Class<?> forName(String className)

               throws ClassNotFoundException {

       Class<?> caller = Reflection.getCallerClass();

       return forName0(className, true, ClassLoader.getClassLoader(caller), caller);

   }

调用了三个参数的重载的方法


   /**

    * Returns the {@code Class} object associated with the class or

    * interface with the given string name, using the given class loader.

    * Given the fully qualified name for a class or interface (in the same

    * format returned by {@code getName}) this method attempts to

    * locate, load, and link the class or interface.  The specified class

    * loader is used to load the class or interface.  If the parameter

    * {@code loader} is null, the class is loaded through the bootstrap

    * class loader.  The class is initialized only if the

    * {@code initialize} parameter is {@code true} and if it has

    * not been initialized earlier.

    *

    * <p> If {@code name} denotes a primitive type or void, an attempt

    * will be made to locate a user-defined class in the unnamed package whose

    * name is {@code name}. Therefore, this method cannot be used to

    * obtain any of the {@code Class} objects representing primitive

    * types or void.

    *

    * <p> If {@code name} denotes an array class, the component type of

    * the array class is loaded but not initialized.

    *

    * <p> For example, in an instance method the expression:

    *

    * <blockquote>

    *  {@code Class.forName("Foo")}

    * </blockquote>

    *

    * is equivalent to:

    *

    * <blockquote>

    *  {@code Class.forName("Foo", true, this.getClass().getClassLoader())}

    * </blockquote>

    *

    * Note that this method throws errors related to loading, linking or

    * initializing as specified in Sections 12.2, 12.3 and 12.4 of <em>The

    * Java Language Specification</em>.

    * Note that this method does not check whether the requested class

    * is accessible to its caller.

    *

    * <p> If the {@code loader} is {@code null}, and a security

    * manager is present, and the caller's class loader is not null, then this

    * method calls the security manager's {@code checkPermission} method

    * with a {@code RuntimePermission("getClassLoader")} permission to

    * ensure it's ok to access the bootstrap class loader.

    *

    * @param name       fully qualified name of the desired class

    * @param initialize if {@code true} the class will be initialized.

    *                   See Section 12.4 of <em>The Java Language Specification</em>.

    * @param loader     class loader from which the class must be loaded

    * @return           class object representing the desired class

    *

    * @exception LinkageError if the linkage fails

    * @exception ExceptionInInitializerError if the initialization provoked

    *            by this method fails

    * @exception ClassNotFoundException if the class cannot be located by

    *            the specified class loader

    *

    * @see       java.lang.Class#forName(String)

    * @see       java.lang.ClassLoader

    * @since     1.2

    */

   @CallerSensitive

   public static Class<?> forName(String name, boolean initialize,

                                  ClassLoader loader)

       throws ClassNotFoundException

   {

       Class<?> caller = null;

       SecurityManager sm = System.getSecurityManager();

       if (sm != null) {

           // Reflective call to get caller class is only needed if a security manager

           // is present.  Avoid the overhead of making this call otherwise.

           caller = Reflection.getCallerClass();

           if (sun.misc.VM.isSystemDomainLoader(loader)) {

               ClassLoader ccl = ClassLoader.getClassLoader(caller);

               if (!sun.misc.VM.isSystemDomainLoader(ccl)) {

                   sm.checkPermission(

                       SecurityConstants.GET_CLASSLOADER_PERMISSION);

               }

           }

       }

       return forName0(name, initialize, loader, caller);

   }


提示我们第二个参数表示是否初始化,看java参考手册


https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4



new可以理解粗略的理解为:   【加载class文件到jvm + 初始化static代码块】(Class.forName) +构造实例(newInstance)


/**

    * Creates a new instance of the class represented by this {@code Class}

    * object.  The class is instantiated as if by a {@code new}

    * expression with an empty argument list.  The class is initialized if it

    * has not already been initialized.

    *

    * <p>Note that this method propagates any exception thrown by the

    * nullary constructor, including a checked exception.  Use of

    * this method effectively bypasses the compile-time exception

    * checking that would otherwise be performed by the compiler.

    * The {@link

    * java.lang.reflect.Constructor#newInstance(java.lang.Object...)

    * Constructor.newInstance} method avoids this problem by wrapping

    * any exception thrown by the constructor in a (checked) {@link

    * java.lang.reflect.InvocationTargetException}.

    *

    * @return  a newly allocated instance of the class represented by this

    *          object.

    * @throws  IllegalAccessException  if the class or its nullary

    *          constructor is not accessible.

    * @throws  InstantiationException

    *          if this {@code Class} represents an abstract class,

    *          an interface, an array class, a primitive type, or void;

    *          or if the class has no nullary constructor;

    *          or if the instantiation fails for some other reason.

    * @throws  ExceptionInInitializerError if the initialization

    *          provoked by this method fails.

    * @throws  SecurityException

    *          If a security manager, <i>s</i>, is present and

    *          the caller's class loader is not the same as or an

    *          ancestor of the class loader for the current class and

    *          invocation of {@link SecurityManager#checkPackageAccess

    *          s.checkPackageAccess()} denies access to the package

    *          of this class.

    */

   @CallerSensitive

   public T newInstance()

       throws InstantiationException, IllegalAccessException

   {

       if (System.getSecurityManager() != null) {

           checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);

       }

       // NOTE: the following code may not be strictly correct under

       // the current Java memory model.

       // Constructor lookup

       if (cachedConstructor == null) {

           if (this == Class.class) {

               throw new IllegalAccessException(

                   "Can not call newInstance() on the Class for java.lang.Class"

               );

           }

           try {

               Class<?>[] empty = {};

               final Constructor<T> c = getConstructor0(empty, Member.DECLARED);

               // Disable accessibility checks on the constructor

               // since we have to do the security check here anyway

               // (the stack depth is wrong for the Constructor's

               // security check to work)

               java.security.AccessController.doPrivileged(

                   new java.security.PrivilegedAction<Void>() {

                       public Void run() {

                               c.setAccessible(true);

                               return null;

                           }

                       });

               cachedConstructor = c;

           } catch (NoSuchMethodException e) {

               throw (InstantiationException)

                   new InstantiationException(getName()).initCause(e);

           }

       }

       Constructor<T> tmpConstructor = cachedConstructor;

       // Security check (same as in java.lang.reflect.Constructor)

       int modifiers = tmpConstructor.getModifiers();

       if (!Reflection.quickCheckMemberAccess(this, modifiers)) {

           Class<?> caller = Reflection.getCallerClass();

           if (newInstanceCallerCache != caller) {

               Reflection.ensureMemberAccess(caller, this, null, modifiers);

               newInstanceCallerCache = caller;

           }

       }

       // Run constructor

       try {

           return tmpConstructor.newInstance((Object[])null);

       } catch (InvocationTargetException e) {

           Unsafe.getUnsafe().throwException(e.getTargetException());

           // Not reached

           return null;

       }

   }

其中根据newInstance是创建一个代表这个类对象的实例,如果没有初始化会触发初始化。


代码的主要逻辑是查找无参数构造方法,然后通过反射来调用构造实例。




写个测试类验证一下:


/**

* 加载测试demo类

*

* @author: 明明如月 liuwangyanghdu@163.com

* @date: 2019-04-09 12:43

*/

public class LoadTestClass {

   static {

       System.out.println("静态代码块");

   }

   public LoadTestClass() {

       System.out.println("构造方法");

   }

  public static void test() {

       System.out.println("这是静态方法");

   }

}

测试类


/**

* 加载测试类

*

* @author: 明明如月 liuwangyanghdu@163.com

* @date: 2019-04-09 12:42

*/

public class LoadTest {

@Test

   public void test(){

       LoadTestClass.test();

   }

  @Test

   public void forName() throws ClassNotFoundException {

       Class<?> aClass = Class.forName("com.chujianyun.common.clazz.LoadTestClass");

       System.out.println(aClass);

   }

   @Test

   public void newTest() {

       new LoadTestClass();

   }

   @Test

   public void loader() throws ClassNotFoundException {

       Class<?> aClass = ClassLoader.getSystemClassLoader().loadClass("com.chujianyun.common.clazz.LoadTestClass");

       System.out.println(aClass);

   }

   @Test

   public void loaderNewInstance() throws ClassNotFoundException, IllegalAccessException, InstantiationException {

       Class<?> aClass = ClassLoader.getSystemClassLoader().loadClass("com.chujianyun.common.clazz.LoadTestClass");

       System.out.println(aClass);

       Object result = aClass.newInstance();

       System.out.println(result instanceof LoadTestClass);

   }

}

测试类分别输出:













另外可以看到调用静态方法前,会触发静态代码块的调用(也会触发类的加载)。



二、为什么加载数据库驱动要用Class.forName()?


其实JDBC4.0以后(mysql-connector-java 5.1.6之后) + java6以后,不再需要显示调用Class.forName()加载驱动了。


下面是摘录的一段话,简单明了:


JDBC 4.0的特性

得益于Mustang中的Java SE 服务提供商机制,Java开发人员再也不必用类似Class.forName() 的代码注册JDBC驱动来明确加载JDBC。当调用DriverManager.getConnection()方法时,DriverManager类将 自动设置合适的驱动程序。该特性向后兼容,因此无需对现有的JDBC代码作任何改动。


https://www.ibm.com/developerworks/cn/java/j-lo-jse65/#N100EE



JDBC 4.0 的规范规定,所有 JDBC 4.0 的驱动 jar 文件必须包含一个 java.sql.Driver,它位于 jar 文件的 META-INF/services 目录下



接下来详细展开:


以mysql驱动 8.0.11为例,采用了SPI机制(这里不展开,详细了解可参考这篇文章:https://juejin.im/post/5af952fdf265da0b9e652de3

————————————————

版权声明:本文为CSDN博主「明明如月学长」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/w605283073/article/details/89164675


相关文章
|
3月前
|
关系型数据库 MySQL Java
【IDEA】java后台操作mysql数据库驱动常见错误解决方案
【IDEA】java后台操作mysql数据库驱动常见错误解决方案
132 0
|
2月前
|
存储 人工智能 Cloud Native
云栖重磅|从数据到智能:Data+AI驱动的云原生数据库
在9月20日2024云栖大会上,阿里云智能集团副总裁,数据库产品事业部负责人,ACM、CCF、IEEE会士(Fellow)李飞飞发表《从数据到智能:Data+AI驱动的云原生数据库》主题演讲。他表示,数据是生成式AI的核心资产,大模型时代的数据管理系统需具备多模处理和实时分析能力。阿里云瑶池将数据+AI全面融合,构建一站式多模数据管理平台,以数据驱动决策与创新,为用户提供像“搭积木”一样易用、好用、高可用的使用体验。
云栖重磅|从数据到智能:Data+AI驱动的云原生数据库
|
2月前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
87 4
|
4月前
|
存储 SQL 关系型数据库
一篇文章搞懂MySQL的分库分表,从拆分场景、目标评估、拆分方案、不停机迁移、一致性补偿等方面详细阐述MySQL数据库的分库分表方案
MySQL如何进行分库分表、数据迁移?从相关概念、使用场景、拆分方式、分表字段选择、数据一致性校验等角度阐述MySQL数据库的分库分表方案。
580 15
一篇文章搞懂MySQL的分库分表,从拆分场景、目标评估、拆分方案、不停机迁移、一致性补偿等方面详细阐述MySQL数据库的分库分表方案
|
3月前
|
SQL 数据管理 数据库
文章初学者指南:SQL新建数据库详细步骤与最佳实践
引言:在当今数字化的世界,数据库管理已经成为信息技术领域中不可或缺的一部分。作为广泛使用的数据库管理系统,SQL已经成为数据管理和信息检索的标准语言。本文将详细介绍如何使用SQL新建数据库,包括准备工作、具体步骤和最佳实践,帮助初学者快速上手。一、准备工作在开始新建数据库之前,你需要做好以下准备工作
277 3
|
3月前
|
存储 人工智能 Cloud Native
云栖重磅|从数据到智能:Data+AI驱动的云原生数据库
阿里云瑶池在2024云栖大会上重磅发布由Data+AI驱动的多模数据管理平台DMS:OneMeta+OneOps,通过统一、开放、多模的元数据服务实现跨环境、跨引擎、跨实例的统一治理,可支持高达40+种数据源,实现自建、他云数据源的无缝对接,助力业务决策效率提升10倍。
|
4月前
|
存储 人工智能 Cloud Native
云栖重磅|从数据到智能:Data+AI驱动的云原生数据库
阿里云数据库重磅升级!元数据服务OneMeta + OneOps统一管理多模态数据
|
5月前
|
SQL Java 数据库连接
java连接数据库加载驱动到java项目
该博客文章介绍了如何在Java项目中通过代码加载数据库驱动并连接SQL Server数据库,包括具体的加载驱动和建立数据库连接的步骤,以及如何将驱动包添加到Java项目的构建路径中。
|
5月前
|
数据库连接 数据库
实现加载驱动、得到数据库对象、关闭资源的代码复用,将代码提取到相应的工具包里边。优化程序
该博客文章展示了如何通过创建工具类`Connectiontools`实现数据库连接、语句执行以及资源关闭的代码复用,以优化程序并提高数据库操作的效率和安全性。
|
18天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
44 3