【问题标题】:Spring Boot JSF IntegrationSpring Boot JSF 集成
【发布时间】:2018-02-21 14:03:25
【问题描述】:

环境:

雄猫 8

Spring Boot 1.5

JSF 2.2

Apache MyFaces

Spring MVC

代码:

我正在 Servlet 3.0 环境中集成 Spring Boot 和 JSF 2.2。

配置类:

JSFConfig.java - JSF 的配置。

@Configuration
@ComponentScan({"com.atul.jsf"})
public class JSFConfig {

        @Bean
        public ServletRegistrationBean servletRegistrationBean() {
            FacesServlet servlet = new FacesServlet();
            return new ServletRegistrationBean(servlet, "*.jsf");
        }

}

Spring Boot 主类:

@SpringBootApplication
@Import({ // @formatter:off 
    JPAConfig.class,
    ServiceConfig.class, // this contains UserServiceImpl.java class.
    WebConfig.class,
    JSFConfig.class,
})
public class SpringbootJpaApplication extends SpringBootServletInitializer{

    public static void main(String[] args) {
        SpringApplication.run(SpringbootJpaApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(SpringbootJpaApplication.class);
    }
}

托管 Bean:

UserBean.java - JSF 的托管 Bean

@ManagedBean
@SessionScoped
public class UserBean implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String name;
    @ManagedProperty(value="#{userServiceImpl}")
    private UserServiceImpl userServiceImpl;

    public void addUser(){      
        System.out.println("User Gets added "+this.name);       
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public UserServiceImpl getUserServiceImpl() {
        return userServiceImpl;
    }

    public void setUserServiceImpl(UserServiceImpl userServiceImpl) {
        this.userServiceImpl = userServiceImpl;
    }
}

小脸:

home.xhtml - 主页

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html">

    <h:head>
        <title>JSF 2.0 Hello World</title>
    </h:head>
    <h:body>
        <h2>JSF 2.0 Hello World Example - hello.xhtml</h2>
        <h:form>
           <h:inputText value="#{userBean.name}"></h:inputText>
           <h:commandButton value="Submit" action="#{userBean.addUser}"></h:commandButton>
        </h:form>
    </h:body>
</html>

faces-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
              version="2.2">

    <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    </application>
    <lifecycle>
        <phase-listener>org.springframework.web.jsf.DelegatingPhaseListenerMulticaster</phase-listener>
    </lifecycle>

</faces-config>

问题:

1) 当我在home.xhtml , userBean.addUser 中提交表单时被调用。 2)userBean.name 使用用户输入的值进行设置。 3)但userServiceImpl 为NULL。 4)这是否意味着 Spring 和 JSF 没有集成?我还注册了SpringBeanFacesELResolver,如

中所述
faces-config.xml 

我还尝试从 UserBean.java 中删除所有 JSF 特定注解,并仅使用 Spring 特定注解,如下所示 -

 @Component
    @SessionScoped 
    public class UserBean implements Serializable{

        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private String name;
        @Autowired
        private UserServiceImpl userServiceImpl;


    }

但是当我提交表单时,我收到 #{userBean) 的目标无法访问错误。这意味着 userBean 对于 Spring 是不可发现的

5)我在这里遗漏了什么吗? 6)我没有使用 Spring Boot 提供的嵌入式 tomcat

【问题讨论】:

    标签: spring spring-boot jsf-2 jsf-2.2


    【解决方案1】:

    这就是我让 JSF 使用 Spring Boot 的方式(完整的 sample project at Github, updated with JSF 2.3 and Spring Boot 2):

    1.依赖关系

    除了标准的 web starter 依赖项之外,您还需要包含标记为已提供的 tomcat 嵌入式 jasper(感谢@Fencer 在here 中的评论)。否则,您将在应用程序启动时遇到异常,因为 JSF 取决于 JSP 处理器(另请参阅我的答案末尾的第一个链接)。

    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <scope>provided</scope>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    2。 Servlet 注册

    注册 JSF servlet 并将其配置为在启动时加载(不需要 web.xml)。如果使用 JSF 2.2,最好使用 *.xhtml 映射,至少在使用 facelets 时:

    @Bean
    public ServletRegistrationBean servletRegistrationBean() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(
                new FacesServlet(), "*.xhtml");
        servletRegistrationBean.setLoadOnStartup(1);
        return servletRegistrationBean;
    }
    

    让你的配置类实现ServletContextAware,这样你就可以设置你的初始化参数。这里你必须强制 JSF 加载配置:

    @Override
    public void setServletContext(ServletContext servletContext) {
        servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration",
                Boolean.TRUE.toString());
        servletContext.setInitParameter("javax.faces.FACELETS_SKIP_COMMENTS", "true");
        //More parameters...
    }
    

    3。 EL 集成

    在 faces-config.xml 中声明 EL resolver。这将成为您的视图文件和托管 bean 属性和方法之间的粘合剂:

    <?xml version="1.0" encoding="UTF-8"?>
    <faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
        version="2.2">
    
        <application>
            <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver
            </el-resolver>
        </application>
    
    </faces-config>
    

    4.视图范围

    编写自定义 Spring 范围来模拟 JSF 视图范围(请记住,您的 bean 将由 Spring 管理,而不是 JSF)。它应该看起来像这样:

    public class ViewScope implements Scope {
    
        @Override
        public Object get(String name, ObjectFactory<?> objectFactory) {
            Map<String, Object> viewMap = FacesContext.getCurrentInstance()
                .getViewRoot().getViewMap();
            if (viewMap.containsKey(name)) {
                return viewMap.get(name);
            } else {
                Object object = objectFactory.getObject();
                viewMap.put(name, object);
                return object;
            }
        }
    
        @Override
        public String getConversationId() {
            return null;
        }
    
        @Override
        public void registerDestructionCallback(String name, Runnable callback) {
    
        }
    
        @Override
        public Object remove(String name) {
            return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
        }
    
        @Override
        public Object resolveContextualObject(String arg0) {
            return null;
        }
    
    }
    

    并将其注册到您的配置类中:

    @Bean
    public static CustomScopeConfigurer viewScope() {
        CustomScopeConfigurer configurer = new CustomScopeConfigurer();
        configurer.setScopes(
                new ImmutableMap.Builder<String, Object>().put("view", new ViewScope()).build());
        return configurer;
    }
    

    5.准备好了!

    现在您可以通过以下方式声明您的托管 bean。请记住使用@Autowired(最好在构造函数中)而不是@ManagedProperty,因为您正在处理Spring Beans。

    @Component
    @Scope("view")
    public class MyBean {
    
        //Ready to go!
    
    }
    

    仍有待实现

    我无法让 JSF 特定的注释在 Spring Boot 上下文中工作。所以@FacesValidator@FacesConverter@FacesComponent等不能用。尽管如此,还是有机会在 faces-config.xml 中声明它们(参见 xsd),这是老式的方式。


    另请参阅:

    【讨论】:

    • Xtreme Biker 您是否找到了让 JSF 注释工作的方法?也许是 Spring Boot 2.0?
    • 不,但是自从我写了这个答案以来,我还没有挖掘太多。由于是 Spring 在这里管理你的 bean,你可以尝试一些注释生成单例,如 @Component。
    • @XtremeBiker 真的需要 Jasper 依赖项吗?
    • @John 确实如此。删除它会抛出异常。
    • 该应用程序还依赖于starter-web。该依赖项已经提供了嵌入式 tomcat。这里的问题与嵌入式 tomcat 在编译时不提供 jasper 功能有关,这似乎是 JSF 所要求的。因此,添加该依赖项可以解决它。另见:*.com/a/25777709/1199132