@ManagedBean @Component class 内的@Autowired 服务在 JSF 请求期间为空

@Autowired service inside a @ManagedBean @Component class is null during JSF request

我尝试将 Spring 3(MVC) 与 JSF 2 结合使用。我在 Spring 和 JSF 方面有一些经验,但之前从未尝试加入它们。最后我有2个文件

@ManagedBean(name = "userBean")
@Scope
@Component
public class someBean {

  @Autowired
  private TestService testService;

  public void printString() {
    System.out.println(testService.getString());
  }
}

@ManagedBean(name = "studentBean")
@Scope
@Component
public class StudentBean {

  @Autowired
  private TestService testService;

  public void printString() {
    System.out.println(testService.getString());
  }
}

对于这些文件,我有 spring、jsf 和 web.xml 的正确配置。并有 .xhtml 页面,我在其中为 'someBean' 和 'StudentBean' 启动 printString()。我在第一种情况下有 NPE,在第二种情况下在控制台中有 'some string'。 原因很简单 - Spring 上下文和 JSF 中的 bean 名称不同。

之后所有问题都完成了
@Component => @Component("userBean") 
public class someBean {

在调试中我看到了

private TestService testService;

@Autowired
public void setTestService(TestService testservice) {
  this.testService = testService;
}

当 JSF bean 创建 testService 时设置不为空,但在

时在 JSF 生命周期期间它为空
public void pringString() {
  testService.blah();
}

测试服务为空。这是我无法理解的。是否有人深入了解 Spring 和 JSF 来详细描述这种情况?

JSF 和 Spring 都可以充当 bean 容器。 @ManagedBean 注释指示 JSF 托管 bean 工具创建 class 的新实例,并在给定的名称下管理它。 @Component 注释指示 Spring ApplicationContext 创建 class 的新实例,并在给定的名称下管理它。也就是说,JSF 和 Spring 都创建了 class 的实例,JSF 可以通过 EL 访问,但是 Spring 可以注入它的依赖项(因为,作为 spring 注释,@Autowired 不被 JSF 托管 bean 工具理解。

因此您可以选择:对所有内容使用 JSF 托管 bean 工具(我不推荐,因为它相当有限),对所有内容使用 CDI(这是一个选项,但不使用 Spring), 或者对所有事情使用 Spring (我通常这样做), 通过删除 @ManagedBean 注释, 并通过在 EL 中注册 SpringBeanFacesELResolver 使 Spring beans 可访问你的脸-config.xml。 Spring 参考手册在 section 19.3.1 中对此进行了描述。

我认为testService 为null 的原因是因为在构造托管bean 时尚未实例化testService。因此,您可以使用 @PostConstruct 将 Spring bean 注入到托管 bean 中。

@ManagedBean(name = "userBean")
@Scope
@Component
public class someBean {

    @Autowired
    private TestService testService;

    public void printString() {
        System.out.println(testService.getString());
    }

    @PostConstruct
    private void init() {
    ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        ServletContext servletContext = (ServletContext) externalContext.getContext();
    WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).
                                   getAutowireCapableBeanFactory().
                                   autowireBean(this);
    }
}

@Service
public class TestService {
    ......
}

我有同样的问题,这是由于@ManagedBean 属性 的名称 annotation.My backing bean 看起来像这样

@Component
@ManagedBean(name="mainBean")
@SessionScoped
public class MainManagedBean{...}

如您所见,为 bean 指定的名称 (mainBean) 与支持 bean 应具有的默认名称 (mainManagedBean) 不同。

我已通过将 属性 名称设置为 "mainManagedBean" 解决了这个问题。我的bean变成这样:

@Component
@ManagedBean(name="mainManagedBean")
@SessionScoped
public class MainManagedBean{...}

希望对您有所帮助