- 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/
我正在使用 Ubuntu 16.04。 当我更新时,我收到以下消息 $ sudo apt-get update .... E: dpkg was interrupted, you must manua
似乎有些autoconf项目使用configure.in文件生成configure脚本,有些使用configure.ac。 使用一种或另一种之间的区别/优势是什么? 最佳答案 这只是风格问题。历史上
我正在尝试按如下方式配置 logback,但它抛出了这个错误。配置文件如下: %d{ISO8601} %-5p [%c] %msg%n
从Miguel de Icaza: We use a library profile that is better suited for mobile devices, so we removed f
我有两个 .config 文件,我需要一个 System.Configuration.Configuration那是它们之间的部分合并? 我可以将文件读取为 XML 并轻松创建所需的合并,但它是一个字
我似乎无法理解这两个注释。我尝试阅读 javadocs 但仍然无法弄清楚。任何人都可以用简单的代码来解释这两个吗? 非常感谢。 最佳答案 您使用 @Configuration作为配置 Spring b
我正在为一个简单的问题而焦头烂额。我的 .NET Core 3 应用程序中有一些设置,我认为最好将其移至我的 appsettings.json 文件。我按照本指南这样做:https://www.c-s
我正在为一个简单的问题而焦头烂额。我的 .NET Core 3 应用程序中有一些设置,我认为最好将其移至我的 appsettings.json 文件。我按照本指南这样做:https://www.c-s
我有以下测试方法: [TestMethod] public void TestHarvestMethod() { HarvestTargetTimeRangeUTC time = new Ha
我的以下代码没有产生预期的输出: public static void main(String[] args) throws MalformedURLException { Configura
我知道要从源代码编译和安装某些东西,在 Unix 系统中,涉及的三个步骤是: 1) ./configure 2) make 3) make install 当我检查OpenCV from source
我有以下片段: static void Main(string[] args) { var container = new UnityContainer(); container.Re
我想好像 apache 的 commons-configuration 可能/支持从属性文件中获取属性作为 map 到目前为止,我已经设法使用以下代码片段间接地做到了这一点 Map map = ne
我正在寻找任何技巧来使用 CKEditor 配置中的参数配置我的插件。我必须传递一些只能在显示 View 时传递的参数。 我想要这样的东西(带有 jQuery 适配器的示例): jQuery('t
在我正在玩的代码中,我看到一些地方是 @Configuration 类除了定义静态类之外什么都不做。格式各不相同,但通常是这样的: @Configuration public class someAu
我们在带有 JRE 7 的 Windows 7 中安装了 Cassandra 2.0.6,我们更新了 cassandra.yaml 文件,如下所示: 数据文件目录:D:\cassandra_data\
我在启动类中收到“'Startup.Configuration' 和'Startup.Configuration' 之间的歧义”错误。我不知道我做了什么导致这个问题。我所做的只是创建了一个 DBCon
我已经安装了 Cygwin64,现在我想安装 extundelete . 所以我下载了它,解压了文件,但是当我运行 ./configure 时它说: $ ./configure Configuring
为什么需要做(在容器目录内): # cd /container/directory/ # ./configure 代替: # pwd /external/path # /container/direc
我正在尝试编译qucs-0.0.19但是当我放置./configure时,它给了我以下错误: checking Checking if admsXml is working... no config
我是一名优秀的程序员,十分优秀!