截止目前,前面第6节遗留的问题还有一个没处理:
我们测试时用的URL全部是以http://.../faces/.../xxx.xhtml路径来访问的,如果以http//.../xxx.faces来访问就可以看到dojo.js没生效,这是相对路径不匹配造成dojo.js无法加载,这个问题也需要解决。
先看效果
这是用/faces访问的效果
下面这是用.faces访问的效果
可以看到用.faces访问时dojo.js没运行,这是因为两个原因:1、dojo.js没加载。2、<script>require(["dojo/parser", ...这段脚本依赖的js文件没有加载。
第1个原因通过查看两个页面的源代码就能发现,用/faces访问时,产生的代码是
<link type="text/css" rel="stylesheet" href="/dojo4j/faces/javax.faces.resource/dojo/resources/dojo.css" />
而用.faces访问时,产生的代码是
<link type="text/css" rel="stylesheet" href="/dojo4j/test/textBoxTest.faces/javax.faces.resource/dojo/resources/dojo.css" />
明显的路径错误,解决这个问题只需修改Header.java,重写资源文件引用的代码,加入方法
private String getResourcePath(String resourceName) {
String uri;
FacesContext context = FacesContext.getCurrentInstance();
String facesServletMapping = Util.getFacesMapping(context);
// If it is extension mapped
if (Util.isPrefixMapped(facesServletMapping)) {
uri = facesServletMapping + ResourceHandler.RESOURCE_IDENTIFIER + '/' + resourceName;
} else {
uri = ResourceHandler.RESOURCE_IDENTIFIER + '/' + resourceName + facesServletMapping;
}
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
return request.getContextPath() + uri;
}
把这几句
writer.writeAttribute("href", request.getContextPath() + context.getExternalContext().getRequestServletPath()
+ "/javax.faces.resource/dojo/resources/dojo.css", null);
writer.writeAttribute("href", request.getContextPath() + context.getExternalContext().getRequestServletPath()
+ "/javax.faces.resource/dijit/themes/" + this.theme + "/" + this.theme + ".css", null);
writer.writeAttribute("src", request.getContextPath() + context.getExternalContext().getRequestServletPath()
+ "/javax.faces.resource/dojo/dojo.js", null);
改为
writer.writeAttribute("href", getResourcePath("dojo/resources/dojo.css"), null);
writer.writeAttribute("href", getResourcePath("dijit/themes/" + this.theme + "/" + this.theme + ".css"), null);
writer.writeAttribute("src", getResourcePath("dojo/dojo.js"), null);
这样改了以后,用/faces访问时生成
<link type="text/css" rel="stylesheet" href="/dojo4j/faces/javax.faces.resource/dojo/resources/dojo.css" />
用.faces访问时生成
<link type="text/css" rel="stylesheet" href="/dojo4j/javax.faces.resource/dojo/resources/dojo.css.faces" />
这样就可以正常生成所需的链接代码。
尽管这样,但dojo在运行<script>require(["dojo/parser", ...时,并不能加载所依赖的脚本文件,通过Google Chrome浏览器调试,可以看到
所依赖的parser.js、TextBox.js、Button.js无法下载,这是因为dojo在加载时这几个脚本文件时路径,用的是标准的.js结尾,但是这种情况JSF是不能正常处理的,必须要在路径后面加上.faces后缀,JSF才能正确读取资源文件
解决这个问题只有用Filter。
Filter.java
package org.dojo4j.webapp;
import java.io.IOException;
import javax.faces.application.ResourceHandler;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.faces.util.Util;
public class DojoFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
ServletException {
if (!(req instanceof HttpServletRequest) || !(resp instanceof HttpServletResponse)) {
throw new ServletException("DojoFilter just supports HTTP requests");
}
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String uri = request.getRequestURI();
if (uri.contains(ResourceHandler.RESOURCE_IDENTIFIER)) {
String facesServletMapping = getFacesMapping(request);
//只处理facesServletMapping为.xxx后缀的情况,不处理facesServletMapping为/xxx为前缀的情况
if (!Util.isPrefixMapped(facesServletMapping)) {
//只有从引用资源的页面URL中才能取得Faces Servlet的后缀
String referer = request.getHeader("Referer");
if (referer != null && referer.trim().length() > 0) {
if (referer.indexOf('?') > -1) {
referer = referer.substring(0, referer.indexOf('?'));
}
if (referer.indexOf('.') > -1) {
String subfix = referer.substring(referer.lastIndexOf('.'));
//给没有Faces Servlet的后缀的资源加上后缀,确保通过JSF框架来解析资源文件
if (!facesServletMapping.equals(subfix)) {
String url = request.getServletPath() + subfix + (request.getQueryString() == null ? "" : "?" + request.getQueryString());
// System.out.println(url);
request.getRequestDispatcher(url).forward(request, response);
return;
}
}
}
}
}
chain.doFilter(req, resp);
}
private String getFacesMapping(HttpServletRequest request) {
String servletPath = request.getServletPath();
String pathInfo = request.getPathInfo();
if (servletPath == null) {
return null;
}
if (servletPath.length() == 0) {
return "/*";
}
if (pathInfo != null) {
return servletPath;
} else if (servletPath.indexOf('.') < 0) {
return servletPath;
} else {
return servletPath.substring(servletPath.lastIndexOf('.'));
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
@Override
public void destroy() {
}
}
web.xml
<filter>
<filter-name>DojoFilter</filter-name>
<filter-class>org.dojo4j.webapp.DojoFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>DojoFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
到这里,开发简单的组件所需的技术就介绍得差不多了,我相信通过这些技术应该能够采用一些简单的js库来制作自己的JSF组件库了,如jquery ui、liger ui等。
后面我会写如何处理JSF ajax方式更新js组件,而不是html控件(下拉框联动),如何改造jsf.js实现同步请求(ajax表格取数、分页、排序),如何自定义dojo js控件来适应JSF组件开发的需要(树、表格、树表格),这些知识深入JSF和DOJO内部,比较复杂,但是对打造企业级的框架又必不可少,如果有兴趣请继续关注。