为什么说JDBC驱动类加载破坏了双亲委派机制

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

大家都知道jdk的类加载机制是双亲委派机制,当我们需要加载一个类的时候,比如如下几种情况

  • new一个类的对象
  • 调用类的静态成员(除了final常量)和静态方法
  • 使用java.lang.reflect包的方法对类进行反射调用
  • 当虚拟机启动,java Demo01,则一定会初始化Demo01类,加载main方法所在的类
  • 当初始化一个类,如果其父类没有被初始化,则先初始化它父类

那么看一段代码:

    public static void main(String[] args) throws SQLException {
        String url = "jdbc:mysql://...";
        String username = "root";
        String password = "root";
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url, username, password);
            PreparedStatement statement = conn.prepareStatement("select * from table where id = ?");
            statement.setLong(1,615444000000000L);
            ResultSet set = statement.executeQuery();
            while(set.next()){
                System.out.println(set.getString("user_id"));
            }
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

上面是一段标准的jdbc查询代码,在加载java.sql.DriverManager类的时候,会执行静态块

    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

主要是这两行代码

ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
//我们看到这里使用了线程上下文加载器,为什么这里要这么做??如果不这么做会怎样?
    public static  ServiceLoader load(Class service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

下面是java.util.ServiceLoader$LazyIterator.nextService()方法

    private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class c = null;
            try {
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service,
                     "Provider " + cn  + " not a subtype");
            }
            try {
                S p = service.cast(c.newInstance());
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

我们看到了Class.forName(cn, false, loader)的调用,如果前面没有用线程上下文加载器(也就是loader),那么这里默认使用的是调用方的类加载器,如下所示:

/**
     * Returns the {@code Class} object associated with the class or
     * interface with the given string name.  Invoking this method is
     * equivalent to:
     *
     * 
* {@code Class.forName(className, true, currentLoader)} *
* * where {@code currentLoader} denotes the defining class loader of * the current class. * *

For example, the following code fragment returns the * runtime {@code Class} descriptor for the class named * {@code java.lang.Thread}: * *

* {@code Class t = Class.forName("java.lang.Thread")} *
*

* A call to {@code forName("X")} causes the class named * {@code X} to be initialized. * * @param className the fully qualified name of the desired class. * @return the {@code Class} object for the class with the * specified name. * @exception LinkageError if the linkage fails * @exception ExceptionInInitializerError if the initialization provoked * by this method fails * @exception ClassNotFoundException if the class cannot be located */ @CallerSensitive public static Class forName(String className) throws ClassNotFoundException { Class caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller); }

那么如果没有使用线程上下文加载器,其实用的就是调用方 ServiceLoader 的加载器,而ServiceLoader在java.util下面,是无法加载com.mysql.jdbc包下的类。 我不认为这是对双亲委派的破坏。

转载于:https://my.oschina.net/wuxiaofei/blog/3045533

你可能感兴趣的:(为什么说JDBC驱动类加载破坏了双亲委派机制)