当前位置: 首页 > 知识库问答 >
问题:

为什么在将CGLIB原型注入Singleton的情况下,每次对原型的访问都会创建一个新对象?

孙辰阳
2023-03-14

免责声明:我读了以下关于JDK动态代理和CGLIB的文章:https://stackoverflow.com/a/21762454/2674303

我读过以下有趣的文章:将Spring原型bean注入单例bean

原型:

@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
class MessageBuilder {

    private static final AtomicInteger instanceCounter = new AtomicInteger(0);

    MessageBuilder() {
        instanceCounter.incrementAndGet();
    }

    static int getInstanceCounter() {
        return instanceCounter.get();
    }
    ....
}
@Service
class MessageService {

    private final MessageBuilder messageBuilder;

    MessageService(MessageBuilder messageBuilder) {
        this.messageBuilder = messageBuilder;
    }

    Message createMessage(String content, String receiver) {
        return messageBuilder
                .withContent(content)
                .withReceiver(receiver)
                .build();
    }     
}

测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MessageServiceTest {

    @Autowired
    private MessageService messageService;

    @Test
    public void shouldCreateTwoBuilders() throws Exception {
        //when
        messageService.createMessage("text", "alice");
        messageService.createMessage("msg", "bob");
        //then
        int prototypeCounter = MessageBuilder.getInstanceCounter();
        assertEquals("Wrong number of instances", 2, prototypeCounter);
    }

}

很明显,测试失败了,因为注射只发生一次,实际结果是1,但我们预期是2。

第二种情况:

@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, 
       proxyMode = ScopedProxyMode.TARGET_CLASS)
class MessageBuilder {
  // ...
}

当我们开始测试时,我们看到实际结果是6,因为内部createMessage方法messageBuilder被访问了3次。并且createMessage方法被调用两次,因此3*2=6。

为了解释这种行为,作者提供了以下图片:

我无法理解哪个bean是依赖的,以及为什么每次访问proxyMessageBuilder都需要新的bean实例化。为什么第一种情况不同?你能解释一下吗?据我所知--代理无论如何都是装箱的--使用CGLIB或使用动态代理,所以无论如何代理都是注入的

共有1个答案

卫弘图
2023-03-14

如果使用prototype作用域定义一个bean,那么当从Spring上下文引用bean时,ApplicationContext将返回一个新实例。在第一个示例中,创建MessageService单例bean时,将创建MessageBuilder原型的一个新实例。但是,由于MessageService在Spring生命周期中只构造了一次(因为它是一个单例),所以它只请求注入MessageBuilder原型bean的一个引用。

换句话说,在第一个示例中,MessageBuilderbean只被实例化一次,因为它被注入(自动连线)到MessageService中一次。在注入的原型bean上执行的方法调用之后不会被代理到新的原型bean实例。

通过将proxyMode设置为target_classApplicationContext不会直接在另一个bean中注入新的原型bean实例,而是注入原型bean的代理。因此,当单例bean从注入的单例beanhtml" target="_blank">调用方法时,中间代理引用一个新的原型bean并调用该方法。

更多信息可以在Spring文档中找到:

如果您想将一个HTTP请求作用域bean注入(例如)更长生存期作用域的另一个bean,您可以选择注入一个AOP代理来代替作用域bean。也就是说,您需要注入一个代理对象,该代理对象公开与作用域对象相同的公共接口,但也可以从相关作用域(如HTTP请求)检索真正的目标对象,并将方法调用委托到真正的对象上。

 类似资料:
  • Spring在这里需要一些帮助。在我们的项目中,我们使用XML和注释配置(Spring 4.1) 最近我遇到了以下任务: 我有一个范围原型的bean列表,它们都实现了相同的接口。 此外,我有一个单独的bean,它有< code>execute方法。在方法内部,bean应该访问这些原型bean的列表。 每次执行“execute”方法时,我都想访问这些原型bean的不同实例)。在singleton中,

  • 问题内容: 我正在阅读Guice文档,并且遇到了一个标题为“ 消除周期”(推荐)的部分,这引起了我的极大兴趣,因为正是这个问题才导致我今天使用该文档。 基本上,要消除循环依赖性,您可以“将依赖性案例提取到单独的类中”。 好的,那里没有新内容。 因此,在示例中,我们有。 您有一个和一个,每个都需要引用的一个实例。这个概念没有问题,并且可以通过经典的依赖注入轻松实现: 这很容易,但是现在,我需要通过G

  • 前言 面向对象的三大特性 封装 继承 多态 原型链的知识 原型链是面向对象的基础,是非常重要的部分。有以下几种知识: 创建对象有几种方法 原型、构造函数、实例、原型链 instanceof的原理 new 运算符 创建对象有几种方法 方式一:字面量 var obj11 = {name: 'qianguyihao'}; var obj12 = new Object(name: 'qia

  • 问题内容: 例如,而不是做 你做 然后再添加“ ClassName”类型的对象 会自动将您的数组类型设置为 ? 问题答案: 否。泛型仅用于编译时。您只是失去了这张支票的好处。在运行时,所有通用信息都将被删除 换一种说法, 在运行时只是一个ArrayList。仅通过列表执行此操作的好处是,在编写代码时,编译器将检查您是否在列表中没有放置任何不适当的内容。

  • 我正在创建一个maven原型。在这里,我有一个原型项目,当用户调用以下命令时,它会为用户创建: MVN原型:生成-DArchetypeGroupId=xxx-DArchetypeArtifactId=Archtype-yyyy-DArchetypeVersion=1.1.0-S5-SNAPSHOT-DgroupId=zzz-DartifactId=pro11 在prototype pom中,我想使

  • 因此,关于原型范围的bean,我知道spring只是在将其移交给请求的bean之前将其进行生命周期处理。如果忘记了它。而且从逻辑上我可以理解,因为它是原型,它将只被每个请求使用(是的,不是http请求)但是spring容器为什么不保留原型bean的引用来管理完整的生命周期呢?