Spring使用JDK动态代理或CGLIB创建给定目标对象的代理。如果使用@Configuration注释类,则使用CGLIB。
但是,Spring AOP的一个局限性是,一旦调用最终到达目标对象,它可能对自身进行的任何方法调用都将针对此引用而不是代理进行调用。使用@Transactional
以及在其他地方记住这些信息很重要。
因此,在下面的代码中,Spring会注入SimpleBean
的实际实例或代理吗?
@Configuration
public class Config {
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean()); //<---
}
}
如果一个类是用@Component
注解的,那是什么行为?
答案 0 :(得分:1)
让我给您另一个视角。
说有另一个bean AnotherBeanConsumer
,它也需要一个simpleBean
。 Simple Bean具有 Singleton 范围:
@Configuration
public class Config {
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
@Bean
public AnotherBeanConsumer anotherBeanConsumer() {
return new AnotherBeanConsumer(simpleBean());
}
}
现在的问题是,用不同方法simpleBean()
和simpleBeanConsumer
进行的两次对anotherBeanConsumer
的调用有可能返回相同bean的相同实例(显然是因为它是单例) ?
IMO(以及免责声明,我与spring无关),这是创建用于包装Configurations的代理的主要原因。
现在的确,正如您已经说过的那样,Spring AOP确实具有调用方法的局限性,但是谁说Spring幕后使用Spring AOP?在较低级别完成的字节码检测没有这样的限制。毕竟,创建代理意味着:“创建具有相同接口但会改变行为的代理对象”,对吗?
例如,如果您使用使用继承的CGLIB,则可以创建一个配置不正确的代理,该配置看起来像这样:
class CGLIB_GENERATED_PROXY extends Config {
private Map<String, Object> singletonBeans;
public SimpleBean simpleBean() {
String name = getNameFromMethodNameMaybePrecached();
if(singletonBeans.get(name) != null) {
return singletonBeans.get(name);
}
else {
SimpleBean bean = super.simpleBean();
singletonBeans.put(name, bean);
return bean;
}
}
....
}
当然,它只是一张示意图,在现实生活中,有一个应用程序上下文基本上可以像这样提供对地图的访问,但是您明白了。
如果这还不够的话,那么为了加载配置(例如ASM),spring必须利用一些甚至更复杂的框架...
这里是一个示例:
如果您使用@ConditionalOnClass(A.class)
并且该类在运行时中确实不存在,那么spring如何加载使用该配置的配置的字节码并且不会在类似NoClassDefFoundException
的情况下失败?
我的观点是,它远远超出了春季AOP的范围,并且有其怪癖:)
话虽如此,我上面所描述的并没有要求将真实组件始终始终包裹在任何类型的代理中。因此,在最简单的情况下,当SimpleBean
本身没有某些需要代理生成的注释(诸如@Cached
,@Transactional
之类的东西)时,Spring不会包装该对象这种类型,您将获得一个普通的SimpleBean
对象。