JVM advanced custom class loader

Keywords: Java jvm

1. Function

  • Isolated load class

    In some frameworks, middleware is isolated from application modules, and classes are loaded into different environments.

  • Modify how classes are loaded

    The loading model of class is not mandatory. It should be loaded dynamically at a certain point in time according to the actual situation.

  • Extended load source

    It can be loaded from database, network, or even TV set-top box.

  • Prevent source code leakage

    You can encrypt the Java source code at compile time and decrypt it at restore time.

2. Scenario

  • When implementing similar in-process isolation effects, class loaders can be used as different namespaces to provide container like and modular effects.
  • When an application needs to obtain class definition information from different data sources, such as network data sources and manipulating bytecode to dynamically modify or generate classes.

3. Attention

  • In general, using different class loaders to load different functional modules will improve the security of applications.
  • However, if mutual conversion between Java types is involved, type conversion can only be performed if both types are loaded by the same class loader, otherwise an exception will occur during conversion.

4. Realization

  • There are two common methods: overriding the loadClass method and overriding the findClass method.
  • The two methods are essentially the same. After all, the findClass method will also be called in the loadClass method. However, logically speaking, we'd better not directly modify the internal logic in the loadClass method, because this method is the place to implement the logic of the parent delegation model. Modifying this method without authorization will lead to the destruction of the model and easy to cause problems. Therefore, the second method is recommended.

Code example

public class MyClassLoader extends ClassLoader {
    private String byteCodePath;

    public MyClassLoader(String byteCodePath) {
        this.byteCodePath = byteCodePath;
    }

    public MyClassLoader(ClassLoader parent, String byteCodePath) {
        super(parent);
        this.byteCodePath = byteCodePath;
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        BufferedInputStream bis = null;
        ByteArrayOutputStream baos = null;
        
        try {
            // Get the path of bytecode file
            String fileName = byteCodePath + className + ".class";
            
            // Get file input stream
            bis = new BufferedInputStream(new FileInputStream(fileName));
            
            // Get byte output stream
            baos = new ByteArrayOutputStream();
            
            // Writes a bytecode file to the output stream
            int len;
            byte[] data = new byte[1024];
            while ((len = bis.read(data)) != -1) {
                baos.write(data, 0, len);
            }
            
            // Convert byte output stream to byte array
            byte[] byteCodes = baos.toByteArray();
			
            // Call the defineClass method to complete the creation of class instances
            Class clazz = defineClass(null, byteCodes, 0, byteCodes.length);
            
            return clazz;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (baos != null) baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bis != null) bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }
}
public class MyClassLoaderTest {
    public static void main(String[] args) {
        MyClassLoader loader = new MyClassLoader("Root path of bytecode file");

        try {
            Class clazz = loader.loadClass("Demo");
            
            System.out.println("The class loaders that load this class are:" + 
                               clazz.getClassLoader().getClass().getName());

            System.out.println("The parent loader that loads this class is:" + 
                               clazz.getClassLoader().getParent().getClass().getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Posted by findapollo on Tue, 16 Nov 2021 20:43:48 -0800