这就是我让 JSF 与 Spring Boot 一起工作的方式(完整Github 上的示例项目,已使用 JSF 2.3 和 Spring Boot 2 进行更新 https://github.com/xtremebiker/jsf-spring-boot):
1. 依赖关系
除了标准的 Web Starter 依赖项之外,您还需要包含标记为提供的 tomcat 嵌入式 jasper(感谢 @Fencer 在here https://stackoverflow.com/a/42155035/1199132)。否则,您将在应用程序启动时遇到异常,因为 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 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/context/ServletContextAware.html这样您就可以设置初始化参数。这里您必须强制 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集成
声明EL旋转变压器 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/jsf/el/SpringBeanFacesELResolver.html在 faces-config.xml 中。这将成为视图文件与托管 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 Bean。
@Component
@Scope("view")
public class MyBean {
//Ready to go!
}
仍有待实现
我无法在 Spring Boot 上下文中使用 JSF 特定注释。所以@FacesValidator
, @FacesConverter
, @FacesComponent
、等等不能使用。尽管如此,还是有机会在 faces-config.xml 中声明它们(请参阅xsd http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd),还是老办法。
也可以看看:
- Spring Boot 与 JSF;找不到工厂 javax.faces.context.FacesContextFactory 的备份 https://stackoverflow.com/questions/25479986/spring-boot-with-jsf-could-not-find-backup-for-factory-javax-faces-context-face/25509937#25509937
- 将 JSF 2.0 的 ViewScope 移植到 Spring 3.0 https://www.primefaces.org/port-jsf-2-0s-viewscope-to-spring-3-0/
- JSP 文件未在 Spring Boot Web 应用程序中呈现 https://stackoverflow.com/questions/20602010/jsp-file-not-rendering-in-spring-boot-web-application