【问题标题】:Spring JSF integration: how to inject a Spring component/service in JSF managed bean?Spring JSF 集成:如何在 JSF 托管 bean 中注入 Spring 组件/服务?
【发布时间】:2016-05-27 13:03:25
【问题描述】:

我了解托管 bean 的工作方式类似于控制器,因为您唯一的任务是将视图层与模型“链接”。

要将 bean 用作托管 bean,我必须声明 @ManagedBeanannotation,这样做我可以直接与 bean 通信 JSF。

如果我想在这个 managedBean 中注入一些组件(来自 Spring),我有两种可能的方法:

  1. 在 ManagedBean 中选择属性(如“BasicDAO dao”)并在属性上方声明@ManagedProperty(#{"basicDAO"})。这样做,我从 ManagedBean 中的 Spring 注入 bean "basicDAO"

  2. 在 ManagedBean 类中声明 @Controller,然后我将拥有 @ManagedBean@Controller 注释,一起使用。在 "BasicDAO dao" 属性中,我必须使用 Spring 中的 @Autowired

我的理解正确吗?

【问题讨论】:

    标签: spring jsf dependency-injection integration managed-bean


    【解决方案1】:

    通过利用当前 WebApplication 上下文的 getBean,您可以在不使用 @Autowired 的情况下自动装配单个 bean。

    更多详情请参考@BalusC 的回答。这只是对他的示例的轻微修改:

    @ManagedBean // JSF-managed.
    @ViewScoped // JSF-managed scope.
    public class GoodBean implements Serializable {
    
        // @Autowired // No Autowired required
        private SpringBeanClass springBeanName; // No setter required.
    
        @PostConstruct
        private void init() {
            WebApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
            this.springBeanName = ctx.getBean(SpringBeanClass.class);
            // springBeanName is now available.
        }
    }
    

    【讨论】:

      【解决方案2】:

      @ManagedBean@Controller

      首先,你应该选择一个框架来管理你的bean。您应该选择 JSF 或 Spring(或 CDI)来管理您的 bean。虽然以下方法有效,但它从根本上是错误的:

      @ManagedBean // JSF-managed.
      @Controller // Spring-managed.
      public class BadBean {}
      

      您最终会得到两个完全独立的同一个托管 bean 类的实例,一个由 JSF 管理,另一个由 Spring 管理。当您将其引用为#{someBean} 时,并不清楚实际上会在 EL 中使用哪个。如果您在faces-config.xml 中注册了SpringBeanFacesELResolver,那么它将是Spring 管理的,而不是JSF 管理的。如果您没有,那么它将是 JSF 管理的。

      此外,当您从 javax.faces.* 包中声明 JSF 托管 bean 特定范围时,例如 @RequestScoped@ViewScoped@SessionScoped@ApplicationScoped,它只会被 @ManagedBean 识别和使用。 @Controller 不会理解它,因为它需要自己的 @Scope 注释。缺席时默认为单例(应用程序范围)。

      @ManagedBean // JSF-managed.
      @ViewScoped // JSF-managed scope.
      @Controller // Spring-managed (without own scope, so actually becomes a singleton).
      public class BadBean {}
      

      当您通过 #{someBean} 引用上述 bean 时,它将返回 Spring 管理的应用程序范围 bean,而不是 JSF 管理的视图范围 bean。


      @ManagedProperty@Autowired

      特定于 JSF 的 @ManagedProperty 仅适用于 JSF 管理的 bean,即当您使用 @ManagedBean 时。 Spring 特定的 @Autowired 仅适用于 Spring 管理的 bean,即当您使用 @Controller 时。以下方法或多或少等效,不能混合使用:

      @ManagedBean // JSF-managed.
      @RequestScoped // JSF-managed scope.
      public class GoodBean {
      
          @ManagedProperty("#{springBeanName}")
          private SpringBeanClass springBeanName; // Setter required.
      }
      
      @Component // Spring-managed.
      @Scope("request") // Spring-managed scope.
      public class GoodBean {
      
          @Autowired
          private SpringBeanClass springBeanName; // No setter required.
      }
      

      请注意,当您按照javadocfaces-config.xml 中注册SpringBeanFacesELResolver 时,

      <application>
          ...
          <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
      </application>
      

      因此您可以通过#{springBeanName} 在EL 中引用Spring 托管bean,然后您也可以在@ManagedProperty 中引用它们,因为它基本上设置了给定EL 表达式的评估结果。反过来,通过@Autowired 注入 JSF 托管 bean,则完全不受支持。但是,当您从 SpringBeanAutowiringSupport 扩展您的 bean 时,您可以在 JSF 托管 bean 中使用 @Autowired。这将在构造函数调用期间自动在 Spring 自动装配上下文中注册 JSF 托管 bean 实例,这意味着 @Autowired 的所有内容都将在 @PostConstruct 及更高版本中可用。

      @ManagedBean // JSF-managed.
      @ViewScoped // JSF-managed scope.
      public class GoodBean extends SpringBeanAutowiringSupport implements Serializable {
      
          @Autowired
          private SpringBeanClass springBeanName; // No setter required.
      
          @PostConstruct
          private void init() {
              // springBeanName is now available.
          }
      }
      

      或者当您的架构不允许从不同的基类扩展 bean 时,您始终可以在 Spring 自动装配上下文中手动注册 JSF 托管 bean 实例,如下所示。有关技巧,另请参阅 How to integrate JSF 2 and Spring 3 (or Spring 4) nicely

      @ManagedBean // JSF-managed.
      @ViewScoped // JSF-managed scope.
      public class GoodBean implements Serializable {
      
          @Autowired
          private SpringBeanClass springBeanName; // No setter required.
      
          @PostConstruct
          private void init() {
              FacesContextUtils
                  .getRequiredWebApplicationContext(FacesContext.getCurrentInstance())
                  .getAutowireCapableBeanFactory().autowireBean(this);
      
              // springBeanName is now available.
          }
      }
      

      @XxxScoped@Scope

      Spring 的 @Scope 对 JSF 作用域的支持有限。 JSF 的@ViewScoped 没有等价物。您基本上要么自行开发自己的范围,要么坚持在 Spring 自动装配上下文中手动注册 JSF 托管 bean 实例,如上所示。

      而且,另一方面,Spring WebFlow 在 JSF 2.2 中通过新的 @FlowScoped 注释被接管。因此,如果您碰巧已经使用 JSF 2.2,那么如果您只想要流范围,则不一定需要使用 Spring WebFlow。


      CDI - 试图统一一切

      从 Java EE 6 开始,CDI 作为 Spring DI 的标准替代品提供。它分别具有 @Named@Inject 注释以及它自己的一组范围。我不确定它如何与 Spring 交互,因为我不使用 Spring,但 @Inject@ManagedBean 内工作,而 @ManagedProperty@ManagedBean 内可以引用 @Named bean。另一方面,@ManagedProperty@Named bean 中不起作用。

      CDI 的目的是将所有不同的 bean 管理框架统一到一个规范/接口中。 Spring 可能是一个完整的 CDI 实现,但他们选择仅部分实现它(仅支持 JSR-330 javax.inject.*,但不支持 JSR-299 javax.enterprise.context.*)。另见Will Spring support CDI?this tutorial

      JSF 将迁移到 CDI 进行 bean 管理,并在未来的版本中弃用 @ManagedBean 和朋友。

      @Named // CDI-managed.
      @ViewScoped // CDI-managed scope.
      public class BetterBean implements Serializable {
      
          @Inject
          private SpringBeanClass springBeanName; // No setter required.
      
          @PostConstruct
          private void init() {
              // springBeanName is now available.
          }
      }
      

      另见:

      【讨论】:

      • 您说:“JSF 特定的 @ManagedProperty 仅在 JSF 托管的 bean 中有效,即当您使用 @ManagedBean 时”。但是我使用@ManagedProperty 来引用Spring-Bean,这很好用。我怎么说:我使用的是@ManagedProperty(#{'basicDAO'}),而bean'basicDAO'是一个@Repository bean。
      • @ManagedProperty 是将spring与jsf(注入bean)集成的方法(我遇到过)。我相信什么(如果我错了,请纠正我) org.springframework.web.jsf.el.SpringBeanFacesELResolver 将 jsf bean 传播到 Spring Bean。通常我们创建构成jsf的Spring容器(org.springframework.web.context.ContextLoaderListener)。正如@BalusC 所说,到目前为止,Spring 不支持 Java EE 6 CDI。我非常想要 @ConversationalScope,它不受具有 JSF 的弹簧容器支持。
      • 因此,ManagedProperty 类似于@Autowired,但在 JSF 中。 :D
      • 一个 54 个赞成的答案被一个引用已弃用注释的新答案替换为接受的答案...哇...
      • @BalusC 关于 MVC 概念,您将 JSF 托管 bean 视为视图还是控制器?
      【解决方案3】:

      还有另一种在 JSF 管理的 bean 中使用 Spring 管理的 bean 的方法,只需从 SpringBeanAutowiringSupport 扩展您的 JSF bean,Spring 将处理依赖项注入。

      @ManagedBean // JSF-managed.
      @ViewScoped // JSF-managed scope.
      public class GoodBean extends SpringBeanAutowiringSupport {
      
          @Autowired
          private SpringBeanClass springBeanName; // No setter required.
      
          // springBeanName is now available.
      }
      

      【讨论】:

      • 我知道 @ManagedBean 在 JSF 中已弃用。但是,当您看到提问者在问题中提到@ManagedBean 时,您又看到“54 个赞成的答案”也提到了@ManagedBean。正如你所看到的,我以“还有另一种方式”开始我的回答。老实说,我没想到我的答案会被接受,而不是@BalusC 的出色答案。我只是想展示另一种更简单的方法。我不明白我怎么能配得上那些“我几乎倾向于投反对票”的话:)“问这个问题的同一个人?”大声笑,不 :D 你以前试过吗? :)
      【解决方案4】:

      执行此操作的简单方法是通过 XML。我在已经制作的 jsf 托管 bean 中使用了 @Component,但 @Autowired 不起作用,因为托管 bean 已经存在于 faces-config.xml 中。如果必须在 xml 文件中保留该托管 bean 定义及其托管属性,则建议将 spring bean 作为另一个托管属性添加到托管 bean 标记中。这里的 spring bean 是在 spring-config.xml 中定义的(可以在某个地方交替自动装配)。请参考 https://stackoverflow.com/a/19904591/5620851

      由我编辑。我建议要么通过注解@Managed 和@Component 完全实现它,要么通过xml 来实现它。

      【讨论】:

        猜你喜欢
        相关资源
        最近更新 更多