【问题标题】:How do I instantiate a new bean in a method call?如何在方法调用中实例化新 bean?
【发布时间】:2015-07-14 22:28:10
【问题描述】:

我有这样的事情:

@Controller
@Scope("session")
public class MyServletController {



    @Autowired
    private QueryRunner queryRunner;

    HashMap<String, Result> resultsMap; 

    @RequestMapping("/submitQuery")
    public ModelAndView submitQuery(HttpServletRequest request)
    {
    String sessionId = request.getParameter("sessionId");
    Result r = queryRunner.runQuery(sessionId, request.getParameter("otherdata"));
    resultsMap.put(sessionId, r);
    }



    @RequestMapping("/getResult")
    @ResponseBody
    public void saveTextLinks(HttpServletRequest request,
            HttpServletResponse response) throws IOException
    {
     String sessionId = request.getParameter("sessionId");      
     //return sessionId result from resultsMap
    }

 }


<bean id="queryRunner"
    class="com.myproject.QueryRunner"
    scope="prototype">
    <aop:scoped-proxy />


    <property name="errorMessageA" value="${error.messagea}"></property>
    <property name="errorMessageB" value="${error.messageb}"></property>

</bean>

我想要的是 Servlet 控制器是会话范围的,但是每个请求都需要实例化一个新的 QueryRunner 以便在一次发送多个请求时不会有任何干扰。

如何配置 Spring 来执行此操作?

诱人的解决方案就是去:

    @RequestMapping("/submitQuery")
    public ModelAndView submitQuery(HttpServletRequest request)
    {
    queryRunner = new QueryRunner(); //<-----
    String sessionId = request.getParameter("sessionId");
    Result r = queryRunner.runQuery(sessionId, request.getParameter("otherdata"));
    resultsMap.put(sessionId, r);
    }

【问题讨论】:

  • 您目前拥有的有什么问题? scope="request" 可能更合适,但除非您在其他代码中也使用QueryRunner,否则它是无用的。
  • @SotiriosDelimanolis scope=request 不适合,因为控制器需要为以后的查询保存数据。将编辑显示。
  • queryRunner bean 的请求范围不是 @Controller bean。
  • @SotiriosDelimanolis 现在看看这个。这是如何工作的,因为QueryRunner 不是 Servlet 控制器 - 它只是普通的 java 类。 Spring 是如何知道实例化它的?
  • 这是一个广泛的主题。简而言之:它使用反射。

标签: java spring instantiation


【解决方案1】:

[...] 但是每个请求都需要实例化一个新的 QueryRunner,以免一次发送多个请求时产生任何干扰

您的设置已经这样做了。你的 QueryRunner bean 被声明为

<bean id="queryRunner"
    class="com.myproject.QueryRunner"
    scope="prototype">
    <aop:scoped-proxy />

这意味着 Spring 将在任何请求此类 bean 的地方注入代理,例如

@Autowired
private QueryRunner queryRunner;

并且代理每次都会在内部将所有调用委托给新初始化的 bean。请注意,这意味着如果您执行类似的操作

Result r1 = queryRunner.runQuery(sessionId, request.getParameter("otherdata"));
Result r2 = queryRunner.runQuery(sessionId, request.getParameter("evenmoredata"));

这些runQuery 调用中的每一个调用都在不同的QueryRunner 实例上调用。

如果您想要一个跨越请求整个生命周期的 QueryRunner bean,只需声明该 bean 具有请求范围

<bean id="queryRunner"
    class="com.myproject.QueryRunner"
    scope="request">
    <aop:scoped-proxy />

在幕后(假设默认 MVC 配置),Spring 会将对 HttpServletRequest 的引用存储在可通过 static 实用程序类访问的 ThreadLocal 上下文中。它将实际的 bean 存储在 HttpServletRequest 属性中。

当您最终在代理上调用runQuery(存储在`@Autowired 字段中)时,Spring 将首先检查现有bean 的上下文。如果找到,它将使用它来调用您的方法。如果没有,它将初始化一个新实例,将其存储在上下文中,然后使用它。这样,前面的例子

Result r1 = queryRunner.runQuery(sessionId, request.getParameter("otherdata"));
Result r2 = queryRunner.runQuery(sessionId, request.getParameter("evenmoredata"));

实际上是在同一个 QueryRunner 实例上调用 runQuery

由于HttpServletRequest 属性在每次请求后都会被清除,因此您的请求范围的 bean 实例也会被清除。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-21
    相关资源
    最近更新 更多