An injected service object null pointer exception occurs in the private method of the SpringMVC control layer

1. Phenomena

The bean of the service layer injected into the private interface in the controller in SpringMVC is null, and the method with the access modifiers public and protected in the same controller will not have such a problem.

The method in the controller is proxied by AOP. If the ordinary Controller does not have AOP, the bean in the private method is also normal.

2. Reason Analysis

Because the private method without AOP enhancement is normal, we can think that the attribute may be empty due to the creation of the proxy object.

First of all, Spring AOP has two implementation methods, one is Jdk dynamic proxy, and the other is Cglib dynamic proxy.

One of these two methods is through the implementation of the interface, and the other is through the creation of subclasses to rewrite, so obviously these two methods cannot proxy private methods.

When creating a proxy object, it will go through such a logic 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())));
   }
}

You can see that the private method is filtered, that is, the private method will not be enhanced in the created proxy object

Using the @Aspect annotation in Spring will register a post-processor to determine whether a proxy needs to be created when the Bean is initialized (the main logic is in the wrapIfNecessary method). And we all know that the bean injects all the dependencies of the attribute when the attribute is assigned, so the bean in the service layer of the bean is completely filled at this time.

Then why is there a null pointer exception when calling the private method?

This is because the proxy created for this class has not completed the bean life cycle, so the properties in it are null. The private method is not intercepted by the real proxy class (filtered as mentioned above), so the private method cannot obtain the proxy object, so the proxy object is used to call the method, and the proxy object is created by Cglib and There is no bean object injected, so a null pointer exception occurs.

When the enhanced method (that is, the method rewritten in the proxy class) is called, the object passed in is not the instance object of the proxy, but the target, that is, the instance object of the bean being proxied, so the service layer can be obtained the bean.

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));
}

It can also be seen from the comments that when the public method is called, "just reflective invocation of the target", that is, just a reflective call to the target

おすすめ

転載: blog.csdn.net/qq_25046827/article/details/129272316