目录
问题
省流
代码复现
TestService
TestAspect
TestController
源码分析
AbstractAutoProxyCreator
CglibAopProxy
Enhancer
问题
工作中,在负责的模块里使用 @DubboService 注解注册了一个 dubbo 接口,给定时任务模块去调用。在自我调试阶段,需要在本地自己验证一下接口的功能实现,所以就在本地写了一个测试接口来调用 dubbo 接口,在 Controller 层使用 @DubboReference 注解注入依赖后,在 debug 时却发现注入的依赖为 null。第一时间怀疑是自己写法不对,时间比较赶,所以就想着先不研究这个问题,可以先通过其他方式来验证,可以使用 Spring 的 @Service 注解将 dubbo 接口实现注册成 Spring 容器的bean,然后通过 @Autowired 来注入依赖,同样可以验证 dubbo 接口的功能。但是最终在 debug 时,注入的依赖同样为 null。
省流
当接口使用了 AOP 时,Controller 层接口方法不能为 private !!!应该为 public!!!
正常来说都不应该遇到这种问题,写接口时一般也是直接复制现有的接口方法,然后修修改改,Controller 层的方法一般都是 public,所以当时出现问题了也没能快速反应到是方法修饰符写错了。项目中存在private修饰的接口方法,估计就是写代码时通过代码提示手滑才选中了private。
代码复现
这里使用简单的代码进行复现错误,以及排查过程。
TestService
使用 Spring 的 @Service 注解注册为 Spring 容器的 bean
@Service
public class TestService {public void doSomething() {System.out.println("doSomething");}
}
TestAspect
@Aspect
@Component
@Slf4j
public class TestAspect {@Pointcut("execution(* test*(..))")public void testPointCut() { }@Around("testPointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {return point.proceed();}
}
TestController
@RestController
@RequestMapping ("test")
public class TestController {@Autowiredprivate TestService testService;@RequestMapping("/test1")private ResponseEntity<String> test1(){testService.doSomething();return ResponseEntity.ok("success");}@RequestMapping("/test2")public ResponseEntity<String> test2(){testService.doSomething();return ResponseEntity.ok("success");}
}
当时项目是能够正常启动的,所以就排除了依赖不存在的问题,如果启动时发现找不到依赖,就会直接抛出异常,导致 Spring 容器启动了。
自己写的接口依赖注入为 null,但是试了下其他接口,却发现一切正常。
最终,在 debug 中发现了问题
依赖注入失败的接口依赖注入成功的接口,拿到的 Controller 实例不一样!
依赖注入失败的接口:
依赖注入成功的接口:
可以看到两个接口最终拿到的是不一样的对象实例,public 方法获取到的就是有依赖注入的TestController实例,而private 方法获取到的是 TestController 的代理对象。代理对象显然是使用 AOP 动态生成的。
源码分析
直接从源码上看 AOP 创建切面代理对象的逻辑
AbstractAutoProxyCreator
在项目依赖中找到 AbstractAutoProxyCreator,可以看到它实现了 SmartInstantiationAwareBeanPostProcessor 接口,BeanPostProcessor 接口,就是用于 Spring 创建实例后做后置处理的,这里就是用来创建 AOP 代理对象。Spring 容器在实例化bean后,就会通过 postProcessAfterInitialization 来做 bean 的后置处理。
调用方法链如下:postProcessAfterInitialization -> wrapIfNecessary -> createProxy
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupportimplements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {//...... 省略无关代码/*** Create a proxy with the configured interceptors if the bean is* identified as one to proxy by the subclass.* @see #getAdvicesAndAdvisorsForBean*/@Overridepublic Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}//...... 省略无关代码/*** Wrap the given bean if necessary, i.e. if it is eligible for being proxied.* @param bean the raw bean instance* @param beanName the name of the bean* @param cacheKey the cache key for metadata access* @return a proxy wrapping the bean, or the raw bean instance as-is*/protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}//...... 省略无关代码/*** Create an AOP proxy for the given bean.* @param beanClass the class of the bean* @param beanName the name of the bean* @param specificInterceptors the set of interceptors that is* specific to this bean (may be empty, but not null)* @param targetSource the TargetSource for the proxy,* already pre-configured to access the bean* @return the AOP proxy for the bean* @see #buildAdvisors*/protected Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);if (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {evaluateProxyInterfaces(beanClass, proxyFactory);}}Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}return proxyFactory.getProxy(getProxyClassLoader());}//...... 省略无关代码}
creatProxy 方法最后委托给了 proxyFactory 来创建代理对象
proxyFactory.getProxy(getProxyClassLoader());
从上面debug的截图中可以看出来,Spring 最后使用的是 cglib 动态代理方式。
所以这里直接找到对应 cglib 动态代理相关的类 CglibAopProxy
CglibAopProxy
class CglibAopProxy implements AopProxy, Serializable {//......省略无关代码@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());}try {Class<?> rootClass = this.advised.getTargetClass();Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");Class<?> proxySuperClass = rootClass;if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {proxySuperClass = rootClass.getSuperclass();Class<?>[] additionalInterfaces = rootClass.getInterfaces();for (Class<?> additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface);}}// Validate the class, writing log messages as necessary.validateClassIfNecessary(proxySuperClass, classLoader);// Configure CGLIB Enhancer...Enhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader &&((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}enhancer.setSuperclass(proxySuperClass);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));Callback[] callbacks = getCallbacks(rootClass);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}// fixedInterceptorMap only populated at this point, after getCallbacks call aboveenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.return createProxyClassAndInstance(enhancer, callbacks);}catch (CodeGenerationException | IllegalArgumentException ex) {throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +": Common causes of this problem include using a final class or a non-visible class",ex);}catch (Throwable ex) {// TargetSource.getTarget() failedthrow new AopConfigException("Unexpected AOP exception", ex);}}protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {enhancer.setInterceptDuringConstruction(false);enhancer.setCallbacks(callbacks);return (this.constructorArgs != null && this.constructorArgTypes != null ?enhancer.create(this.constructorArgTypes, this.constructorArgs) :enhancer.create());}//......省略无关代码}
可以看到,CglibAopProxy 最后将创建代理对象委托给了 Enhancer
Enhancer
在 Enhancer 中,可以看到其中的 getMethods 方法,其中的 CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));
public class Enhancer extends AbstractClassGenerator {private static void getMethods(Class superclass, Class[] interfaces, List methods, List interfaceMethods, Set forcePublic) {ReflectUtils.addAllMethods(superclass, methods);List target = (interfaceMethods != null) ? interfaceMethods : methods;if (interfaces != null) {for (int i = 0; i < interfaces.length; i++) {if (interfaces[i] != Factory.class) {ReflectUtils.addAllMethods(interfaces[i], target);}}}if (interfaceMethods != null) {if (forcePublic != null) {forcePublic.addAll(MethodWrapper.createSet(interfaceMethods));}methods.addAll(interfaceMethods);}CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_STATIC));CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));CollectionUtils.filter(methods, new DuplicatesPredicate());CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_FINAL));}}
VisibilityPredicate 中,可以看到会取方法的修饰符做判断,Modifier.isPrivate(mode) 为true,则返回false。即将被代理对象的private方法过滤掉了,被代理对象中没有对应的方法,所以就只能执行代理对象中的方法了,而代理对象是由cglib实例化的,里面没有spring注入的对象,所以报空指针。
public class VisibilityPredicate implements Predicate {private boolean protectedOk;private String pkg;private boolean samePackageOk;public VisibilityPredicate(Class source, boolean protectedOk) {this.protectedOk = protectedOk;this.samePackageOk = source.getClassLoader() != null;this.pkg = TypeUtils.getPackageName(Type.getType(source));}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())));}}
}