我接触Spring只有几个月了,最近在浏览指南部分时发现了Spring Boot。这些指南非常容易完成,并且很好地初步掌握了项目的基本(和令人敬畏的(想法,即能够以最少的配置构建和部署企业级应用程序,同时坚持广泛的Spring/JEE的良好实践。我对将 Spring Boot 用于测试项目非常感兴趣,因为有了它,它们的设置和运行变得更加容易和快捷,并且仍然非常接近我的生产环境。我目前正在尝试使用 Spring Boot 和 Primefaces 作为我选择的视图技术构建一个项目,但这种设置显然还没有像 Thymeleaf 那样开箱即用,在类路径中拥有它的二进制文件就足够了。我尝试包括以下gradle/maven工件,但没有成功:
- 素数
- JSF-API
- JSF-IMPL
- El-API
Spring Boot 的嵌入式 Tomcat 服务器开始时没有明显的错误,但在尝试在浏览器中打开/view 等页面后,嵌入式服务器会显示以下错误消息:HTTP Status 500 - Circular view path: would dispatch back to the current handler URL again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
在几个不同的地方搜索后,我仍然找不到有关如何设置此类项目的任何资源/教程。以下是我的build.gradle文件的内容:
buildscript {
repositories {
maven { url "http://repo.spring.io/libs-snapshot" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.0.0.RC4")
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'spring-boot'
idea {
module {
downloadJavadoc = true
}
}
group = 'com.hello'
version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
mavenLocal()
maven { url "http://repository.primefaces.org" }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
// compile ("org.thymeleaf:thymeleaf-spring4")
compile group: 'org.primefaces', name: 'primefaces', version: '4.0'
compile group: 'com.sun.faces', name: 'jsf-api', version: '2.2.2'
compile group: 'com.sun.faces', name: 'jsf-impl', version: '2.2.2'
compile group: 'javax.el', name: 'el-api', version: '1.0'
}
task wrapper(type: Wrapper) {
gradleVersion=1.10
}
我的主要课程:
package com.hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@EnableAutoConfiguration
@ComponentScan
@Configuration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
以及我的WebMvcConfigurerAdapter实现:
package com.hello;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("view");
registry.addViewController("/view").setViewName("view");
}
}
连同我的视图.html文件(我也尝试了 view.xhtml(,这些是我为这个项目创建的所有文件,因为我正在尝试进行最小的设置。
如果有人能看到我做错了什么(或根本没有做(,我们将不胜感激。在这种情况下,我是否需要额外的文件/bean进行 JSF 配置?如果是这样,应该在哪里以及如何放置这些文件?
这个问题也已经发布在官方春季论坛上:http://forum.spring.io/forum/spring-projects/boot/746552-spring-boot-and-jsf-primefaces-richfaces
编辑:在包含 M. Deinum 的 JSF 配置 bean 并删除 WebMvcConfigurerAdapter 定义(与 Spring MVC 相关(之后,Spring Boot 似乎将请求映射到 FacesServlet 实例,现在我收到以下错误消息: HTTP Status 500 - Servlet.init() for servlet FacesServlet threw exception
java.lang.IllegalStateException: Could not find backup for factory javax.faces.context.FacesContextFactory
新 JsfConfig bean 的代码:
package com.hello;
import com.sun.faces.config.ConfigureListener;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import javax.faces.webapp.FacesServlet;
@Configuration
public class JsfConfig {
@Bean
public FacesServlet facesServlet() {
return new FacesServlet();
}
@Bean
public ServletRegistrationBean facesServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(facesServlet(), "*.xhtml");
registration.setName("FacesServlet");
return registration;
}
@Bean
public ServletListenerRegistrationBean<ConfigureListener> jsfConfigureListener() {
return new ServletListenerRegistrationBean<ConfigureListener>(new ConfigureListener());
}
@Bean
public ViewResolver getViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/templates/");
resolver.setSuffix(".xhtml");
return resolver;
}
}
你使用的JSF不能与Spring MVC一起使用,两者都是不同的技术。您必须正确设置 JSF。为此,您需要添加一个FacesServlet
,并且面ConfigureListener
。这是正确设置 JSF 所必需的,通常会自动检测到ConfigureListener
,但嵌入式版本似乎并没有真正做到这一点。
@Configuration
public class JsfConfig {
@Bean
public FacesServlet facesServlet() {
return new FacesServlet();
}
@Bean
public ServletRegistrationBean facesServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(facesServlet(), "*.xhtml");
registration.setName("FacesServlet")
return registration;
}
@Bean
public ListenerRegistationBean jsfConfigureListener() {
return new ListenerRegistrationBean(new ConfigureListener());
}
}
像这样的东西。我可能会建议你禁用DispatcherServlet等的自动配置,因为你使用的是JSF而不是Spring MVC。
@EnableAutoConfiguration(exclude={WebMvcAutoConfiguration.class,DispatcherServletAutoConfiguration }
在用Spring Boot设置FacesServlet后,louvelg看到了这个问题,我通过添加几个配置文件和来自spring-web的ServletRegistrationBean,让它与JSF/primefaces一起工作。web.xml和faces-config.xml文件有点伤害了Spring Boot在没有大量xml文件的情况下进行最小设置的想法,但这是我能够用JSF找到的最小设置,如果有人知道更好/更干净的方法来做到这一点,请告诉我。
以下是我的 gradle.build 文件中的依赖项:
compile group: "org.springframework.boot", name: "spring-boot-starter"
compile group: "org.springframework", name: "spring-web", version: "4.0.2.RELEASE"
compile group: "org.apache.tomcat.embed", name: "tomcat-embed-core", version: tomcatVersion
compile group: "org.apache.tomcat.embed", name: "tomcat-embed-logging-juli", version: tomcatVersion
compile group: "org.apache.tomcat.embed", name: "tomcat-embed-jasper", version: tomcatVersion
compile group: "org.primefaces", name: "primefaces", version: "4.0"
compile group: "com.sun.faces", name: "jsf-api", version: "2.1.21"
compile group: "com.sun.faces", name: "jsf-impl", version: "2.1.21"
其中tomcatVersion
是"7.0.34"。这里的主要变化是:
- 删除
spring-boot-starter-web
,其中还包括 Spring MVC,正如 M. Deinum 所指出的那样。 - 包括
spring-boot-starter
(更容易启动(和spring-web
- 显式包含 tomcat 嵌入依赖项,因为
spring-boot-starter-web
不再存在。
以下是我的主要课程的新内容:
package com.hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import javax.faces.webapp.FacesServlet;
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public ServletRegistrationBean servletRegistrationBean() {
FacesServlet servlet = new FacesServlet();
return new ServletRegistrationBean(servlet, "*.xhtml");
}
}
@EnableAutoConfiguration
,如果您希望Spring Boot自动设置Tomcat,并且ServletRegistrationBean将xhtml请求映射到FacesServlet。
我的 webapp 目录中的 WEB-INF/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>
</faces-config>
web.xml 文件也在 webapp/WEB-INF 中:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
</web-app>
servlet 定义似乎是多余的,但由于某种原因,如果我从 web.xml 文件或 servlet 注册 bean 中删除它们,xhtml 页面的呈现将无法按预期工作或根本不工作。编辑:事实证明,web.xml文件中不需要servlet映射,这只是一个IDE问题,在我的情况下,Intellij Idea不知道servlet注册bean中定义的servlet映射,所以它抱怨缺少预期的servlet映射,但应用程序运行没有问题。
感谢所有做出贡献的人。
我也遇到了同样的问题。我发现了一个稍微不同的解决方案,没有[@EnableAutoConfiguration]:
[绒球.xml]
<?xml version="1.0" encoding="UTF-8"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
<!-- http://stackoverflow.com/questions/25479986/spring-boot-with-jsf-could-not-find-backup-for-factory-javax-faces-context-face/25509937#25509937 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- JSF -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.faces</artifactId>
<version>2.2.11</version>
</dependency>
<!-- JSR-330 -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- JSP API -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!-- Spring web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
...
</dependencies>
<build>
<outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory>
</build>
</project>
和 Java 配置类:
...
@Configuration
public class JsfConfig extends SpringBootServletInitializer {
@Bean
public ServletRegistrationBean servletRegistrationBean() {
FacesServlet servlet = new FacesServlet();
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(servlet, "*.xhtml");
return servletRegistrationBean;
}
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.setPort(8080);
factory.setSessionTimeout(10, TimeUnit.MINUTES);
return factory;
}
}
[WEB-INF/web.xml]
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
</servlet>
</web-app>
[人脸配置.xml]
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
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 http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
</faces-config>
我关注了这篇文章,我遇到了同样的错误。这有点黑客,但目前这是我能得到的最好的。
将其添加到 web.xml 文件中的 src/main/webapp/WEB-INF 文件夹中
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
</servlet>
</web-app>
这应该可以解决找不到备份出厂问题。
收到此错误后,我遇到了进一步的问题
It appears the JSP version of the container is older than 2.1 and unable to locate the EL RI expression factory, com.sun.el.ExpressionFactoryImpl. If not using JSP or the EL RI, make sure the context initialization parameter, com.sun.faces.expressionFactory, is properly set.
所以我添加了这个依赖项
compile "org.glassfish.web:el-impl:2.2"
这就是事情开始为我工作的时候。
我仍在努力使它不需要网络.xml。因为这确实是Spring Boot应该如何工作的黑客。我在这方面取得的任何进一步进展,我会尽量记住回发到这个答案,但如果我不这样做,我将添加到我创建的此示例应用程序中。
https://github.com/Zergleb/Spring-Boot-JSF-Example
代码中的这一行正在注册一个控制器,当收到/view
请求时,该控制器将重定向到逻辑名称为view
的视图。
registry.addViewController("/view").setViewName("view");
然后,视图解析器将采用逻辑名称,应用前缀和后缀并查找视图定义,例如 view.xhtml
。
这里的问题似乎是添加了默认视图解析程序,它没有添加后缀或前缀。
有关定义默认视图解析程序的代码,请参阅WebMvcAutoConfiguration.defaultViewResolver()
。
默认视图解析器尝试加载名为 /view
的视图,这会再次触发同一控制器,从而创建一个循环。
尝试注释该行addViewController
或定义自己的视图解析器,例如:
@Bean
public ViewResolver getViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".xhtml");
return resolver;
}