Se produce una excepción de puntero nulo de objeto de servicio inyectado en el método privado de la capa de control SpringMVC

1. Fenómenos

El bean de la capa de servicio inyectado en la interfaz privada en el controlador en SpringMVC es nulo, y el método con los modificadores de acceso público y protegido en el mismo controlador no tendrá ese problema.

El método en el controlador está representado por AOP.Si el controlador ordinario no tiene AOP, el bean en el método privado también es normal.

2. Análisis de la razón

Debido a que el método privado sin mejora AOP es normal, podemos pensar que el atributo puede estar vacío debido a la creación del objeto proxy.

En primer lugar, Spring AOP tiene dos métodos de implementación, uno es el proxy dinámico Jdk y el otro es el proxy dinámico Cglib.

Uno de estos dos métodos es a través de la implementación de la interfaz, y el otro es a través de la creación de subclases para reescribir, por lo que obviamente estos dos métodos no pueden representar métodos privados.

Al crear un objeto proxy, pasará por una lógica de este tipo Enhancer#generateClass -> Enhancer#getMethods -> CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true)) -> VisibilityPredicate#evaluate

public boolean evaluate(Object arg) {
     
     
   Member member = (Member)arg;
   int mod = member.getModifiers();
   if (Modifier.isPrivate(mod)) {
     
     
       return false;
   } else if (Modifier.isPublic(mod)) {
     
     
       return true;
   } else if (Modifier.isProtected(mod) && this.protectedOk) {
     
     
       return true;
   } else {
     
     
       return this.samePackageOk && this.pkg.equals(TypeUtils.getPackageName(Type.getType(member.getDeclaringClass())));
   }
}

Puede ver que el método privado está filtrado, es decir, el método privado no se mejorará en el objeto proxy creado

El uso de la anotación @Aspect en Spring registrará un posprocesador para determinar si es necesario crear un proxy cuando se inicializa el Bean (la lógica principal está en el método wrapIfNecessary). Y todos sabemos que el bean inyecta todas las dependencias del atributo cuando se asigna el atributo, por lo que el bean en la capa de servicio del bean está completamente lleno en este momento.

Entonces, ¿por qué hay una excepción de puntero nulo al llamar al método privado?

Esto se debe a que el proxy creado para esta clase no ha completado el ciclo de vida del bean, por lo que sus propiedades son nulas. El método privado no es interceptado por la clase de proxy real (filtrado como se mencionó anteriormente), por lo que el método privado no puede obtener el objeto de proxy, por lo que el objeto de proxy se usa para llamar al método, y Cglib crea el objeto de proxy y hay no se inyectó ningún objeto bean, por lo que se produce una excepción de puntero nulo.

Cuando se llama al método mejorado (es decir, el método reescrito en la clase de proxy), el objeto que se pasa no es el objeto de instancia del proxy, sino el destino, es decir, el objeto de instancia del bean que se está representando, por lo que el capa de servicio se puede obtener el frijol.

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
     
     
   @Override
   @Nullable
   public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
     
     
       // 省略...
       target = targetSource.getTarget();
       Class<?> targetClass = (target != null ? target.getClass() : null);
       List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
       Object retVal;
       // Check whether we only have one InvokerInterceptor: that is,
       // no real advice, but just reflective invocation of the target.
       if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
     
     
          // We can skip creating a MethodInvocation: just invoke the target directly.
          // Note that the final invoker must be an InvokerInterceptor, so we know
          // it does nothing but a reflective operation on the target, and no hot
          // swapping or fancy proxying.
          Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
          retVal = invokeMethod(target, method, argsToUse, methodProxy);
       }
       else {
     
     
          // We need to create a method invocation...
          retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
       }
       retVal = processReturnType(proxy, target, method, retVal);
       return retVal;
       // 省略...
   }
}

static boolean isMethodProxyCompatible(Method method) {
     
     
   return (Modifier.isPublic(method.getModifiers()) &&
           method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
           !AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method));
}

También se puede ver en los comentarios que cuando se llama al método público, "solo invocación reflexiva del objetivo", es decir, solo una llamada reflexiva al objetivo

Supongo que te gusta

Origin blog.csdn.net/qq_25046827/article/details/129272316
Recomendado
Clasificación