About Java class loader of this, the market no one talked about books

First, think of a programmer

As we all know, Tomcat processing business, rely on? Ultimately rely on our own Servlet written. You may say you do not write servlet, you use spring MVC, that is someone to help you write, you only need to configure on the line. Here, there is a border, Tomcat container operators, relevant jar package containers are placed in its own installation directory lib below; we do, be regarded as business, regarded webapp, our servlet, either custom or spring mvc the DispatcherServlet, are placed under our war package inside WEB-INF / lib. Read the previous article, it is to know the students, both by different class loaders to load. In the implementation of Tomcat, commissioned webappclassloader to load the servlet in the WAR, and then generate a corresponding reflected servlet. There the subsequent request, the servlet generates a call to service method.

In org.apache.catalina.core.StandardWrapper # loadServlet, that is responsible for generating servlet:

About Java class loader of this, the market no one talked about books

org.apache.catalina.core.DefaultInstanceManager#newInstance(java.lang.String)
    @Override
    public Object newInstance(String className) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, ClassNotFoundException {
        Class<?> clazz = loadClassMaybePrivileged(className, classLoader);
        return newInstance(clazz.newInstance(), clazz);
    }

In the figure, we will use to generate instanceManager servlet instance according to the parameters specified servletClass. newInstance code below, mainly with the current context of the servlet classloader to load, and generate a reflected servlet objects.

Our focus is on the red box turns out strong turn: Why object consists webappclassloader loaded, can be converted to Tomcat common Servlet classloader to load it? It stands to reason, two different class loaders to load classes are isolated from each other, ah, you should not throw a ClassCastException it? Really, I turned a lot of books, never mention this, even the Internet is also very vague.

One more question about the SPI. In SPI, the main specification is designated by the java community, such as JDBC, there are so many manufacturers, mysql, oracle, postgre, everyone has their own jar package, if there is no JDBC specification, we have estimated that the implementation class program for all manufacturers , that migration is in trouble, the code you write for the mysql database into oracle words, the code does not change is certainly not running. So, JCP organizations to develop the JDBC specification, JDBC specification specifies a bunch of interfaces, we usually develop, only for the interface programming, realized how to do, to chant the manufacturers, from manufacturers to achieve the JDBC specification. In this example the code, the java.sql.Driver oracle.jdbc.OracleDriver achieved, while in the static initializer block oracle.jdbc.OracleDriver, have the following code:

    static {
        try {
            if (defaultDriver == null) {
                defaultDriver = new oracle.jdbc.OracleDriver();
                DriverManager.registerDriver(defaultDriver);
            }
    // 省略
    }

Among them, the red sentence, Oracle Driver is JDBC interface To register yourself, realize java.sql.DriverManager # registerDriver (java.sql.Driver) as follows:

java.sql.DriverManager#registerDriver(java.sql.Driver) 

public static synchronized void registerDriver(java.sql.Driver driver)
        throws SQLException {

        registerDriver(driver, null);
    }

Can be seen, the parameter registerDriver (java.sql.Driver) Method for the java.sql.Driver, we pass parameters oracle.jdbc.OracleDriver type, two types, is loaded by different class loaders (java .sql.Driver jdk start classes loaded by the loader, and oracle.jdbc.OracleDriver, if a web application, for the webappclassloader tomcat to load, anyway, anyway), so that the two types are not loaded by the jdk even the class loader is not the same, how it will be able to convert normal, why not throw ClassCastException?

Second, different classes loaded by class, the key that can be converted

After observing two examples above, I do not know if you have not found, we are to achieve a conversion as an interface. Perhaps this is the crux of the problem. We can boldly speculated, based on parents appointed mechanisms and the like, when loading implementation class, jvm encountered in the implementation class references to other classes, will trigger loading, the loading process will trigger loadClass, for example, load webappclassloader when loading oracle.jdbc.OracleDriver, triggering load java.sql.Driver, but obviously can not webappclassloader java.sql.Driver to load, so it is entrusted to jdk class loader, so, eventually, oracle.jdbc.OracleDriver in cited java.sql.Driver, in fact, by the class loader to load the jdk. The parameter type java.sql.Driver driver registerDriver (java.sql.Driver driver) is also made of the class loader to load the jdk, both the same, it is naturally interchangeable.

They came up with a (not necessarily), in the case of simultaneously meet the following conditions:

  • 1 pre-conditions, the interface jar package, define an interface Test

  • 2 precondition to realize jar package, defined Test class implementations, such TestImpl. (But not included in the class of the interface, you can not say compile it into the jar package interfaces classpath)

  • 3 pre-conditions, the interface is loaded by interface_classLoader jar package, to achieve loaded by impl_classloader jar package, which will impl_classloader when they can not be loaded, delegated to interface_classLoader

Is defined in achieving Test interface implementation class of the jar, to generate the reflection object, it can be converted to Test Type.

Having guess, is confirmation process.

Third, verify

1, define the interface jar
D:\classloader_interface\ITestSample.java  

/**
 * desc:
 *
 * @author : 
 * creat_date: 2019/6/16 0016
 * creat_time: 19:28
 **/
public interface ITestSample {
}

Under cmd, execute:

D:\classloader_interface>javac ITestSample.java
D:\classloader_interface>jar cvf interface.jar ITestSample.class
已添加清单
正在添加: ITestSample.class(输入 = 103) (输出 = 86)(压缩了 16%)

At this time, in the current directory to generate an interface package called interface.jar the jar.

2, implement the interface definition jar

In different directory, create an implementation class.

D:\classloader_impl\TestSampleImpl.java

/**
 * Created by Administrator on 2019/6/25.
 */
public class TestSampleImpl implements  ITestSample{

}

Compile, package:

D:\classloader_impl>javac -cp D:\classloader_interface\interface.jar TestSampleI
mpl.java

D:\classloader_impl>jar -cvf impl.jar TestSampleImpl.class
已添加清单
正在添加: TestSampleImpl.class(输入 = 221) (输出 = 176)(压缩了 20%)

Please note that the above marked red line, but without the compiler.

3, the test

Testing idea is to load with a urlclassloader interface.jar in ITestSample, with another URLClassLoader impl.jar to load the TestSampleImpl, and then determines whether the latter java.lang.Class # isAssignableFrom able to turn into the former.

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * desc:
 *
 * @author : caokunliang
 * creat_date: 2019/6/14 0014
 * creat_time: 17:04
 **/
public class MainTest {

    public static void testInterfaceByOneAndImplByAnother()throws Exception{
        URL url = new URL("file:D:\\classloader_interface\\interface.jar");
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
        Class<?> iTestSampleClass = urlClassLoader.loadClass("ITestSample");

        URL implUrl = new URL("file:D:\\classloader_impl\\impl.jar");
        URLClassLoader implUrlClassLoader = new URLClassLoader(new URL[]{implUrl}, urlClassLoader);
        Class<?> testSampleImplClass = implUrlClassLoader.loadClass("TestSampleImpl");

        System.out.println("实现类能转否?:"  + iTestSampleClass.isAssignableFrom(testSampleImplClass));

    }

    public static void main(String[] args) throws Exception {
        testInterfaceByOneAndImplByAnother();
    }

}

Printing is as follows:

About Java class loader of this, the market no one talked about books

4, extending Test 1

If we make the following changes, you guess what will happen? The main difference here is that:

Before reform, urlClassloader as parentClassloader:

  URLClassLoader implUrlClassLoader = new URLClassLoader(new URL[]{implUrl}, urlClassLoader);

After the change, does not pass, the default application will jdk class loader as parent:

  URLClassLoader implUrlClassLoader = new URLClassLoader(new URL[]{implUrl});

Print result:

Exception in thread "main" java.lang.NoClassDefFoundError: ITestSample
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:455)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:367)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at MainTest.testInterfaceByOneAndImplByAnother(MainTest.java:23)
    at MainTest.main(MainTest.java:33)
Caused by: java.lang.ClassNotFoundException: ITestSample
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 13 more

As a result, line 23, Class testSampleImplClass = implUrlClassLoader.loadClass ( "TestSampleImpl") <?>; Here the error, suggesting no ITestSample.

This is because, after loading the implUrlClassLoader, triggered implicitly loaded on ITestSample. This implicitly loaded will which loader used to load it, there is no default under specified circumstances, is to use the current class loader, and the current class loader is implUrlClassLoader, but the class loader to begin loading ITestSample, it is to follow the parent delegation, its parent loader is the appclassloader, (jdk default application class loader), but appclassloader can not be loaded ITestSample, then still also to implUrlClassLoader, but implUrlClassLoader can not be loaded, then an exception is thrown.

5, Extension Test 2

We do a change, and a change at the same test, but this time, we pass a special class loader, as parentClassLoader. It is special is that, almostSameUrlClassLoader and front loading interface.jar class loader exactly the same, but is a new instance.

        URLClassLoader almostSameUrlClassLoader = new URLClassLoader(new URL[]{url});
        URLClassLoader implUrlClassLoader = new URLClassLoader(new URL[]{implUrl}, almostSameUrlClassLoader);

This time, look at the results, maybe you guessed it?

About Java class loader of this, the market no one talked about books

The error did not, after all, almostSameUrlClassLoader know where to load ITestSample, however, the final results show that the class implementation class does not turn into ITestSample.

6, Extension Test 3

To be honest, some students may isAssignableFrom not very familiar with the java.lang.Class #, we change the more you are not familiar with, how?

        URL implUrl = new URL("file:D:\\classloader_impl\\impl.jar");
        URLClassLoader almostSameUrlClassLoader = new URLClassLoader(new URL[]{url});
        URLClassLoader implUrlClassLoader = new URLClassLoader(new URL[]{implUrl}, almostSameUrlClassLoader);
        Class<?> testSampleImplClass = implUrlClassLoader.loadClass("TestSampleImpl");
        Object o = testSampleImplClass.newInstance();
        Object cast = iTestSampleClass.cast(o); // 将 o 转成 接口的那个类
        System.out.println(cast);

result:

About Java class loader of this, the market no one talked about books

If you replace this, which lacks the problem:

        URL implUrl = new URL("file:D:\\classloader_impl\\impl.jar");
        URLClassLoader almostSameUrlClassLoader = new URLClassLoader(new URL[]{url});
        URLClassLoader implUrlClassLoader = new URLClassLoader(new URL[]{implUrl}, urlClassLoader);
        Class<?> testSampleImplClass = implUrlClassLoader.loadClass("TestSampleImpl");
        Object o = testSampleImplClass.newInstance();
        Object cast = iTestSampleClass.cast(o);
        System.out.println(cast);

carried out:

About Java class loader of this, the market no one talked about books

Guess you like

Origin blog.51cto.com/14295088/2413630