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(); } } }