目录
问题
工作中,在负责的模块里使用 @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 {
@Autowired
private 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 ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
//...... 省略无关代码
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public 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 {
//......省略无关代码
@Override
public 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 above
enhancer.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() failed
throw 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())));
}
}
}
标签:enhancer,return,Spring,beanName,Boot,bean,null,public
From: https://blog.csdn.net/typeracer/article/details/143087198