Spring @DeclareParents 注释如何工作?它如何从新接口实现方法?



在阅读Spring AOP文档时,我遇到了@DeclareParents注释。我能够使用它构建一个工作示例:

public interface Openable {
void open();
void close();
boolean isOpen();
}
public interface Paintable {
void paint(Color color);
Color getColor();
}
@Component
public class Door implements Openable {
private boolean isOpen = false;
private Color color;
@Override
public void open() {
isOpen = true;
}
@Override
public void close() {
isOpen = false;
}
@Override
public boolean isOpen() {
return isOpen;
}
}
@Component
public class Fence implements Paintable {
private Color color;
@Override
public void paint(Color color) {
this.color = color;
}
@Override
public Color getColor() {
return color;
}
}
@Component
@Aspect
public class IntroductionAspect {
@DeclareParents(value="aopTraining.IntrocuctionsTest.Openable+", defaultImpl=Fence.class)
public static Paintable openable;
}
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan
public class IntroductionsAppConfig {
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(IntroductionsAppConfig.class);
Fence fence = context.getBean(Fence.class);
System.out.println("Color of the fence: " + fence.getColor());
Paintable doorAsPaintable = (Paintable) context.getBean(Door.class);
Openable doorAsOpenable = (Openable) doorAsPaintable; 
System.out.println("Door is open: " + doorAsOpenable.isOpen());
doorAsOpenable.open();
System.out.println("Door is open: " + doorAsOpenable.isOpen());
System.out.println("Door's current color: " + doorAsPaintable.getColor());
doorAsPaintable.paint(Color.GREEN);
System.out.println("Door's current color: " + doorAsPaintable.getColor());
System.out.println("Color of the fence: " + fence.getColor());
}
}

输出:

Color of the fence: null
Door is open: false
Door is open: true
Door's current color: null
Door's current color: java.awt.Color[r=0,g=255,b=0]
Color of the fence: null

所以我理解原理:我正在向Openable接口添加一个新类型(Paintable(。因此,所有可打开的东西(即Door(在运行时都可以绘制。我想知道的是:春天在内部是如何做到的?当它为Door类创建代理时,它如何实现Paintable接口提供的新方法?根据我目前所看到的,我的建议如下:它使用我在defaultImpl属性中提供的Paintable实现,即Fence。它似乎实例化了一个新Fence,将其(可能(存储到Door-proxy上的某个字段中,然后将对DoorPaintable方法的所有调用委托给这个内部Fence对象。我想知道,这个建议是否正确?不幸的是,文档中没有对此进行详细说明。

如果您向主类添加更多日志输出...

// (...)
Paintable doorAsPaintable = (Paintable) context.getBean(Door.class);
Openable doorAsOpenable = (Openable) doorAsPaintable;
Class<?> dynamicProxyClass = doorAsPaintable.getClass();
System.out.println("Dynamic proxy: " + dynamicProxyClass);
System.out.println("Dynamic proxy parent: " + dynamicProxyClass.getSuperclass());
System.out.println("Dynamic proxy interfaces: " + Arrays.asList(dynamicProxyClass.getInterfaces()));
// (...)

。然后,您将在日志输出中看到以下内容(抱歉,我在示例应用程序中使用了除您以外的其他包名称(:

Dynamic proxy: class spring.aop.q60221207.Door$$EnhancerBySpringCGLIB$$a29f3532
Dynamic proxy parent: class spring.aop.q60221207.Door
Dynamic proxy interfaces: [interface spring.aop.q60221207.Paintable, interface org.springframework.aop.SpringProxy, interface org.springframework.aop.framework.Advised, interface org.springframework.cglib.proxy.Factory]

所以你看到动态 CGLIB 代理扩展Door,这里不足为奇。然后 Spring 让代理实现一些与 AOP 相关的接口,并且还Paintable。就是这样,很简单。

在调试器中,您还可以看到一些字段,例如CGLIB$CALLBACK_0CGLIB$CALLBACK_4。在我当地的环境中,#4是有趣的。它是一个CglibAopProxy$AdvisedDispatcher实例,并有一个字段advised该字段是具有此值的ProxyFactory实例(为可读性添加换行符(:

org.springframework.aop.framework.ProxyFactory:
1 interfaces [spring.aop.q60221207.Paintable];
1 advisors [org.springframework.aop.aspectj.DeclareParentsAdvisor@797cf65c];
targetSource [SingletonTargetSource for target object [spring.aop.q60221207.Door@29526c05]];
proxyTargetClass=true;
optimize=false;
opaque=false;
exposeProxy=false;
frozen=false

后记:现在你知道更多,但实际上你真的不需要知道,因为它是关于 Spring 内部的。你在 Spring 手册中找不到它,因为内部实现理论上可以随时更改。此外,如果你按照Spring手册中的描述从Spring AOP切换到完整的AspectJ,那么所有这些信息都是无效的,因为AspectJ不使用代理,而是直接转换Java字节码。那么我的答案看起来就大不相同了。


更新:你问:

但问题实际上是,Spring如何知道,新方法的实施应该是什么。换句话说,既然 Door 实现了 Painttable,Spring 如何确定 Paint Table 方法在 Door 类上的实现?

它使代理调用相应的Fence方法,因为您在方面将其指定为defaultImpl。为此,Spring 创建了一个内部Fence委托实例,通过反射在其上调用该方法。你可以看到,如果你调试成一个方法调用,比如

doorAsPaintable.paint(Color.GREEN);

直到你达到方法

package org.springframework.aop.support;
class DelegatePerTargetObjectIntroductionInterceptor ...
public Object invoke(MethodInvocation mi)

有关其他问题,请阅读 Spring 源代码。

相关内容

  • 没有找到相关文章

最新更新