首页 > 编程知识 正文

双亲委派机制优缺点,双亲委派机制面试题

时间:2023-05-04 18:53:31 阅读:15525 作者:1234

标题父母委托机制? 好处:父母委托机制有什么缺陷? 详细情况如何打破父母的委托机制? 使用上下文加载程序?

父母委托机制?

参考:

3359砖局域网. zhi Hu.com/p/185612299

33559 www.Jian Shu.com/p/09 f 73 af 48 a 98

最终加载任务是顶层BootstrapClassLoader,因为当:类加载器收到类加载任务时,其父母委托机制时缺省用于加载JVM类的机制将首先传递给其父加载器类加载器按从父代到子集的顺序主要包括以下内容:

BootstrapClassLoader (启动类加载器) :主要位于核心类库(如java.lang.* )、JVM_HOME/lib目录下的ExtClassLoader ext class loader :负责将位于由Java _ runtime _ home/lib/ext或java.ext.dir系统变量指定的位置的类库加载到内存中。 开发人员可以直接使用标准的扩展类加载器。 appclassloader:主要负责加载APP应用程序的主函数类,并将由“系统类路径”(CLASSPATH )指定的类库加载到内存中。 开发人员可以直接使用系统类加载器。 自定义类加载器:主要加载APP应用程序的主要函数类的优点。 一种方法可以避免类的重复加载,也可以避免篡改java核心API。

父母委托机制有什么缺陷? 限制是父加载器无法加载子类加载器路径中的类。 如果基础类调用了用户类,该怎么办?

典型地,这引出了对父母委托机制缺陷的讨论。 接口:在java.sql.Driver和java.sql包中定义。 包位于jdkjrelibrt.jar中,java.sql包还提供了其他相应的类。接口实现类com.mysql.jdbc.Driver由第三方提供问题是,驱动程序管理器在重新获取链接时总是加载到com.mysql.jdbc.Driver类中

简单来说,启动类加载程序有一种获取APP应用程序类加载程序的方法。 其方法为http://www.Sina.com/http://www.Sina.com /

补充说明: java本身包含一组资源管理服务JNDI,位于rt.jar中,由启动类加载器加载。 JNDI是Java命名目录接口(javanaminganddirectoryinterface ),简单地说是线程上下文类加载器

有关详细信息,请加载//1 .数据访问class.forname (com.MySQL.JDBC.driver ); //2 .连接到数据'库'连接connection conn=driver manager.getconnection (' JDBC : MySQL ://localhost 33603306/mydb drdxx=GBK '、' root '、''); 核心是这个Class.forName ()触发了mysql驱动的加载。 让我们看看mysql驱动程序接口的实现。

publicclassdriverextendsnonregisteringdriverimplementsjava.SQL.driver { public driver } throws sqlexception { }静态{状态驱动程序(); 可以看到Class.forName ()实际上启动了静态代码块,并在驱动程序管理器中注册了mysql的驱动程序实现。

此时,在驱动程序管理器中获取连接时,只要选择遍历当前的所有驱动程序实现并建立连接即可。

驱动程序接口由java提供生产数据库,具体实现由供应商实现。 驱动程序管理器类管理此驱动程序的实现。

公共英特尔

rface Driver { Connection connect(String url, java.util.Properties info) throws SQLException; boolean acceptsURL(String url) throws SQLException; DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException; int getMajorVersion(); int getMinorVersion(); boolean jdbcCompliant(); public Logger getParentLogger() throws SQLFeatureNotSupportedException;} public class DriverManager { // List of registered JDBC drivers 这里用来保存所有Driver的具体实现 private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { registerDriver(driver, null); } public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException { /* Register the driver if it has not already been added to our list */ if(driver != null) { registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); } else { // This is for compatibility with the original DriverManager throw new NullPointerException(); } println("registerDriver: " + driver); } } 如何打破双亲委派机制?

在JDBC4.0以后,开始支持使用spi的方式来注册这个Driver,具体做法就是在mysql的jar包中的META-INF/services/java.sql.Driver 文件中指明当前使用的Driver是哪个,然后使用的时候就直接这样就可以了:

Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?drdxx=GBK", "root", "");

可以看到这里直接获取连接,省去了上面的Class.forName()注册过程。
现在,我们分析下看使用了这种spi服务的模式原本的过程是怎样的:

第一,从META-INF/services/java.sql.Driver文件中获取具体的实现类名“com.mysql.jdbc.Driver”第二,加载这个类,这里肯定只能用class.forName(“com.mysql.jdbc.Driver”)来加载

好了,问题来了,Class.forName()加载用的是调用者的Classloader,这个调用者DriverManager是在rt.jar中的,ClassLoader是启动类加载器,而com.mysql.jdbc.Driver肯定不在<JAVA_HOME>/lib下,所以肯定是无法加载mysql中的这个类的。这就是双亲委派模型的局限性了,父级加载器无法加载子级类加载器路径中的类。

那么,这个问题如何解决呢?按照目前情况来分析,这个mysql的drvier只有应用类加载器能加载,那么我们只要在启动类加载器中有方法获取应用程序类加载器,然后通过它去加载就可以了。这就是所谓的线程上下文加载器。
线程上下文类加载器可以通过Thread.setContextClassLoaser()方法设置,如果不特殊设置会从父类继承,一般默认使用的是应用程序类加载器

很明显,线程上下文类加载器让父级类加载器能通过调用子级类加载器来加载类,这打破了双亲委派模型的原则

上下文类加载器的使用?

现在我们看下DriverManager是如何使用线程上下文类加载器去加载第三方jar包中的Driver类的。

public class DriverManager { static { loadInitialDrivers(); println("JDBC DriverManager initialized"); } private static void loadInitialDrivers() { //省略代码 //这里就是查找各个sql厂商在自己的jar包中通过spi注册的驱动 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); try{ while(driversIterator.hasNext()) { driversIterator.next(); } } catch(Throwable t) { // Do nothing } //省略代码 }}

使用时,我们直接调用DriverManager.getConn()方法自然会触发静态代码块的执行,开始加载驱动
然后我们看下ServiceLoader.load()的具体实现:

public static <S> ServiceLoader<S> load(Class<S> service) { //拿到线程上下文类加载器,然后构造了一个ServiceLoader,后续的具体查找过程 ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); } public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader){ return new ServiceLoader<>(service, loader); }

接下来,DriverManager的loadInitialDrivers()方法中有一句driversIterator.next();,它的具体实现如下:

private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class<?> c = null; try { //此处的cn就是产商在META-INF/services/java.sql.Driver文件中注册的Driver具体实现类的名称 //此处的loader就是之前构造ServiceLoader时传进去的线程上下文类加载器 c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } //省略部分代码 }

​ 现在,我们成功的做到了通过线程上下文类加载器拿到了应用程序类加载器(或者自定义的然后塞到线程上下文中的),同时我们也查找到了厂商在子级的jar包中注册的驱动具体实现类名,这样我们就可以成功的在rt.jar包中的DriverManager中成功的加载了放在第三方应用程序包中的类了。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。