The problem found

2022-01-21 at 9:00 a.m., a large area of “system unknown error” was reported in the order system, leading to some users unable to place orders normally. If you query background logs, you can find a large number of duplicate class Attempts.

java.lang.LinkageError-->loader (instance of org/springframework/boot/loader/LaunchedURLClassLoader): attempted duplicate class definition for name: "com/order/vo/OrderAndExtendVO$$BeanMapByCGLIB$$e8178b2a" StackTrace: org.springframework.cglib.core.CodeGenerationException: java.lang.LinkageError-->loader (instance of org/springframework/boot/loader/LaunchedURLClassLoader): attempted duplicate class definition for name: "com/order/vo/OrderAndExtendVO$$BeanMapByCGLIB$$e8178b2a" at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:538) at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:110) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:108) at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:134) at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319) at org.springframework.cglib.beans.BeanMap$Generator.create(BeanMap.java:127) at Org. Springframework. Additional. Beans. BeanMap. Create (BeanMap. Java: 59)... omit other stackCopy the code

Problem analysis

Cglib is trying to generate a class that already exists.

This code calls the beanmap.create (Object) method, which generates the dynamic proxy class. We directly into the AbstractClassGenerator. Create (Object) of the source code, you can see, in the global cache will not generate again, in theory, the proxy class will not repeat, do the cache invalidation?

At first I suspected that the cache was disabled. . But, only through the useCache field AbstractClassGenerator setUseCache Settings (Boolean) method, and the whole project and no place is reference to this method, so this assumption is not established.

abstract public class AbstractClassGenerator<T> implements ClassGenerator {
    // Whether to use cache
    private boolean useCache = true;
    private static final Object NAME_KEY = new Object();
    // Cache is global
    private static final Source SOURCE = new Source(BeanMap.class.getName());
    protected static class Source {
        String name;
        /* * Global cache, format: * 
      
 * { * "classLoader1":{ * NAME_KEY:["className1","className2"], * "className1":class1, * "className2":class2 * }, * "classLoader2":{ * NAME_KEY:["className2","className3"], * "className3":class3, * "className2":class2 *} *} * zzs001 * 

* @author ZZS * @date 24 Jan 2022 9:51:41 am * @param args void */
Map cache = new WeakHashMap();
public Source(String name) {
this.name = name; }}protected Object create(Object key) {
try {
Class gen = null;

synchronized (source) {
ClassLoader loader = getClassLoader();
Map cache2 = null;
cache2 = (Map)source.cache.get(loader);
if (cache2 == null) {
cache2 = new HashMap();
cache2.put(NAME_KEY, new HashSet());
source.cache.put(loader, cache2);
} else if (useCache) {
Reference ref = (Reference)cache2.get(key);
gen = (Class) (( ref == null)?null : ref.get());
}
if (gen == null) {
Object save = CURRENT.get();
CURRENT.set(this);
try {
this.key = key;

if (attemptLoad) {
try {
gen = loader.loadClass(getClassName());
} catch (ClassNotFoundException e) {
// ignore}}if (gen == null) {
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
getClassNameCache(loader).add(className);
gen = ReflectUtils.defineClass(className, b, loader);
}

if (useCache) {
cache2.put(key, new WeakReference(gen));
}
return firstInstance(gen);
} finally{ CURRENT.set(save); }}}return firstInstance(gen);
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw newCodeGenerationException(e); }}}Copy the code

It occurred to me that this cglib was just spring embedded Cglib, not “real Cglib.” Caching only works on the current cglib. If the native Cglib were to create the same class, would an error be reported?

By looking at the references, the project does have a case where different CgliBs are used to create the same class:

Then I tried the code and found the same error:

Problem solving

Duplicate class attempt is generated by using two different cglibs: Spring and native cglibs.

Knowing the cause, the solution is very simple. Just change the cGLIb package to the same one.

After repair, there is no such error in production, which basically proves that we are right.

conclusion

The above is the processing process of this production error. Here I have a few questions:

  1. Cglib determines if a class exists. Why not just check the class in the project? Using cache, which is unreliable?
  2. Why doesn’t Spring rely on Cglib directly? You want to embed one yourself?

Finally, thanks for reading, welcome to exchange, correction.

This article original articles, reproduced please attach the original source link: www.cnblogs.com/ZhangZiShen…