Suz*_*ioc 25 java jsp servlets spring-mvc
基本配置文件看起来不直观.
如果我创建简单的hello world示例,然后重命名home.jsp为home.html和编辑servlet-context.xml文件
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
Run Code Online (Sandbox Code Playgroud)
至
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".html" />
</beans:bean>
Run Code Online (Sandbox Code Playgroud)
我开始出错了
WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/myapp/WEB-INF/views/home.html] in DispatcherServlet with name 'appServlet'
Run Code Online (Sandbox Code Playgroud)
为什么?什么suffix属性意味着?
UPDATE
我的控制器如下.如您所见,它不包含文件扩展名
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "home";
}
}
Run Code Online (Sandbox Code Playgroud)
wal*_*ros 30
问题的背景
首先要理解的是:它不是渲染jsp文件的spring.它是JspServlet(org.apache.jasper.servlet.JspServlet).这个servlet附带Tomcat(jasper编译器)而不是spring.这个JspServlet知道如何编译jsp页面以及如何将它作为html文本返回给客户端.默认情况下,tomcat中的JspServlet只处理匹配两种模式的请求:*.jsp和*.jspx.
现在当spring用InternalResourceView(或JstlView)渲染视图时,确实发生了三件事:
"public ModelAndView doSomething() { return new ModelAndView("home") }")RequestDispatcher知道应该将每个*.jsp请求转发到JspServlet(因为这是默认的tomcat配置)当您只是将视图名称更改为home.html时,tomcat将不知道如何处理请求.这是因为没有servlet处理*.html请求.
解
怎么解决这个问题.有三个最明显的解决方案:
初始配置(仅处理jsp)
首先让我们假设我们配置没有xml文件的spring(仅基于@Configuration批注和spring的WebApplicationInitializer接口).
基本配置如下
public class MyWebApplicationContext extends AnnotationConfigWebApplicationContext {
private static final String CONFIG_FILES_LOCATION = "my.application.root.config";
public MyWebApplicationContext() {
super();
setConfigLocation(CONFIG_FILES_LOCATION);
}
}
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = new MyWebApplicationContext();
servletContext.addListener(new ContextLoaderListener(context));
addSpringDispatcherServlet(servletContext, context);
}
private void addSpringDispatcherServlet(ServletContext servletContext, WebApplicationContext context) {
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet",
new DispatcherServlet(context));
dispatcher.setLoadOnStartup(2);
dispatcher.addMapping("/");
dispatcher.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
}
package my.application.root.config
// (...)
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
@Qualifier("jstlViewResolver")
private ViewResolver jstlViewResolver;
@Bean
@DependsOn({ "jstlViewResolver" })
public ViewResolver viewResolver() {
return jstlViewResolver;
}
@Bean(name = "jstlViewResolver")
public ViewResolver jstlViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/internal/");
resolver.setViewClass(JstlView.class);
resolver.setSuffix(".jsp");
return resolver;
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,我使用UrlBasedViewResolver和后备视图类JstlView,但您可以在示例中使用InternalResourceViewResolver并不重要.
上面的示例使用一个视图解析器配置应用程序,该解析器处理以.jsp.结尾的jsp文件.注意:如开头所述,JstlView确实使用tomcat的RequestDispatcher将请求转发给JspSevlet以将jsp编译为html.
解决方案1的实现 - 将html暴露为资源文件:
我们修改WebConfig类以添加新的资源匹配.我们还需要修改jstlViewResolver,以便它既不带前缀也不带后缀:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
@Qualifier("jstlViewResolver")
private ViewResolver jstlViewResolver;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/someurl/resources/**").addResourceLocations("/resources/");
}
@Bean
@DependsOn({ "jstlViewResolver" })
public ViewResolver viewResolver() {
return jstlViewResolver;
}
@Bean(name = "jstlViewResolver")
public ViewResolver jstlViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix(""); // NOTE: no preffix here
resolver.setViewClass(JstlView.class);
resolver.setSuffix(""); // NOTE: no suffix here
return resolver;
}
// NOTE: you can use InternalResourceViewResolver it does not matter
// @Bean(name = "internalResolver")
// public ViewResolver internalViewResolver() {
// InternalResourceViewResolver resolver = new InternalResourceViewResolver();
// resolver.setPrefix("");
// resolver.setSuffix("");
// return resolver;
// }
}
Run Code Online (Sandbox Code Playgroud)
通过添加这个,我们说每个发送到http://my.server/someurl/resources/的请求都被映射到您的web目录下的资源目录.因此,如果您将home.html放在资源目录中并将浏览器指向http://my.server/someurl/resources/home.html,则会提供该文件.要通过控制器处理此问题,请返回资源的完整路径:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
// (...)
return new ModelAndView("/someurl/resources/home.html"); // NOTE here there is /someurl/resources
}
}
Run Code Online (Sandbox Code Playgroud)
如果你在同一目录下放置一些jsp文件(不仅是*.html文件),比如在同一资源目录中的home_dynamic.jsp,你可以用类似的方式访问它,但你需要使用服务器上的实际路径.的路径中不能与/ someurl启动/因为这不仅是映射以.html结尾的HTML资源).在这种情况下,jsp是动态资源,最后由JspServlet使用磁盘上的实际路径访问.所以访问jsp的正确方法是:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
// (...)
return new ModelAndView("/resources/home_dynamic.jsp"); // NOTE here there is /resources (there is no /someurl/ because "someurl" is only for static resources
}
Run Code Online (Sandbox Code Playgroud)
要在基于xml的配置中实现此目的,您需要使用:
<mvc:resources mapping="/someurl/resources/**" location="/resources/" />
Run Code Online (Sandbox Code Playgroud)
并修改您的jstl视图解析器:
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- Please NOTE that it does not matter if you use InternalResourceViewResolver or UrlBasedViewResolver as in annotations example -->
<beans:property name="prefix" value="" />
<beans:property name="suffix" value="" />
</beans:bean>
Run Code Online (Sandbox Code Playgroud)
解决方案2的实施:
在这个选项中,我们使用tomcat的JspServlet来处理静态文件.因此,您可以在html文件中使用jsp标签:)如果您这样做,它当然是您的选择.很可能你想使用普通的html,所以只是不要使用jsp标签,内容将像静态html一样提供.
首先,我们删除视图解析器的前缀和后缀,如上例所示:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
@Qualifier("jstlViewResolver")
private ViewResolver jstlViewResolver;
@Bean
@DependsOn({ "jstlViewResolver" })
public ViewResolver viewResolver() {
return jstlViewResolver;
}
@Bean(name = "jstlViewResolver")
public ViewResolver jstlViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver(); // NOTE: this time I'm using InternalResourceViewResolver and again it does not matter :)
resolver.setPrefix("");
resolver.setSuffix("");
return resolver;
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们添加JspServlet来处理*.html文件:
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = new MyWebApplicationContext();
servletContext.addListener(new ContextLoaderListener(context));
addStaticHtmlFilesHandlingServlet(servletContext);
addSpringDispatcherServlet(servletContext, context);
}
// (...)
private void addStaticHtmlFilesHandlingServlet(ServletContext servletContext) {
ServletRegistration.Dynamic servlet = servletContext.addServlet("HtmlsServlet", new JspServlet()); // org.apache.jasper.servlet.JspServlet
servlet.setLoadOnStartup(1);
servlet.addMapping("*.html");
}
}
Run Code Online (Sandbox Code Playgroud)
重要的是,要使此类可用,您需要在tomcat的安装中添加jasper.jar,仅用于编译时.如果你有maven应用程序,那么通过使用为jar提供的scope =来实现这一点非常容易.maven中的依赖关系如下所示:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>${tomcat.libs.version}</version>
<scope>provided</scope> <!--- NOTE: scope provided! -->
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<version>${tomcat.libs.version}</version>
<scope>provided</scope>
</dependency>
Run Code Online (Sandbox Code Playgroud)
如果你想以xml方式做到这一点.您需要注册jsp servlet来处理*.html请求,因此您需要在web.xml中添加以下条目
<servlet>
<servlet-name>htmlServlet</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>htmlServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
Run Code Online (Sandbox Code Playgroud)
现在在您的控制器中,您可以像以前的示例一样访问html和jsp文件.优点是解决方案1中不需要"/ someurl /"额外映射.您的控制器将如下所示:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
// (...)
return new ModelAndView("/resources/home.html");
}
Run Code Online (Sandbox Code Playgroud)
指向你的jsp,你做的完全一样:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
// (...)
return new ModelAndView("/resources/home_dynamic.jsp");
}
Run Code Online (Sandbox Code Playgroud)
解决方案3的实施:
第三种解决方案在某种程度上是解决方案1和解决方案2的组合.因此,在这里,我们希望将所有请求传递给*.html到其他一些servlet.你可以编写自己的或者寻找已经存在的servlet的一些好的候选者.
如上所述,我们首先清理视图解析器的前缀和后缀:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
@Qualifier("jstlViewResolver")
private ViewResolver jstlViewResolver;
@Bean
@DependsOn({ "jstlViewResolver" })
public ViewResolver viewResolver() {
return jstlViewResolver;
}
@Bean(name = "jstlViewResolver")
public ViewResolver jstlViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver(); // NOTE: this time I'm using InternalResourceViewResolver and again it does not matter :)
resolver.setPrefix("");
resolver.setSuffix("");
return resolver;
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们编写自己的servlet(或重用一些现有的)来代替使用tomcat的JspServlet:
public class StaticFilesServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
String resourcePath = request.getRequestURI();
if (resourcePath != null) {
FileReader reader = null;
try {
URL fileResourceUrl = request.getServletContext().getResource(resourcePath);
String filePath = fileResourceUrl.getPath();
if (!new File(filePath).exists()) {
throw new IllegalArgumentException("Resource can not be found: " + filePath);
}
reader = new FileReader(filePath);
int c = 0;
while (c != -1) {
c = reader.read();
if (c != -1) {
response.getWriter().write(c);
}
}
} finally {
if (reader != null) {
reader.close();
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我们现在指示spring将所有对*.html的请求传递给我们的servlet
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = new MyWebApplicationContext();
servletContext.addListener(new ContextLoaderListener(context));
addStaticHtmlFilesHandlingServlet(servletContext);
addSpringDispatcherServlet(servletContext, context);
}
// (...)
private void addStaticHtmlFilesHandlingServlet(ServletContext servletContext) {
ServletRegistration.Dynamic servlet = servletContext.addServlet("HtmlsServlet", new StaticFilesServlet());
servlet.setLoadOnStartup(1);
servlet.addMapping("*.html");
}
}
Run Code Online (Sandbox Code Playgroud)
优点(或缺点,取决于你想要的)是jsp标签显然不会被处理.控制器看起来像往常一样:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
// (...)
return new ModelAndView("/resources/home.html");
}
Run Code Online (Sandbox Code Playgroud)
对于jsp:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
// (...)
return new ModelAndView("/resources/home_dynamic.jsp");
}
Run Code Online (Sandbox Code Playgroud)
小智 5
Resolver类用于解析视图类的资源,依次查看类,从资源生成视图.例如,使用典型的InternalResourceViewResolver,如下所示:
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
Run Code Online (Sandbox Code Playgroud)
视图名称"home"将映射为"/WEB-INT/views/home.jsp",然后使用视图类InternalResourceView(用于JSP)转换为JSP视图.如果用".html"替换后缀值,Spring可以获取特定资源"/WEB-INT/views/home.html",但不知道如何生成它.
我认为InternalResourceViewResolver支持servlet 和jsp 文件。根据 Spring 的 API javadocs,后缀是“在构建 URL 时附加到视图名称”的后缀。它不是文件的扩展名,尽管它非常具有误导性。我检查了UrlBasedViewResolver setSuffix()类。
我想,如果他们将其命名为 viewSuffix,可能会更有意义。
| 归档时间: |
|
| 查看次数: |
72805 次 |
| 最近记录: |