springAop代理后导致controller中service为null的bug原因
1.现象:
@RequestMapping("/test")
@RestController
public class TestController {
@Autowired
private TestService testService;
@GetMapping
@TestAop
private String getName(){
return testService.getName();
}
}
就是一个简单的controller的类,然后切面为:
@Aspect @Component public class AopTest { @Before("@annotation(TestAop)") public void before(){ System.out.println("test aop"); } }
直接访问返回如下错误:
java.lang.NullPointerException: null at com.aop.privatebug.demo.TestController.getName(TestController.java:18) ~[classes/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.23.jar:5.3.23] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.23.jar:5.3.23] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.23.jar:5.3.23] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.23.jar:5.3.23] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.23.jar:5.3.23] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.23.jar:5.3.23] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071) ~[spring-webmvc-5.3.23.jar:5.3.23] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964) ~[spring-webmvc-5.3.23.jar:5.3.23] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.23.jar:5.3.23] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.23.jar:5.3.23] at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) ~[tomcat-embed-core-9.0.65.jar:4.0.FR] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.23.jar:5.3.23] at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.65.jar:4.0.FR] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.65.jar:9.0.65] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.65.jar:9.0.65]
打断点发现是service为空了。

2,原因查找
这里controller没有继承故所以采用的cglib的代理方式代理的。所以先设置将cglib代理生成的对象写到指定目录查看生成的代理对象。
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\temp");

设置好后打开cglib代理生成的类。这里只copy了部分代码,发现private方法的getName()并没有生成代理的方法。
public class TestController$$EnhancerBySpringCGLIB$$9e3f2bbd extends TestController implements SpringProxy, Advised, Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private MethodInterceptor CGLIB$CALLBACK_1; private NoOp CGLIB$CALLBACK_2; private Dispatcher CGLIB$CALLBACK_3; private Dispatcher CGLIB$CALLBACK_4; private MethodInterceptor CGLIB$CALLBACK_5; private MethodInterceptor CGLIB$CALLBACK_6; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$equals$0$Method; private static final MethodProxy CGLIB$equals$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$toString$1$Method; private static final MethodProxy CGLIB$toString$1$Proxy; private static final Method CGLIB$hashCode$2$Method; private static final MethodProxy CGLIB$hashCode$2$Proxy; private static final Method CGLIB$clone$3$Method; private static final MethodProxy CGLIB$clone$3$Proxy; static void CGLIB$STATICHOOK3() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("com.aop.privatebug.demo.TestController$$EnhancerBySpringCGLIB$$9e3f2bbd"); Class var1; Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$equals$0$Method = var10000[0]; CGLIB$equals$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$0"); CGLIB$toString$1$Method = var10000[1]; CGLIB$toString$1$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$1"); CGLIB$hashCode$2$Method = var10000[2]; CGLIB$hashCode$2$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$2"); CGLIB$clone$3$Method = var10000[3]; CGLIB$clone$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$3"); } final boolean CGLIB$equals$0(Object var1) { return super.equals(var1); } public final boolean equals(Object var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_5; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_5; } if (var10000 != null) { Object var2 = var10000.intercept(this, CGLIB$equals$0$Method, new Object[]{var1}, CGLIB$equals$0$Proxy); return var2 == null ? false : (Boolean)var2; } else { return super.equals(var1); } }
查看springmvc触发controller的方式为通过method.invoke()反射的方式触发。所以怀疑可能是cglib生成的代理子类没有注入service,导致直接触发method的反射时,获取代理类中的service没有拿到。故查看spring生成代理类的代码为位置:
在AbstractAutoProxyCreator的postProcessAfterInitialization()方法中调用wrapIfNecessary()中生成的cglib的代理类果然testService没有注入serivce的值。而自动注入的AutowiredAnnotationBeanPostProcessor的执行是在AbstractAutoProxyCreator
之前执行的。

3.结论
a.导致springAop代理后导致controller中service为null的bug原因的为private修改的方法cglib是不会代理的,而生成的代理类中的成员变量是不会注入spring的值的。所有会导致没有代理的方法,获取不到spring自动注入的值。
b.代理后的方法为什么就拿的到spring自动注入的值呢?原因如下:
查看cglib生成的类的代码方法的代码:

然后methodInterceptor,从上面spring的aop生成代理类的时候发现CGLIB$CALLBACK_0类型为DynamicAdvisedInterceptor类型,然后发现调用时获取了targetSource的target,而target就是spring中上面的未代理前TestController,而TestController经过
AutowiredAnnotationBeanPostProcessor的自动注入过程时有service的,所以在这里调用的时候用target调用反射是获取的到service
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable { private final AdvisedSupport advised; public DynamicAdvisedInterceptor(AdvisedSupport advised) { this.advised = advised; } @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); Object var16; try { if (this.advised.exposeProxy) { oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } target = targetSource.getTarget(); Class<?> targetClass = target != null ? target.getClass() : null; List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; if (chain.isEmpty() && CglibAopProxy.CglibMethodInvocation.isMethodProxyCompatible(method)) { Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = CglibAopProxy.invokeMethod(target, method, argsToUse, methodProxy); } else { retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed(); } retVal = CglibAopProxy.processReturnType(proxy, target, method, retVal); var16 = retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } return var16; }

浙公网安备 33010602011771号