- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我最近注意到 Spring 在 @Configuration 类中成功拦截了类内函数调用,但在常规 bean 中没有。
这样的电话
@Repository
public class CustomerDAO {
@Transactional(value=TxType.REQUIRED)
public void saveCustomer() {
// some DB stuff here...
saveCustomer2();
}
@Transactional(value=TxType.REQUIRES_NEW)
public void saveCustomer2() {
// more DB stuff here
}
}
无法启动新事务,因为虽然 saveCustomer() 的代码在 CustomerDAO 代理中执行,但 saveCustomer2() 的代码在展开的 CustomerDAO 类中执行,正如我在调试器中查看“this”所看到的那样,因此 Spring 没有机会拦截对 saveCustomer2 的调用。
但是,在以下示例中,当 transactionManager() 调用 createDataSource() 时,它会被正确拦截并调用代理的 createDataSource(),而不是解包类的 createDataSource(),正如在调试器中查看“this”所证明的那样。
@Configuration
public class PersistenceJPAConfig {
@Bean
public DriverManagerDataSource createDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//dataSource.set ... DB stuff here
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager( ){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(createDataSource());
return transactionManager;
}
}
所以我的问题是,为什么 Spring 可以正确拦截第二个示例中的类内函数调用,但不能在第一个示例中。是否使用不同类型的动态代理?
编辑:从这里的答案和其他来源,我现在了解以下内容:@Transactional 使用 Spring AOP 实现,其中代理模式通过用户类的包装/组合来执行。 AOP 代理足够通用,因此可以将许多方面链接在一起,并且可以是 CGLib 代理或 Java 动态代理。
在@Configuration 类中,Spring 还使用CGLib 创建了一个继承自用户@Configuration 类的增强类,并用在调用用户/ super 函数之前做一些额外工作的函数覆盖用户的@Bean 函数,例如检查这是否是函数的第一次调用。这个类是代理吗?这取决于定义。你可能会说它是一个代理,它使用来自真实对象的继承而不是使用组合来包装它。
总而言之,从这里给出的答案中,我了解到这是两种完全不同的机制。为什么做出这些设计选择是另一个悬而未决的问题。
最佳答案
Is it using different types of dynamic proxies?
几乎正是
让我们弄清楚 @Configuration
类和回答以下问题的 AOP 代理有什么区别:
@Transactional
方法也没有事务语义?@Configuration
和AOP有什么关系?@Transactional
方法没有事务语义?简答:
这就是 AOP 的制作方式。
长答案:
The Spring Framework’s declarative transaction management is made possible with Spring aspect-oriented programming (AOP)
Spring AOP is proxy-based.
同段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();
}
}
The key thing to understand here is that the client code inside the
main(..)
method of theMain
class has a reference to the proxy.This means that method calls on that object reference are calls on the proxy.
As a result, the proxy can delegate to all of the interceptors (advice) that are relevant to that particular method call.
However, once the call has finally reached the target object (the
SimplePojo
, reference in this case), any method calls that it may make on itself, such asthis.bar()
orthis.foo()
, are going to be invoked against thethis
reference, and not the proxy.This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
(重点部分被强调了。)
你可能认为aop的工作原理如下:
假设我们有一个想要代理的 Foo
类:
Foo.java
:
public class Foo {
public int getInt() {
return 42;
}
}
没有什么特别的。只是 getInt
方法返回 42
拦截器:
Interceptor.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");
}
}
}
最后InterceptingFoo.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
现在看起来应该很熟悉了
target
method
index
您可以尝试在应用程序中引入几个方面,并在调用建议方法时看到堆栈在 proceed
方法处增长
最后一切都在 MethodProxy 结束。 .
来自 invoke
方法javadoc:
Invoke the original method, on a different object of the same type.
正如我之前提到的文档:
once the call has finally reached the
target
object any method calls that it may make on itself are going to be invoked against thethis
reference, and not the proxy
我希望现在,或多或少,很清楚为什么。
@Configuration
和AOP有什么关系?答案是他们不相关。
所以这里的 Spring 可以自由地为所欲为。这里它与 proxy AOP 语义无关。
它使用 ConfigurationClassEnhancer
增强了这些类.
看看:
If Spring can successfully intercept intra class function calls in a @Configuration class, why does it not support it in a regular bean?
我希望从技术角度很清楚为什么。
现在我的想法来自非技术方面:
我认为它没有完成,因为 Spring AOP 在这里足够长...
自 Spring Framework 5 起 Spring WebFlux框架已经引入。
目前Spring团队正在努力增强reactive编程模型
查看一些值得注意的近期博文:
引入了越来越多的 less-proxying 方法来构建 Spring 应用程序。 (例如,参见 this commit)
所以我认为,即使有可能做你所描述的事情,它也远非 Spring Team 目前的第一优先事项
关于java - 如果 Spring 可以成功拦截 @Configuration 类中的类内函数调用,为什么它在常规 bean 中不支持呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56193642/
我刚开始使用新的拦截方法,有一个基本问题,想知道如何在一个测试中链接下面的两个断言。 cy.intercept('GET', '/states').as('states'); cy.reload(tr
我有一个标签控件的自定义版本(使用用户控件构建)。在设计器中工作时,我想截取 Name 属性的设置(在属性面板中)并使用它来生成 Text 属性。也就是说,如果我在属性面板的 Name 属性中输入“l
嗨,我想通过 soapUI 解析 ssl soap 消息,而我试图通过 HttpMonitor 进行拦截它显示在异常下方 ERROR:Exception in request: javax.net.s
是否有可能从某个任意层拦截反向梯度,修改其值并继续反向传播回到网络的开始,根据您提供的修改后的梯度值更新所有先前层的反向梯度? 我知道你可以directly modify the gradients
我可以从什么 dll 中获得 Intercept 的扩展?我从 http://github.com/danielmarbach/ninject.extensions.interception 添加了
我有一个实现 onCreateOptionsMenu 方法的顶级 TabHost。我希望子 Activity (选项卡内的子 Activity )能够通过 onOptionsItemSelected
我在尝试反序列化 URL 时遇到此错误 Caused by: java.net.MalformedURLException: no protocol: www.boo.com at java.
首先,我是 Spring 的新手,这是我第一次尝试使用 Spring 编写基于 REST 的应用程序。 我计划在请求参数和响应中使用 Json。这让我想到两个问题。 有没有办法将 products="
在我基于 j_security_check 的登录表单中登录时一切正常。在这种情况下,我看到 JSESSIONID cookie 中的路径具有来自 URL 的值。但是当另一个登录页面构造动态表单(它正
我有一个我一直致力于下载文件的程序。一切正常,除非用户使用 AVG。奇怪的是,为了解决这个问题,似乎必须禁用 AVG 的“电子邮件保护”;将我的程序或 JRE 添加到异常(exception)列表不起
我正在寻找一种方法来挂接 SMSManager 或较低级别的机制,以便我可以在发送任何外发 SMS 消息之前拦截、读取和取消它们。 最佳答案 迟到总比不到好:) 我已经在这上面花了 2 天...并且不
我已成功拦截对 read() 的调用, write() , open() , unlink() , rename() , creat()但不知何故截获完全相同的语义stat()没有发生。我已经使用 L
阿里云ECS通过安全组屏蔽/拦截/阻断特定IP对云服务器的访问 所适用的场景: 通过安全组屏蔽、拦截、阻止特定IP对用户云服务器的访问,或者屏蔽IP访问服务器的特定端口。 配置的方法: 1、
我希望能够在类本身的构造函数中代理类的所有方法。 class Boy { constructor() { // proxy logic, do something before
使用 ajax 请求可以使用以下代码完成: let oldXHROpen = window.XMLHttpRequest.prototype.open; window.lastXhr = ''; wi
我想“拦截”/更改将 OData 与 Web API 一起使用时生成的 OData 查询..但我不完全确定如何“提取”生成的查询..我假设 OData 过滤器、扩展和更多一些如何生成某种表达式树或某种
当 JUnit 中的断言失败时,我想做一些“自己的事情”。我想要这个: public class MyAssert extends org.junit.Assert { // @Overrid
如何拦截 PartialFunction?例如在 Actor 中,如果我只想打印进入以下接收方法的所有内容,然后再将其传递给流程方法: class MyActor extends Actor {
我们正在使用 fluentvalidation(带有服务堆栈)来验证我们的请求 DTO。我们最近扩展了我们的框架以接受“PATCH”请求,这意味着我们现在需要仅在补丁包含要验证的字段时才应用验证。 我
我有一个作为 excel 插件运行的 WPF 应用程序,它有这样的可视化树 精益求精 元素主机 WPF 用户控件 WPF 色带条控件 现在,在 excel 中加载插件时,不会启用位于 WPF 功能区栏
我是一名优秀的程序员,十分优秀!