Если вы добавите еще несколько выводов журнала в свой основной класс ...
// (...)
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]
Итак, вы видите, что динамический c CGLIB-прокси расширяется Door
, что неудивительно. Затем Spring заставляет прокси-сервер реализовывать несколько интерфейсов, связанных с AOP, а также Paintable
. Вот и все, довольно просто.
В отладчике вы также можете увидеть несколько полей, например от CGLIB$CALLBACK_0
до CGLIB$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
Postscript: Сейчас вы знаете больше, но на самом деле вам действительно не нужно знать, потому что речь идет о внутренностях Spring. Вы не найдете его в руководстве Spring, потому что внутренняя реализация может теоретически измениться в любое время. И, кроме того, если вы переключитесь с Spring AOP на полный AspectJ, как описано в руководстве по Spring, то вся эта информация будет недействительной, поскольку AspectJ не использует прокси, а непосредственно преобразует байт-код Java. Тогда мой ответ выглядел бы совсем по-другому.
Обновление: Вы спросили:
Но вопрос был на самом деле, откуда Spring знает, какова реализация из новых методов должно быть. Другими словами, теперь, когда Door реализует Paintable, как Spring определяет реализацию методов Paintable для класса Door?
Он заставляет прокси вызывать соответствующие методы Fence
, потому что вы задали его как defaultImpl
в твоем аспекте. Для этого Spring создает внутренний Fence
делегат экземпляр, для которого вызывается метод через отражение. Вы можете видеть, что если вы отлаживаете вызов метода, например
doorAsPaintable.paint(Color.GREEN);
, пока не достигнете метода
package org.springframework.aop.support;
class DelegatePerTargetObjectIntroductionInterceptor ...
public Object invoke(MethodInvocation mi)
Для дальнейших вопросов, пожалуйста, прочитайте исходный код Spring.