【问题标题】:Dependency Injection in Struts2 Accessing Session Scoped BeansStruts2 中的依赖注入访问 Session Scoped Bean
【发布时间】:2013-06-19 02:29:51
【问题描述】:

最近我需要在 Struts2 中使用 DI。我知道它使用自己的 DI 实现,如 Guice 但不是 Guice,因为我找不到一些适合设置注入 bean 范围的注释。简而言之,我创建了一个 bean

//@Repository
//@Scope("session")
public class Session {

    private Map<String, Object> map = new HashMap<>();

    public Map<String, Object> getMap() {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }
}

我已经评论了 Spring bean 使用的注释。我通过 spring DI 成功创建了同一个 bean,并设置了我的对象被注入的范围。现在,我想对 Struts2 和 DI 做同样的事情。为此,我在 struts.xml

中创建了 bean 定义
<bean name="session" class="jspbean.struts.Session" scope="session"/>

创建该 bean 并将其注入到我的操作中的简单操作

public class DefaultAction extends ActionSupport {

    private Session session;

    //  @Autowired
    @Inject("session")
    public void setSession(Session session) {
        this.session = session;
    }

    public Session getSession() {
        return session;
    }

    private Map<String, String> myMap = new HashMap<String, String>();

    public Map<String, String> getMyMap() {
        return myMap;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    @Override
    public String execute() throws Exception {
        //populate my bean with sample data
        myMap.put("q1", "Question1");
        myMap.put("q2", "Question2");
        session.getMap().put("myMap", myMap);
        return SUCCESS;
    }
}

在 JSP 中,我在会话 bean 上使用简单的迭代器

<s:iterator value="session.map['myMap']">
  <s:textfield name="myMap['%{key}']" value="%{value}" theme="simple" size="10" /><br>
</s:iterator>

现在,当我运行这个简单的应用程序时,我遇到了异常

Stacktraces
Unable to instantiate Action, jspbean.struts.DefaultAction, defined for '' in namespace '/'java.lang.IllegalStateException: Scope strategy not set. Please call Container.setScopeStrategy().

    com.opensymphony.xwork2.DefaultActionInvocation.createAction(DefaultActionInvocation.java:316)
    com.opensymphony.xwork2.DefaultActionInvocation.init(DefaultActionInvocation.java:397)
    com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:194)
    org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:63)
    org.apache.struts2.impl.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:39)
    com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:58)
    org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:536)
    org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
    org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    java.lang.Thread.run(Unknown Source)


java.lang.RuntimeException: java.lang.IllegalStateException: Scope strategy not set. Please call Container.setScopeStrategy().

    com.opensymphony.xwork2.inject.ContainerImpl$MethodInjector.inject(ContainerImpl.java:301)
    com.opensymphony.xwork2.inject.ContainerImpl.inject(ContainerImpl.java:492)
    com.opensymphony.xwork2.inject.ContainerImpl$6.call(ContainerImpl.java:530)
    com.opensymphony.xwork2.inject.ContainerImpl$6.call(ContainerImpl.java:528)
    com.opensymphony.xwork2.inject.ContainerImpl.callInContext(ContainerImpl.java:584)
    com.opensymphony.xwork2.inject.ContainerImpl.inject(ContainerImpl.java:528)
    com.opensymphony.xwork2.ObjectFactory.injectInternalBeans(ObjectFactory.java:139)
    com.opensymphony.xwork2.spring.SpringObjectFactory.autoWireBean(SpringObjectFactory.java:208)
    com.opensymphony.xwork2.spring.SpringObjectFactory.buildBean(SpringObjectFactory.java:183)
    com.opensymphony.xwork2.spring.SpringObjectFactory.buildBean(SpringObjectFactory.java:154)
    com.opensymphony.xwork2.ObjectFactory.buildBean(ObjectFactory.java:151)
    com.opensymphony.xwork2.ObjectFactory.buildAction(ObjectFactory.java:121)
    com.opensymphony.xwork2.DefaultActionInvocation.createAction(DefaultActionInvocation.java:297)
    com.opensymphony.xwork2.DefaultActionInvocation.init(DefaultActionInvocation.java:397)
    com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:194)
    org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:63)
    org.apache.struts2.impl.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:39)
    com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:58)
    org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:536)
    org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
    org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    java.lang.Thread.run(Unknown Source)


java.lang.IllegalStateException: Scope strategy not set. Please call Container.setScopeStrategy().

    com.opensymphony.xwork2.inject.InternalContext.getScopeStrategy(InternalContext.java:53)
    com.opensymphony.xwork2.inject.Scope$5$1.create(Scope.java:130)
    com.opensymphony.xwork2.inject.ContainerImpl$ParameterInjector.inject(ContainerImpl.java:469)
    com.opensymphony.xwork2.inject.ContainerImpl.getParameters(ContainerImpl.java:484)
    com.opensymphony.xwork2.inject.ContainerImpl.access$000(ContainerImpl.java:34)
    com.opensymphony.xwork2.inject.ContainerImpl$MethodInjector.inject(ContainerImpl.java:299)
    com.opensymphony.xwork2.inject.ContainerImpl.inject(ContainerImpl.java:492)
    com.opensymphony.xwork2.inject.ContainerImpl$6.call(ContainerImpl.java:530)
    com.opensymphony.xwork2.inject.ContainerImpl$6.call(ContainerImpl.java:528)
    com.opensymphony.xwork2.inject.ContainerImpl.callInContext(ContainerImpl.java:584)
    com.opensymphony.xwork2.inject.ContainerImpl.inject(ContainerImpl.java:528)
    com.opensymphony.xwork2.ObjectFactory.injectInternalBeans(ObjectFactory.java:139)
    com.opensymphony.xwork2.spring.SpringObjectFactory.autoWireBean(SpringObjectFactory.java:208)
    com.opensymphony.xwork2.spring.SpringObjectFactory.buildBean(SpringObjectFactory.java:183)
    com.opensymphony.xwork2.spring.SpringObjectFactory.buildBean(SpringObjectFactory.java:154)
    com.opensymphony.xwork2.ObjectFactory.buildBean(ObjectFactory.java:151)
    com.opensymphony.xwork2.ObjectFactory.buildAction(ObjectFactory.java:121)
    com.opensymphony.xwork2.DefaultActionInvocation.createAction(DefaultActionInvocation.java:297)
    com.opensymphony.xwork2.DefaultActionInvocation.init(DefaultActionInvocation.java:397)
    com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:194)
    org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:63)
    org.apache.struts2.impl.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:39)
    com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:58)
    org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:536)
    org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
    org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    java.lang.Thread.run(Unknown Source)

异常说我需要设置范围策略。所以,我的问题是这个范围策略是什么以及如何在我的简单应用程序中实现它。另外,还有注解@Scoped,这个注解在我的情况下如何应用?

我的示例参考:

  1. bean configuration

【问题讨论】:

  • AFAIK Struts2 内部注入仅用于框架,它提供给明显的范围(在查看 struts-default.xml 时很明显),这些是“单例”和“默认”(可能按需创建)。出于兴趣,有人努力完全转向 guice 作为内部注入机制。同时,如果您需要“会话”范围,则需要使用第三方 DI 进行注入。不建议在您的操作类中使用 S2 内部机制,因为这可能会发生变化,并且如前所述,我们正在努力改变这部分。
  • 目前在 Struts 2 中使用的旧的、被黑的、内部版本的 Guice 对 Web 应用程序或 servlet 规范一无所知。就目前而言,仅使用 S2 的 DI 无法实现您想要做的事情。如果您必须,则将 bean factory 注入拦截器并将 bean 设置为会话,或类似的东西。
  • 我不知道;在粗略的代码行走之后,我看不到任何处理将配置级会话范围的 bean 放入实例化操作的东西,但我只花了 15 分钟的时间寻找。配置类在org.apache.struts2.config.BeanSelectionProvider;您最好的选择是在启动期间逐步执行其中的一些代码,以查看 (a) 在初始化期间会话范围的配置 bean 发生了什么,例如,它们保存在哪里,以及 (b) 开始查看请求处理和操作实例化看到的机制......
  • ... (c) 注入发生的位置,以及 (d) 注入会话范围配置 bean 失败的具体情况。就像我说的,我不知道在会话中确定它们的范围实际上有效,但这并不意味着它没有
  • 这不能回答您的问题,但对于替代会话范围解决方案,您可以查看此处code.google.com/p/struts2-conversation/wiki/SessionScope(免责声明,我编写了链接的 wiki 页面)

标签: java struts2 dependency-injection session-scope scope-strategy


【解决方案1】:

我相信 Luiggi 的评论是正确的。 “@Inject”需要在字段值本身上,而不是在 setter 上。

 @Inject("session")
 private Session session;

只要您在 struts.xml 中定义了“会话”bean,或者在 Struts 容器中注册了它,它就应该能够找到并注入它。从你的解释看来是这样。

有关更多具体信息,请查看关于 Struts 用户列表的讨论:Struts user question on built-in DI

【讨论】:

  • 我不相信,如果你这不是我熟悉的东西。
  • 对不起,意思是说如果有可能这不是我熟悉的东西。我以前只将 Struts 容器用于一些相当基本的事情。如果我需要的不仅仅是非常基本的 DI 功能,我通常只会去 Spring 或 Guice。您也许可以在需要会话时使用拦截器进行设置,但这会非常粗糙。
  • 是的,我们所做的一切都只是默认行为,但我们所做的并不需要更改 bean 的范围。
【解决方案2】:

让我们从查看文档开始了解什么是Scope.Strategy。它说

可插入的范围策略。使用户能够提供自定义 请求、会话和向导范围的实现。实施和 传递给 Container.setScopeStrategy(com.opensymphony.xwork2.inject.Scope.Strategy)

好的,假设我想实现会话范围。然后我需要知道我可以实现它的地方。该框架有它的扩展点,您可以在其中插入扩展或简单地扩展default 实现并提供您自己的自定义实现。这很容易通过查看BeanSelectionProvider 来完成。然后分析堆栈跟踪,我认为最好的办法是扩展DefaultActionProxyFactory。扩展它还需要扩展DefaultActionProxy

public class MyActionProxyFactory extends DefaultActionProxyFactory {

    public MyActionProxyFactory() {
        super();
    }

    @Override
    public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {

        MyActionProxy proxy = new MyActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
        container.inject(proxy);
        container.setScopeStrategy(new MyScopeStrategy());
        proxy.prepare();
        return proxy;
    }
}

public class MyActionProxy extends DefaultActionProxy {

    protected MyActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
        super(inv, namespace, actionName, methodName, executeResult, cleanupContext);
    }

    @Override
    protected void prepare() {
        super.prepare();
    }
}

public class MyScopeStrategy implements Scope.Strategy {

    @Override
    public <T> T findInRequest(Class<T> type, String name, Callable<? extends T> factory) throws Exception {
        return null;
    }

    @Override
    public <T> T findInSession(Class<T> type, String name, Callable<? extends T> factory) throws Exception {

        ActionContext context = ActionContext.getContext();
        SessionMap<String, T> sessionMap = (SessionMap<String, T>) context.getSession();

        if (sessionMap == null) {
            sessionMap = new SessionMap<String, T>(ServletActionContext.getRequest());
            context.setSession((Map<String, Object>) sessionMap);
        }

        T obj = sessionMap.get(name);

        if (obj == null) {
            obj = factory.call();
            sessionMap.put(name, obj);
        }
        return obj;
    }

    @Override
    public <T> T findInWizard(Class<T> type, String name, Callable<? extends T> factory) throws Exception {
        return null;
    }
}

在配置文件struts.xml你应该设置属性

<constant name="struts.actionProxyFactory" value="jspbean.struts.factory.MyActionProxyFactory"/>

这就是您在会话范围内注入 bean Session 所需的全部内容。可以对其他范围进行类似的实现。请注意,像 singlton(默认使用)、线程和默认等其他范围似乎不需要这种可插入的扩展。最后一句话是关于@Scoped注解。如果您通过 xml 配置提供 bean,则不会使用它。但是,如果您以任何其他方式为 bean 提供 ContainerBuilder,它就能够在其上找到注释并设置相应的范围。

【讨论】:

  • 你好 Roman,虽然我不确定现在有多少人还在使用 Struts,但我在 GitHub 上的 Struts2 演示项目中重复了你在此处发布的想法:github.com/nineninesevenfour/struts2-demo。我希望你没问题。
  • 希望,它会帮助有兴趣挖掘框架内部的人。另外我在 GitHub 上有一个 project,我在其中测试了这个解决方案。请记住,如果你觉得这个答案有帮助,你也应该投票赞成这个问题。
猜你喜欢
  • 1970-01-01
  • 2014-01-04
  • 2013-01-17
  • 1970-01-01
  • 2019-04-22
  • 2014-06-28
  • 1970-01-01
  • 2012-08-02
  • 1970-01-01
相关资源
最近更新 更多