一篇文章吃透:为什么加载数据库驱动要用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


相关文章
|
1天前
|
人工智能 数据管理 Serverless
阿里云数据库走向Serverless与AI驱动的一站式数据平台具有重大意义和潜力
阿里云数据库走向Serverless与AI驱动的一站式数据平台具有重大意义和潜力
410 2
|
1天前
|
人工智能 运维 Cloud Native
、你如何看待阿里云数据库走向Serverless与AI驱动的一站式数据平台?
、你如何看待阿里云数据库走向Serverless与AI驱动的一站式数据平台?
150 2
|
1天前
|
人工智能 数据管理 大数据
阿里云数据库走向Serverless与AI驱动的一站式数据平台是一个很有前景和意义的发展方向
阿里云数据库走向Serverless与AI驱动的一站式数据平台是一个很有前景和意义的发展方向
39 2
|
1天前
|
Cloud Native 关系型数据库 分布式数据库
数据之势丨云原生数据库,走向Serverless与AI驱动的一站式数据平台
在大模型席卷之下,历史的齿轮仍在转动,很多人开始思考,大模型能为数据库带来哪些改变。阿里云数据库产品事业部负责人李飞飞表示,数据库和智能化的结合是未来非常重要的发展方向,数据库的使用门槛将大幅降低。
|
1天前
|
Java 测试技术 数据库
SpringBoot启动时设置不加载数据库
SpringBoot启动时设置不加载数据库
16 0
|
1天前
|
SQL Java 关系型数据库
在Python中编写Java数据库驱动是不可能的
在Python中编写Java数据库驱动是不可能的
|
1天前
|
存储 NoSQL 关系型数据库
一篇文章带你搞懂非关系型数据库MongoDB
一篇文章带你搞懂非关系型数据库MongoDB
70 0
|
1天前
|
存储 SQL 关系型数据库
【MySQL 数据库】6、一篇文章学习【索引知识】,提高大数据量的查询效率【文末送书】
【MySQL 数据库】6、一篇文章学习【索引知识】,提高大数据量的查询效率【文末送书】
62 0
|
1天前
|
人工智能 运维 数据管理
阿里云数据库走向Serverless与AI驱动的一站式数据平台
阿里云数据库走向Serverless与AI驱动的一站式数据平台
274 2
|
1天前
|
人工智能 Cloud Native 数据管理
阿里云数据库:向Serverless与AI驱动的一站式数据平台迈进
众所周知,在人工智能迅猛发展的现在,在AI驱动下的数据平台,正在向一站式、智能化的方向演进,还有就是云原生+Serverless的不断深入,一站式数据平台将让数据管理开发像“搭积木”一样简单实用,以性价比更高、体验更优的云数据库服务,助推用户业务提效增速。据悉阿里云数据库正在朝着Serverless与AI驱动的方向发展,构建一站式、智能化的数据平台,这一发展趋势将为用户提供更简单、实用的数据管理开发体验,以提高业务效率和降低成本。那么本文就来分享一下如何看待阿里云数据库的这一转变,并展望云原生和Serverless对数据管理与开发的未来带来的更多可能性。
71 1
阿里云数据库:向Serverless与AI驱动的一站式数据平台迈进