Una línea de registros de registro, lo que resulta en un accidente en línea de P1

imagen

Fuente: juejin.cn/post/7156439842958606349

  • Revisión de incidentes en línea

  • Restauración de escenarios

  • Análisis de código fuente

    • Principio de serialización de JavaBeanSerizlier

    • Diagrama de flujo de serialización

  • Código de muestra

  • especificación de código

  • Tres casos de serialización de alta frecuencia


Revisión de incidentes en línea

Hace algún tiempo, un colega agregó una función muy simple. Cuando se lanzó el código por la noche, reviewpensó en los valores emprendedores y trabajadores de la empresa. Agregó temporalmente una línea de registros de registro. Pensó que básicamente no había ningún problema con solo una línea de registro simple. Llame a la policía, revierta el código rápidamente, encuentre el problema y elimine el código para agregar registros, y vuelva a conectarse.

Un blog separado de front-end y back-end basado en Spring Boot + MyBatis Plus + Vue 3.2 + Vite + Element Plus, que incluye un sistema de gestión en segundo plano que admite funciones como artículos, categorías, gestión de etiquetas y paneles.

  • Dirección de GitHub: https://github.com/weiwosuoai/WeBlog

  • Dirección de la casa rural: https://gitee.com/AllenJiang/WeBlog

 
 

Restauración de escenarios

define un CountryDTO

public class CountryDTO {
    private String country;

    public void setCountry(String country) {
        this.country = country;
    }

    public String getCountry() {
        return this.country;
    }

    public Boolean isChinaName() {
        return this.country.equals("中国");
    }
}

Definir la clase de prueba FastJonTest

public class FastJonTest {
    @Test
    public void testSerialize() {
        CountryDTO countryDTO = new CountryDTO();
        String str = JSON.toJSONString(countryDTO);
        System.out.println(str);
    }
}

Error al ejecutar 空指针:

imagen

puntero nulo

Del mensaje de error se puede ver que el método fue ejecutado durante el proceso de serialización isChinaName(), en este momento this.countryla variable está vacía, por lo que surge el problema:

  • ¿ Por qué se realiza la serialización isChinaName()?

  • Por extensión, ¿qué métodos se ejecutarán durante el proceso de serialización?

Análisis de código fuente

Observe la información de la pila del enlace de la llamada a través de la depuración

imagen

imagen

imagen

imagen

En la cadena de llamadas, una clase ASMSerializer_1_CountryDTO.writese genera dinámicamente FastJsonusando tecnología .asmASMSerializer_1_CountryDTO

Uno de los escenarios de uso de la tecnología asm es generar dinámicamente clases para reemplazar javala reflexión, a fin de evitar la sobrecarga de reflexión durante la ejecución repetida.

Principio de serialización de JavaBeanSerizlier

En la siguiente figura se puede ver que en el proceso de serialización, el método JavaBeanSerializerde la clase se llama principalmente write().

imagen

Clase de implementación de ObjectSerializer JavaBeanSerializer

Se JavaBeanSerializerobtiene principalmente a través de getObjectWriter()métodos, mediante getObjectWriter()la depuración del proceso de ejecución, para encontrar los com.alibaba.fastjson.serializer.SerializeConfig#createJavaBeanSerializermétodos más críticos y luego para encontrar com.alibaba.fastjson.util.TypeUtils#computeGetters

public static List<FieldInfo> computeGetters(Class<?> clazz, //
                                                 JSONType jsonType, //
                                                 Map<String,String> aliasMap, //
                                                 Map<String,Field> fieldCacheMap, //
                                                 boolean sorted, //
                                                 PropertyNamingStrategy propertyNamingStrategy //
    ){
    //省略部分代码....
    Method[] methods = clazz.getMethods();
    for(Method method : methods){
        //省略部分代码...
        if(method.getReturnType().equals(Void.TYPE)){
            continue;
        }
        if(method.getParameterTypes().length != 0){
            continue;
        }
            //省略部分代码...
        JSONField annotation = TypeUtils.getAnnotation(method, JSONField.class);
        //省略部分代码...
        if(annotation != null){
            if(!annotation.serialize()){
                continue;
            }
            if(annotation.name().length() != 0){
                //省略部分代码...
            }
        }
        if(methodName.startsWith("get")){
         //省略部分代码...
        }
        if(methodName.startsWith("is")){
         //省略部分代码...
        }
    }
}

A partir del código, se puede dividir aproximadamente en tres situaciones:

  • @JSONField(.serialize = false, name = "xxx")anotación

  • getXxx() : El método que comienza con get

  • isXxx(): método que comienza con is

Diagrama de flujo de serialización

imagen

Diagrama de flujo de serialización

Un blog separado de front-end y back-end basado en Spring Boot + MyBatis Plus + Vue 3.2 + Vite + Element Plus, que incluye un sistema de gestión en segundo plano que admite funciones como artículos, categorías, gestión de etiquetas y paneles.

  • Dirección de GitHub: https://github.com/weiwosuoai/WeBlog

  • Dirección de la casa rural: https://gitee.com/AllenJiang/WeBlog

 
 

Código de muestra

/**
 * case1: @JSONField(serialize = false)
 * case2: getXxx()返回值为void
 * case3: isXxx()返回值不等于布尔类型
 * case4: @JSONType(ignores = "xxx")
 */
@JSONType(ignores = "otherName")
public class CountryDTO {
    private String country;

    public void setCountry(String country) {
        this.country = country;
    }

    public String getCountry() {
        return this.country;
    }

    public static void queryCountryList() {
        System.out.println("queryCountryList()执行!!");
    }

    public Boolean isChinaName() {
        System.out.println("isChinaName()执行!!");
        return true;
    }

    public String getEnglishName() {
        System.out.println("getEnglishName()执行!!");
        return "lucy";
    }

    public String getOtherName() {
        System.out.println("getOtherName()执行!!");
        return "lucy";
    }

    /**
     * case1: @JSONField(serialize = false)
     */
    @JSONField(serialize = false)
    public String getEnglishName2() {
        System.out.println("getEnglishName2()执行!!");
        return "lucy";
    }

    /**
     * case2: getXxx()返回值为void
     */
    public void getEnglishName3() {
        System.out.println("getEnglishName3()执行!!");
    }

    /**
     * case3: isXxx()返回值不等于布尔类型
     */
    public String isChinaName2() {
        System.out.println("isChinaName2()执行!!");
        return "isChinaName2";
    }
}

El resultado de la operación es:

isChinaName()执行!!
getEnglishName()执行!!
{"chinaName":true,"englishName":"lucy"}

especificación de código

Se puede ver que todavía hay muchas reglas de serialización. Por ejemplo, a veces debe prestar atención al valor de retorno, a veces debe prestar atención a la cantidad de parámetros, a veces debe prestar atención a las anotaciones y, a veces @JSONType, debe prestar atención @JSONFielda las anotaciones; el grado de dominio de los puntos de conocimiento es diferente, y esta variación puede conducir fácilmente a problemas de código, así que trate de tener una solución recomendada.

Se recomienda usar @JSONField(serialize = false)para marcar explícitamente los métodos que no participan en la serialización. El siguiente es @JSONFieldel código después de usar anotaciones. ¿Puede ver de un vistazo qué métodos no necesitan participar en la serialización?

public class CountryDTO {
    private String country;

    public void setCountry(String country) {
        this.country = country;
    }

    public String getCountry() {
        return this.country;
    }

    @JSONField(serialize = false)
    public static void queryCountryList() {
        System.out.println("queryCountryList()执行!!");
    }

    public Boolean isChinaName() {
        System.out.println("isChinaName()执行!!");
        return true;
    }

    public String getEnglishName() {
        System.out.println("getEnglishName()执行!!");
        return "lucy";
    }

    @JSONField(serialize = false)
    public String getOtherName() {
        System.out.println("getOtherName()执行!!");
        return "lucy";
    }

    @JSONField(serialize = false)
    public String getEnglishName2() {
        System.out.println("getEnglishName2()执行!!");
        return "lucy";
    }

    @JSONField(serialize = false)
    public void getEnglishName3() {
        System.out.println("getEnglishName3()执行!!");
    }

    @JSONField(serialize = false)
    public String isChinaName2() {
        System.out.println("isChinaName2()执行!!");
        return "isChinaName2";
    }
}

Tres casos de serialización de alta frecuencia

imagen

Tres casos de serialización de alta frecuencia

Básicamente se sigue el proceso anterior, encontrando problemas --> análisis de principios --> resolución de problemas --> sublimación (especificación de programación).

  • Centrándose en los negocios: resolución de problemas -> cómo elegir una buena solución -> cómo expandir n aplicaciones del sistema con una buena solución;

  • Concéntrese en la tecnología: resuelva un solo problema y domine los principios de esta línea a lo largo de un solo problema.

Pero, de hecho, no estoy satisfecho con este código, porque depende demasiado de FastJson. El efecto que quiero es no depender de ningún marco de serialización JSON en particular. Cuando necesito reemplazarlo, puedo reemplazarlo en cualquier momento.

Y al escribir código, no confíe demasiado en los registros. Solo necesita registrar información importante y crítica, no todos los registros. He visto un sistema de gestión que se ejecuta con discos de 128G en una hora. Casi no hay concurrencia, pero casi todas las solicitudes generan varios registros M. Hablaré de esto por separado más adelante.

Con respecto a las anotaciones de características como @JSONFieldy @JSONType, estandarizaré y daré una nueva solución de desacoplamiento dentro del equipo más adelante y las eliminaré.

 
 

 

 

Supongo que te gusta

Origin blog.csdn.net/2301_78588786/article/details/131870419
Recomendado
Clasificación