有些书说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中也没有发现破坏,我不知道代码显示破坏的位置。
请告诉我具体的代码,谢谢。
答案 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加载类。它不会从您的类路径中加载类。
第二种选择违反了“家长委托”模型,因此此处违反了该模型。这种突破仍然是有争议的,有人认为这是一个不好的设计。