diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderProvider.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderProvider.java index 7b7d3d2c8a..610870f29d 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderProvider.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderProvider.java @@ -419,6 +419,51 @@ public boolean unregister(ObjectReaderModule module) { return modules.remove(module); } + public void cleanUp(Class objectClass) { + mixInCache.remove(objectClass); + cache.remove(objectClass); + cacheFieldBased.remove(objectClass); + for (ConcurrentHashMap tlc : tclHashCaches.values()) { + for (Iterator> it = tlc.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + ObjectReader reader = entry.getValue(); + if (reader.getObjectClass() == objectClass) { + it.remove(); + } + } + } + } + + public void cleanUp(ClassLoader classLoader) { + for (Iterator> it = mixInCache.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + if (entry.getKey().getClassLoader() == classLoader) { + it.remove(); + } + } + + for (Iterator> it = cache.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + Type keyType = entry.getKey(); + Class keyClass = TypeUtils.getClass(keyType); + if (keyClass != null && keyClass.getClassLoader() == classLoader) { + it.remove(); + } + } + + for (Iterator> it = cacheFieldBased.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + Type keyType = entry.getKey(); + Class keyClass = TypeUtils.getClass(keyType); + if (keyClass != null && keyClass.getClassLoader() == classLoader) { + it.remove(); + } + } + + int tclHash = System.identityHashCode(classLoader); + tclHashCaches.remove(tclHash); + } + public ObjectReaderCreator getCreator() { ObjectReaderCreator contextCreator = JSONFactory.getContextReaderCreator(); if (contextCreator != null) { diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterProvider.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterProvider.java index d397a5e85f..f3cadbfbf7 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterProvider.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterProvider.java @@ -10,10 +10,7 @@ import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -274,4 +271,37 @@ public static boolean isNotReferenceDetect(final Class clazz) { return Arrays.binarySearch(NOT_REFERENCES_TYPE_HASH_CODES, System.identityHashCode(clazz)) >= 0 || ((clazz.getModifiers() & ENUM) != 0 && clazz.getSuperclass() == Enum.class); } + + public void cleanUp(Class objectClass) { + mixInCache.remove(objectClass); + cache.remove(objectClass); + cacheFieldBased.remove(objectClass); + } + + public void cleanUp(ClassLoader classLoader) { + for (Iterator> it = mixInCache.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + if (entry.getKey().getClassLoader() == classLoader) { + it.remove(); + } + } + + for (Iterator> it = cache.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + Type keyType = entry.getKey(); + Class keyClass = TypeUtils.getClass(keyType); + if (keyClass != null && keyClass.getClassLoader() == classLoader) { + it.remove(); + } + } + + for (Iterator> it = cacheFieldBased.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + Type keyType = entry.getKey(); + Class keyClass = TypeUtils.getClass(keyType); + if (keyClass != null && keyClass.getClassLoader() == classLoader) { + it.remove(); + } + } + } } diff --git a/core/src/test/java/com/alibaba/fastjson2/issues/Issue783.java b/core/src/test/java/com/alibaba/fastjson2/issues/Issue783.java new file mode 100644 index 0000000000..86f5a22284 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues/Issue783.java @@ -0,0 +1,99 @@ +package com.alibaba.fastjson2.issues; + +import com.alibaba.fastjson2.JSONFactory; +import com.alibaba.fastjson2.read.ClassLoaderTest; +import com.alibaba.fastjson2.reader.ObjectReader; +import com.alibaba.fastjson2.reader.ObjectReaderProvider; +import com.alibaba.fastjson2.writer.ObjectWriter; +import com.alibaba.fastjson2.writer.ObjectWriterProvider; +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; + +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; + +public class Issue783 { + @Test + public void test() { + ObjectWriterProvider writerProvider = JSONFactory.getDefaultObjectWriterProvider(); + ObjectWriter objectWriter = writerProvider.getObjectWriter(Bean.class); + ObjectWriter objectWriter1 = writerProvider.getObjectWriter(Bean.class); + assertSame(objectWriter, objectWriter1); + writerProvider.cleanUp(Bean.class); + ObjectWriter objectWriter2 = writerProvider.getObjectWriter(Bean.class); + assertNotSame(objectWriter, objectWriter2); + } + + @Test + public void test1() { + ObjectReaderProvider readerProvider = JSONFactory.getDefaultObjectReaderProvider(); + ObjectReader objectReader = readerProvider.getObjectReader(Bean.class); + ObjectReader objectReader1 = readerProvider.getObjectReader(Bean.class); + assertSame(objectReader, objectReader1); + + readerProvider.cleanUp(Bean.class); + ObjectReader objectReader2 = readerProvider.getObjectReader(Bean.class); + assertNotSame(objectReader, objectReader2); + } + + public static class Bean { + } + + @Test + public void test2() throws Exception { + ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader(); + Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo"); + + ObjectWriterProvider writerProvider = JSONFactory.getDefaultObjectWriterProvider(); + ObjectWriter objectWriter = writerProvider.getObjectWriter(objectClass); + ObjectWriter objectWriter1 = writerProvider.getObjectWriter(objectClass); + assertSame(objectWriter, objectWriter1); + + writerProvider.cleanUp(classLoader); + + ObjectWriter objectWriter2 = writerProvider.getObjectWriter(objectClass); + assertNotSame(objectWriter, objectWriter2); + } + + @Test + public void test3() throws Exception { + ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader(); + Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo"); + + ObjectReaderProvider readerProvider = JSONFactory.getDefaultObjectReaderProvider(); + ObjectReader objectReader = readerProvider.getObjectReader(objectClass); + ObjectReader objectReader1 = readerProvider.getObjectReader(objectClass); + assertSame(objectReader, objectReader1); + + readerProvider.cleanUp(classLoader); + ObjectReader objectReader2 = readerProvider.getObjectReader(objectClass); + assertNotSame(objectReader, objectReader2); + } + + public static class ExtClassLoader + extends ClassLoader { + public ExtClassLoader() throws IOException { + super(Thread.currentThread().getContextClassLoader()); + + { + byte[] bytes; + InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("external/Demo.clazz"); + bytes = IOUtils.toByteArray(is); + is.close(); + + super.defineClass("com.alibaba.mock.demo.api.Demo", bytes, 0, bytes.length); + } + { + byte[] bytes; + InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("external/MockDemoService.clazz"); + bytes = IOUtils.toByteArray(is); + is.close(); + + super.defineClass("com.alibaba.mock.demo.service.MockDemoService", bytes, 0, bytes.length); + } + } + } +}