为什么这本书说JDBC损害了父母的授权模式?

时间:2018-04-25 11:46:38

标签: java jdbc jvm classloader

有些书说JDBC破坏了Parental委托模型,但我读到了关于JDBC的来源,但没有找到破解显示的地方。

似乎每个类都由Application Classloader加载:

public static void doingJdbc(){
    try {

        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/design", "root", "fengcs");

        String sql = "select * from tb_user";

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        preparedStatement.executeQuery();

        ResultSet resultSet = preparedStatement.getResultSet();

        while (resultSet.next()) {
            int id = resultSet.getInt(1);
            System.out.println("===================>"+ id);
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

在DriverManager中也没有发现破坏,我不知道代码显示破坏的位置。

请告诉我具体的代码,谢谢。

1 个答案:

答案 0 :(得分:0)

它确实破坏了家长委托模式。

委托模型要求在请求类加载器尝试加载类本身之前,将对类加载器的任何请求首先委派给其父类加载器。

因此,通过委托模型,您的代码片段中的java.sql.Connection应该由系统类加载器(又称为应用程序类加载器)传递给BootStrap类加载器(根类加载器),并且BootStrap类加载器将尝试加载它。并且可以加载。

尝试:

    public static void main(String[] args){
        System.out.println(Connection.class.getClassLoader());
    }

输出为null,这意味着它是由根类加载器加载的。

但是,如果您这样做


    public static void main(String[] args){
        Connection connection = null;
        try {
            connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/foo", "username", "password");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        System.out.println(connection.getClass().getClassLoader());
    }

输出为sun.misc.Launcher$AppClassLoader@18b4aac2或类似的东西,这意味着它是由System classloader(应用程序类加载器)加载的。

因此,我们证明了根类加载器能够加载java.sql.Connection,但是由其子类System加载器加载。根据定义,这违反了委托模型。

然后是如何为什么

的问题

关于方式,窍门在DriverManager类中。

来自javadoc



 private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {

        // ...

        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;

                callerCL = Thread.currentThread().getContextClassLoader();



       // ...

        for(DriverInfo aDriver : registeredDrivers) {
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    // ...

                    Connection con = aDriver.driver.connect(url, info);

                   // ...
                        return con;
                    }

它尝试从当前线程(通常是系统类加载器)中获取类加载器。并使用它来加载数据库驱动程序实现并返回连接对象。

或者简单地说,父类加载器尝试使用子类加载器来加载某些类!根据父级委托,父类加载器甚至不应该知道子类加载器的存在。

然后是一个问题,为什么Sun / Oracle会这样做?

由两个不同的类加载器加载的类不能相互访问。因此,必须由相同的类加载器加载java.sql.Connection类及其实现。 然后我们可以 1)通过Bootstrap类加载器加载特定数据库供应商提供的java.sql.Connection及其实现类。 要么 2)通过系统类加载器加载它们。

这里第一个选项是不可能的,因为我们知道Bootstrap类加载器从rt.jar加载类。它不会从您的类路径中加载类。

第二种选择违反了“家长委托”模型,因此此处违反了该模型。这种突破仍然是有争议的,有人认为这是一个不好的设计。

相关问题