gpt4 book ai didi

spring - @Configuration 与 @Component 类中的自调用行为

转载 作者:行者123 更新时间:2023-12-04 16:42:31 25 4
gpt4 key购买 nike

我的问题是关于内部方法调用的 AOP Spring 行为。

@Service
class Service {
@Transactional
public void method1() {
method1();
}

@Transactional
public void method2() {}
}

如果我们从外部调用method1(),method1()会以事务模式执行,但由于内部调用method2(),method2()内部的代码不会以事务模式执行。

同时,对于 Configuration 类,通常我们应该有相同的行为:

@Configuration
class MyConfiguration{
@Bean
public Object1 bean1() {
return new Object1();
}

@Bean
public Object1 bean2() {
Object1 b1 = bean1();
return new Object2(b1);
}
}

通常,如果我理解得很好,从 bean2() 调用 bean1() 方法不应该被代理对象拦截,因此,如果我们多次调用 bean1(),我们每次都应该得到不同的对象。

首先,您能否从技术上解释为什么代理对象没有拦截内部调用,其次检查我对第二个示例的理解是否正确。

最佳答案

常规 Spring @Component s
有关普通 Spring (AOP) 代理或动态代理(JDK、CGLIB)在一般情况下如何工作的说明,请参阅 my other answer 和示例代码。首先阅读它,你就会明白为什么不能通过 Spring AOP 拦截这些类型的代理的自调用。@Configuration
至于 @Configuration 类,它们的工作方式不同。为了避免已经创建的 Spring bean 仅仅因为它们的 @Bean 工厂方法被再次外部或内部调用而再次创建,Spring 为它们创建了特殊的 CGLIB 代理。
我的配置类之一如下所示:

package spring.aop;

import org.springframework.context.annotation.*;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ApplicationConfig {
@Bean(name = "myInterfaceWDM")
public MyInterfaceWithDefaultMethod myInterfaceWithDefaultMethod() {
MyClassImplementingInterfaceWithDefaultMethod myBean = new MyClassImplementingInterfaceWithDefaultMethod();
System.out.println("Creating bean: " + myBean);
return myBean;
}

@Bean(name = "myTestBean")
public Object myTestBean() {
System.out.println(this);
myInterfaceWithDefaultMethod();
myInterfaceWithDefaultMethod();
return myInterfaceWithDefaultMethod();
}
}
相应的应用程序如下所示:
package spring.aop;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args);
MyInterfaceWithDefaultMethod myInterfaceWithDefaultMethod =
(MyInterfaceWithDefaultMethod) appContext.getBean("myInterfaceWDM");
System.out.println(appContext.getBean("myTestBean"));
}
}
这打印(编辑删除我们不想看到的东西):
  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.2.RELEASE)

2019-07-07 08:37:55.750 INFO 22656 --- [ main] spring.aop.DemoApplication : Starting DemoApplication on (...)
(...)
Creating bean: spring.aop.MyClassImplementingInterfaceWithDefaultMethod@7173ae5b
spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a@72456279
运行应用程序时,即使 myInterfaceWithDefaultMethod() 内有多次调用,也不会多次调用方法 myTestBean() 。为什么?
如果您在 myInterfaceWithDefaultMethod() 中的 myTestBean() 调用之一上放置断点并让调试器在那里停止,您将了解更多信息。然后你可以通过评估代码来检查情况:
System.out.println(this);

spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a@72456279
所以 config 类确实是一个 CGLIB 代理。但是它有哪些方法呢?
for (Method method: this.getClass().getDeclaredMethods()) {
System.out.println(method);
}

public final java.lang.Object spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.myTestBean()
public final spring.aop.MyInterfaceWithDefaultMethod spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.myInterfaceWithDefaultMethod()
public final void spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.setBeanFactory(org.springframework.beans.factory.BeanFactory) throws org.springframework.beans.BeansException
final spring.aop.MyInterfaceWithDefaultMethod spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.CGLIB$myInterfaceWithDefaultMethod$1()
public static void spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.CGLIB$SET_THREAD_CALLBACKS(org.springframework.cglib.proxy.Callback[])
public static void spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.CGLIB$SET_STATIC_CALLBACKS(org.springframework.cglib.proxy.Callback[])
public static org.springframework.cglib.proxy.MethodProxy spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.CGLIB$findMethodProxy(org.springframework.cglib.core.Signature)
final void spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.CGLIB$setBeanFactory$6(org.springframework.beans.factory.BeanFactory) throws org.springframework.beans.BeansException
static void spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.CGLIB$STATICHOOK4()
private static final void spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.CGLIB$BIND_CALLBACKS(java.lang.Object)
final java.lang.Object spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.CGLIB$myTestBean$0()
static void spring.aop.ApplicationConfig$$EnhancerBySpringCGLIB$$8b4ed8a.CGLIB$STATICHOOK3()
这看起来有点乱,让我们打印方法名称:
for (Method method: this.getClass().getDeclaredMethods()) {
System.out.println(method.name);
}

myTestBean
myInterfaceWithDefaultMethod
setBeanFactory
CGLIB$myInterfaceWithDefaultMethod$1
CGLIB$SET_THREAD_CALLBACKS
CGLIB$SET_STATIC_CALLBACKS
CGLIB$findMethodProxy
CGLIB$setBeanFactory$6
CGLIB$STATICHOOK4
CGLIB$BIND_CALLBACKS
CGLIB$myTestBean$0
CGLIB$STATICHOOK3
该代理是否实现了任何接口(interface)?
for (Class<?> implementedInterface : this.getClass().getInterfaces()) {
System.out.println(implementedInterface);
}

interface org.springframework.context.annotation.ConfigurationClassEnhancer$EnhancedConfiguration
不错,有意思。让我们阅读一些 Javadoc。实际上 ConfigurationClassEnhancer 类是包范围的,所以我们必须阅读 source code 中的 Javadoc:

Enhances Configuration classes by generating a CGLIB subclass which interacts with the Spring container to respect bean scoping semantics for @Bean methods. Each such @Bean method will be overridden in the generated subclass, only delegating to the actual @Bean method implementation if the container actually requests the construction of a new instance. Otherwise, a call to such an @Bean method serves as a reference back to the container, obtaining the corresponding bean by name.


内部接口(interface) EnhancedConfiguration 实际上是公开的,但 Javadoc 仍然只在 source code 中:

Marker interface to be implemented by all @Configuration CGLIB subclasses. Facilitates idempotent behavior for enhance through checking to see if candidate classes are already assignable to it, e.g. have already been enhanced.Also extends BeanFactoryAware, as all enhanced @Configuration classes require access to the BeanFactory that created them.

Note that this interface is intended for framework-internal use only, however must remain public in order to allow access to subclasses generated from other packages (i.e. user code).


现在,如果我们进入 myInterfaceWithDefaultMethod() 调用,我们会看到什么?生成的代理方法调用方法 ConfigurationClassEnhancer.BeanMethodInterceptor.intercept(..) 并且该方法的 Javadoc 说:

Enhance a @Bean method to check the supplied BeanFactory for the existence of this bean object.


在那里你可以看到其余的魔法正在发生,但描述真的超出了这个已经很长的答案的范围。

关于spring - @Configuration 与 @Component 类中的自调用行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56917484/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com