它是否使用不同类型的动态代理?
几乎完全
让我们弄清楚类和AOP代理之间的区别是什么,回答以下问题:@Configuration
- 为什么自调用方法没有事务语义,即使Spring能够拦截自调用的方法?
@Transactional
- 和AOP是如何相关的?
@Configuration
为什么自调用方法没有事务语义?@Transactional
简短的回答:
这就是AOP的制作方式。
长答案:
- 声明式事务管理依赖于 AOP(对于 Spring AOP 上的大多数 Spring 应用程序)
Spring框架的声明式事务管理通过Spring面向方面的编程(AOP)成为可能。
- 它是基于代理的 (§5.8.1.了解 AOP 代理)
Spring AOP是基于代理的。
来自同一段落:SimplePojo.java
public class SimplePojo implements Pojo {
public void foo() {
// this next method invocation is a direct call on the 'this' reference
this.bar();
}
public void bar() {
// some logic...
}
}
以及代理它的片段:
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();
}
}
这里要了解的关键是,类方法内的客户端代码具有对代理的引用。main(..)
Main
这意味着对该对象引用的方法调用是对代理的调用。
因此,代理可以委派给与该特定方法调用相关的所有侦听器(建议)。
但是,一旦调用最终到达目标对象(SimplePojo
,在本例中为引用),它可能对自己进行的任何方法调用(例如 this.bar()
或 this.foo())
都将针对此
引用而不是代理进行调用。
这具有重要意义。这意味着自调用不会导致与方法调用相关的建议有机会执行。
(强调关键部分。)
您可能会认为aop的工作原理如下:
想象一下,我们有一个想要代理的类:Foo
傅.java
:
public class Foo {
public int getInt() {
return 42;
}
}
没有什么特别的。只是方法返回getInt
42
拦截器:
拦截器.java
:
public interface Interceptor {
Object invoke(InterceptingFoo interceptingFoo);
}
LogInterceptor.java
(用于演示):
public class LogInterceptor implements Interceptor {
@Override
public Object invoke(InterceptingFoo interceptingFoo) {
System.out.println("log. before");
try {
return interceptingFoo.getInt();
} finally {
System.out.println("log. after");
}
}
}
InvokeTargetInterceptor.java
:
public class InvokeTargetInterceptor implements Interceptor {
@Override
public Object invoke(InterceptingFoo interceptingFoo) {
try {
System.out.println("Invoking target");
Object targetRetVal = interceptingFoo.method.invoke(interceptingFoo.target);
System.out.println("Target returned " + targetRetVal);
return targetRetVal;
} catch (Throwable t) {
throw new RuntimeException(t);
} finally {
System.out.println("Invoked target");
}
}
}
最后拦截Foo.java
:
public class InterceptingFoo extends Foo {
public Foo target;
public List<Interceptor> interceptors = new ArrayList<>();
public int index = 0;
public Method method;
@Override
public int getInt() {
try {
Interceptor interceptor = interceptors.get(index++);
return (Integer) interceptor.invoke(this);
} finally {
index--;
}
}
}
将所有东西连接在一起:
public static void main(String[] args) throws Throwable {
Foo target = new Foo();
InterceptingFoo interceptingFoo = new InterceptingFoo();
interceptingFoo.method = Foo.class.getDeclaredMethod("getInt");
interceptingFoo.target = target;
interceptingFoo.interceptors.add(new LogInterceptor());
interceptingFoo.interceptors.add(new InvokeTargetInterceptor());
interceptingFoo.getInt();
interceptingFoo.getInt();
}
将打印:
log. before
Invoking target
Target returned 42
Invoked target
log. after
log. before
Invoking target
Target returned 42
Invoked target
log. after
现在让我们来看看 ReflectiveMethodInvocation
。
以下是其方法的一部分:proceed
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
++this.currentInterceptorIndex
现在应该看起来很熟悉
您可以尝试在应用程序中引入几个方面,并在调用建议的方法时看到堆栈在profeed
方法处增长
最后,一切都在MethodProxy结束。
从它的调用
方法javadoc:
在相同类型的不同对象上调用原始方法。
正如我之前提到的文档:
一旦调用最终到达对象,它可能对自己进行的任何方法调用都将针对引用而不是代理进行调用。target
this
我希望现在或多或少,原因很清楚。
和AOP是如何相关的?@Configuration
答案是它们不相关。
所以春天在这里可以自由地做任何它想做的事情。在这里,它不绑定到代理AOP语义。
它使用 ConfigurationClassEnhancer
增强了此类类。
看看:
回到问题
如果Spring可以成功地拦截@Configuration类中的类内函数调用,为什么它在常规bean中不支持它呢?
我希望从技术角度来看,原因很清楚。
现在我从非技术方面的想法:
我认为它没有完成,因为春季AOP在这里的时间足够长了......
自Spring Framework 5以来,Spring WebFlux框架已经推出。
现在Spring团队正在努力增强反应式编程模型
查看一些值得注意的近期博客文章:
引入了越来越多的功能,以构建Spring应用程序的代理较少方法。(例如,请参阅此提交)
所以我认为,即使有可能做你所描述的事情,它现在还远远不是Spring Team的首要任务。