每次从它调用方法时都会创建代理原型 bean [英] Proxied prototype bean is created every time a method is invoked from it

查看:39
本文介绍了每次从它调用方法时都会创建代理原型 bean的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在从 websockets 回调线程(与管理原始 HTTP 请求的线程不同的线程 - 因此是问题)访问请求范围的 bean 时遇到问题.您可以在此处阅读更多相关信息:在一个不同的线程(处理 websocket 流量)

I am having an issue accessing a Request scoped bean from a websockets callback thread (different thread than the one managing the original HTTP request - hence the issue). You can read more about it here: Accessing a request scoped bean in a different thread (that handles websocket traffic)

从那以后,我设法解决了这个问题(即使我对解决方案不是 100% 满意),但我看到了一种我不明白的行为.

I have since, managed to get around the problem (even though I am not 100% happy with the solution) but I am seeing a behaviour I do not understand.

我目前的解决方案如下:我已将 Bean 从请求范围更改为原型范围:

My current solution is as follows: I have changed the Bean from request scope to prototype scope:

@Bean
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public DbClientI getDbClient() throws StrykeDomainException {       
   DbClientI dbClient = requestContextHolder.getAttribute("dbClientReq", DbClientI.class);      
   if (dbClient != null) {
       logger.info("Retrieved DbClient proxy instance: {}", dbClient.hashCode());
   }    
   return dbClient;
}

我正在专用拦截器 (HandlerInterceptorAdapter) 中创建和销毁 Bean 背后的实例,并将其存储在 RequestContextHolder 中,以便可以通过我的 Bean 配置(如上)检索它.

I am creating and destroying the instance behind the Bean in a dedicated Interceptor (HandlerInterceptorAdapter) and storing it in the RequestContextHolder, so that it can be retrieved by my Bean configuration (above).

@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {

    boolean shouldProceedInterceptorChain = true;

    if (authenticatedUserInfo.isUserAuthenticated()) {

        try {
            DbClientI dbClient = dbCliFactory.createDbClientForCurrentRequest();
            requestContextHolder.setAttribute("dbClientReq", dbClient, true);                           
            dbClient.connect();     

        } catch (Exception e) {
            shouldProceedInterceptorChain = false;              
        }                           
    }

    return shouldProceedInterceptorChain;
}

@Override
public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) {

    //Note: we must use this method to disconnect instead of "postHandle", because postHandle will not run in case of an exception 
    if (authenticatedUserInfo.isUserAuthenticated()) {          
        DbClientI dbClient = appContext.getBean(DbClientI.class);            
        if (dbClient != null && dbClient.isConnected()) {
            dbClient.disconnect();                
            dbClient = null;
        }
    }
}

该解决方案有效,但是每次在代码中访问 dbClient Bean 时都会调用 getDbClient() 方法!从另一个 bean 中调用 dbClient bean 的方法会导致调用 getDbClient() 方法.我的理解是 getDbClient() 方法应该只在每次注入另一个 bean 时被调用,例如在将它注入另一个 bean 的构造函数时.这是 Spring 文档所说的:

The solution works, but the method getDbClient() is called every time the dbClient Bean is accessed in the code! Calling a method off the dbClient bean from within another bean causes the getDbClient() method to be called. My understanding is that the getDbClient() method should only get called each time it is injected into another bean, for example when injecting it in the constructor of another bean. This is what the Spring docs say:

bean 部署的非单例、原型作用域导致每次请求特定的 bean 时创建一个新的 bean 实例bean 被制造(即,它被注入另一个 bean 或被通过容器上的程序化 getBean() 方法调用请求)https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html#beans-factory-scopes-prototype

The non-singleton, prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made (that is, it is injected into another bean or it is requested via a programmatic getBean() method call on the container) https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html#beans-factory-scopes-prototype

功能方面这很好,因为在下面总是有相同的 DbClient 实例(由拦截器创建的实例),但每次使用 bean 时调用方法 getDbClient() 的事实肯定会影响性能.

Functionality-wise this is fine because underneath there is always the same instance of DbClient (the one created by the interceptor), but the fact that the method getDbClient() is called every time the bean is used is surely impacting performance.

如何更改我的代码,以便仅在将 bean 提供给另一个 bean 而不是每次使用时才调用 getDbClient()?
谢谢.

How can I change my code so that getDbClient() is only called when the bean is supplied to another bean and not every time it is used?
Thanks.

推荐答案

利用评论中的见解,并在我的最后做了一些测试,我意识到我误解的关键在于代理的使用.

Using the insight from the comments and doing a bit more testing on my end I realised that the key of my misunderstanding lied on the usage of the proxy.

确实,正如 Spring 文档所说,当使用 Prototype 范围时,只会在每次注入 bean 或调用 ApplicationContext.getBean() 时创建一个新实例.新实例不会仅仅通过简单地访问 bean 来创建,例如通过调用它的方法.

Indeed, as the Spring docs say, when using a Prototype scope a new instance will only get created every time the bean is injected or ApplicationContext.getBean() is called. A new instance will NOT get created just by simply accessing the bean, for example by calling a method on it.

然而,如果同一个 bean 也用代理属性装饰,注入时创建的是代理而不是类的实际实例.这导致每次访问 bean 时 Spring 都会调用配置方法"来检索实际的底层实例(在我的例子中是 getDbClient 方法)(例如:调用它的方法).

However, if the same bean is also decorated with the proxy property, what gets created on injection is a proxy and not an actual instance of the class. This results in Spring calling the "configuration method" to retrieve the actual underlying instance (in my case the getDbClient method) every time the bean is accessed (eg: calling a method on it).

请注意,上述内容适用于代理"原型 bean.对于代理"请求范围的 bean,将在请求开始时执行单个调用以获取实际实例.对 bean 的后续使用不会触发调用以检索 bean 的新实例.

Note that the above is true for "proxyed" prototype beans. For "proxyed" request scoped beans, a single call to get the actual instance will be performed at the start of the request. Subsequent use of the bean will not trigger a call to retrieve a new instance of the bean.

这篇关于每次从它调用方法时都会创建代理原型 bean的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆