參考:https://blog.csdn.net/justloveyou_/article/details/72217806java
tomcat類加載詳解:https://blog.csdn.net/justloveyou_/article/details/72231425web
1、文件類加載器spring
-
使用的類express
編譯java類爲class,移動到咱們想要放的目錄,刪除系統資源下的類,避免被appclassloader加載。apache
-
參考代碼:數組
- 待測試類:Sample.java
package com.example; public class Sample { private Sample instance; public void setSample(Object instance) { System.out.println(this.toString()); System.out.println(this.getClass().getClassLoader()); System.out.println(instance.toString()); System.out.println(instance.getClass().getClassLoader()); this.instance = (Sample) instance; } }
- 文件加載器 FileSystemClassLoader.java
package classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; //https://blog.csdn.net/justloveyou_/article/details/72217806 // 文件系統類加載器 public class FileSystemClassLoader extends ClassLoader { private String rootDir; public FileSystemClassLoader(String rootDir) { this.rootDir = rootDir; } // 獲取類的字節碼 @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] classData = getClassData(name); // 獲取類的字節數組 if (classData == null) { throw new ClassNotFoundException(); } else { return defineClass(name, classData, 0, classData.length); } } private byte[] getClassData(String className) { // 讀取類文件的字節 String path = classNameToPath(className); try { InputStream ins = new FileInputStream(path); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead = 0; // 讀取類文件的字節碼 while ((bytesNumRead = ins.read(buffer)) != -1) { baos.write(buffer, 0, bytesNumRead); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } private String classNameToPath(String className) { // 獲得類文件的徹底路徑 return rootDir + File.separatorChar + className.replace('.', File.separatorChar) + ".class"; } }
- 測試類:ClassIdentity.java
package classloader; import java.lang.reflect.Method; public class ClassIdentity { public static void main(String[] args) { new ClassIdentity().testClassIdentity(); } public void testClassIdentity() { String classDataRootPath = "C:\\Users\\JackZhou\\Documents\\NetBeansProjects\\classloader\\build\\classes"; classDataRootPath = "D:\\installdata\\indeaprojects\\demo\\src\\main\\java"; FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath); FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath); String className = "com.example.Sample"; try { Class<?> class1 = fscl1.loadClass(className); // 加載Sample類 Object obj1 = class1.newInstance(); // 建立對象 Class<?> class2 = fscl2.loadClass(className);//改了fscl2爲fscl1不報錯 Object obj2 = class2.newInstance(); Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class); setSampleMethod.invoke(obj1, obj2);//不是一個classloder加載的不能互轉,fscl2改成fscl1就不會報錯 } catch (Exception e) { e.printStackTrace(); } } }
com.example.Sample@6b884d57 classloader.FileSystemClassLoader@85ede7b com.example.Sample@38af3868 classloader.FileSystemClassLoader@63961c42 java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at classloader.ClassIdentity.testClassIdentity(ClassIdentity.java:23) at classloader.ClassIdentity.main(ClassIdentity.java:8) Caused by: java.lang.ClassCastException: com.example.Sample cannot be cast to com.example.Sample at com.example.Sample.setSample(Sample.java:12) ... 6 more
- 若是改成同一個類加載器,發現類實例對象不同,能夠互轉
com.example.Sample@65b54208 classloader.FileSystemClassLoader@85ede7b com.example.Sample@1be6f5c3 classloader.FileSystemClassLoader@85ede7b
2、網絡類加載器tomcat
-
使用的類springboot
我使用的容器是tomcat,框架是springboot2,maven3.6, jdk1.8 構建工程,提供服務網絡
-
代碼:app
個人資源路徑(file:D:/installdata/indeaprojects/demo/target/classes/)
- WebConfig.java (對外提供本地資源)
package com.config; import org.springframework.boot.web.servlet.MultipartConfigFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.unit.DataSize; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.MultipartConfigElement; /** * description: //攔截器 * https://blog.csdn.net/allen_lu_hh/article/details/83218028 * @author: heliming * @date:2019/04/14 下午 6:04 */ @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/classes/**").addResourceLocations("file:D:/installdata/indeaprojects/demo/target/classes/"); } /** * 設置單個文件上傳大小限制爲10M(拍照上傳頭像文件超限) */ @Bean public MultipartConfigElement getMultipartConfigElement() { MultipartConfigFactory factory = new MultipartConfigFactory(); factory.setMaxFileSize(DataSize.ofBytes(10 * 1024 * 1024)); return factory.createMultipartConfig(); } }
-
客戶端使用的接口
- ICalculator.java
package classloader; public interface ICalculator extends Versioned { String calculate(String expression); }
- Versioned.java
package classloader; public interface Versioned { String getVersion(); }
-
服務端對接口實現(須要引用客戶端的接口,jar包方式或者class文件方式)
- CalculatorAdvanced.java
package com.example; import classloader.ICalculator; public class CalculatorAdvanced implements ICalculator { @Override public String calculate(String expression) { return "Result is " + expression; } @Override public String getVersion() { return "2.0"; } }
- CalculatorBasic.java
package com.example; import classloader.ICalculator; public class CalculatorBasic implements ICalculator { @Override public String calculate(String expression) { return expression; } @Override public String getVersion() { return "1.0"; } }
- HangxinApplication.java(服務端啓動類)
package com; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class HangxinApplication { public static void main(String[] args) { SpringApplication.run(HangxinApplication.class, args); } }
- application.yml
server: port: 8888
- pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
-
客戶端代碼:
- 啓動類CalculatorTest.java
package classloader; public class CalculatorTest { public static void main(String[] args) { String url = "http://10.0.21.168:8888/classes"; NetworkClassLoader ncl = new NetworkClassLoader(url); String basicClassName = "com.example.CalculatorBasic"; String advancedClassName = "com.example.CalculatorAdvanced"; try { Class<?> clazz = ncl.loadClass(basicClassName); // 加載一個版本的類 ICalculator calculator = (ICalculator) clazz.newInstance(); // 建立對象 System.out.println(calculator.getVersion()); clazz = ncl.loadClass(advancedClassName); // 加載另外一個版本的類 calculator = (ICalculator) clazz.newInstance(); System.out.println(calculator.getVersion()); } catch (Exception e) { e.printStackTrace(); } } }
- 接口1 ICalculator.java
package classloader; public interface ICalculator extends Versioned { String calculate(String expression); }
- 接口2 Versioned.java
package classloader; public interface Versioned { String getVersion(); }
- 網絡類加載器 NetworkClassLoader.java
package classloader; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.URL; public class NetworkClassLoader extends ClassLoader { private String rootUrl; public NetworkClassLoader(String rootUrl) { // 指定URL this.rootUrl = rootUrl; } // 獲取類的字節碼 @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); } else { return defineClass(name, classData, 0, classData.length); } } private byte[] getClassData(String className) { // 從網絡上讀取的類的字節 String path = classNameToPath(className); System.out.println(path); try { URL url = new URL(path); InputStream ins = url.openStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead = 0; // 讀取類文件的字節 while ((bytesNumRead = ins.read(buffer)) != -1) { baos.write(buffer, 0, bytesNumRead); } return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return null; } private String classNameToPath(String className) { // 獲得類文件的URL return rootUrl + "/" + className.replace('.', '/') + ".class"; } }
-
測試代碼:啓動服務端的springboot,而後啓動客戶端,發現客戶端能夠加載服務端的代碼。
http://10.0.21.168:8888/classes/com/example/CalculatorBasic.class 1.0 http://10.0.21.168:8888/classes/com/example/CalculatorAdvanced.class 2.0