JSP的全称叫做: java server pages。
我们这里也打造一个jsp, 名称叫做 java sign pages。
区别在于 没有 server 这个字样。 我会尝试打造一个 类似于 freemarker的模板标记语言, 从而,让 我们所写的 viewer 和 server 分离。
虽然我们现在还毫无头绪,但是,可以回想一下jsp 和 servlet的关系:
事实上,jsp就是servlet未编译版本,任何jsp最终都被tomcat容器编译到work目录并以java类的形态执行。
你可以在 你的tomcat运行目录中看到端倪:
\work\Catalina\localhost\_\org\apache\jsp
你的webroot下的jsp页面的最终形态是:
index_jsp.class
当然tomcat还提供一个编译前版本:
index_jsp.java
让我们看看他的内容:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static java.util.Vector _jspx_dependants;
public java.util.List getDependants() {
return _jspx_dependants;
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
try {
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html; charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\n");
out.write("<html>\n");
out.write("\t<head>\n");
out.write(" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
out.write(" <title>multi-media searcher demo</title>\n");
out.write("\t\t<style type=\"text/css\">\n");
out.write("\t\t\tbody { background-color: #fff; padding: 0 20px; color:#000; font: 13px/18px Arial, sans-serif; }\n");
out.write("\t\t\ta { color: #360; }\n");
out.write("\t\t\th3 { padding-top: 20px; }\n");
out.write("\t\t\tol { margin:5px 0 15px 16px; padding:0; list-style-type:square; }\n");
out.write("\t\t</style> \n");
out.write("\t</head>\n");
out.write("\t\n");
out.write("\t<body>\n");
out.write("\t\t\n");
out.write("\t\t<h1>This is my first jsp page!</h1>\n");
out.write("\t\t\n");
out.write("\t</body>\n");
out.write("\t\n");
out.write("</html>");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
如果我们也这么照葫芦画瓢的来一份不就OK了?
你可以参考一下我的博文:
从String中动态(内存中)编译和加载java类
我们采取这篇文章中提到的最简单的方式,生成一个java类,调用
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
工具进行编译,然后用
URLClassLoader
进行加载,并使用。
先看一个简单的例子,试验一下我们的想法是否可行:
创建一个接口:
package net.csdn.blog.deltatang;
public interface Cat {
public void say();
}
动态定义实现类,并初始化,调用接口:
package net.csdn.blog.deltatang;
import java.io.File;
import java.io.FileWriter;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class DynaCompTest {
public static void main(String[] args) throws Exception {
StringBuffer srcbuf = new StringBuffer();
srcbuf.append("package test;");
srcbuf.append("import net.csdn.blog.deltatang.Cat;");
srcbuf.append("");
srcbuf.append("public class CatImpl implements Cat {");
srcbuf.append("");
srcbuf.append(" static {");
srcbuf.append(" System.out.print(\"hello, \");");
srcbuf.append(" }");
srcbuf.append("");
srcbuf.append(" public CatImpl() {");
srcbuf.append(" System.out.println(\"world\");");
srcbuf.append(" }");
srcbuf.append("");
srcbuf.append(" public void say() {");
srcbuf.append(" System.out.println(\"I'm a cat!\");");
srcbuf.append(" }");
srcbuf.append("");
srcbuf.append("}");
String source = srcbuf.toString();
// Save source in .java file.
File root = new File("/java");
File sourceFile = new File(root, "test/CatImpl.java");
sourceFile.getParentFile().mkdirs();
new FileWriter(sourceFile).append(source).close();
// Compile source file.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());
// Load and instantiate compiled class.
URLClassLoader classLoader = URLClassLoader
.newInstance(new URL[] { root.toURI().toURL() });
Class<?> cls = Class.forName("test.CatImpl", true, classLoader); // Should
// print
// "hello".
Object instance = cls.newInstance(); // Should print "world".
System.out.println(instance); // Should print "test.Test@hashcode".
Cat cat = (Cat)instance;
cat.say();
}
}
输出为:
source code : -------------------------------
package test;
import net.csdn.blog.deltatang.Cat;
public class CatImpl implements Cat {
static {
System.out.print("hello, ");
}
public CatImpl() {
System.out.println("world");
}
public void say() {
System.out.println("I'm a cat!");
}
}
---------------------------------------------
hello, world
test.CatImpl@578fd6
I'm a cat!
试验成功!
让我们整理下代码:
package net.csdn.blog.deltatang.jsp4me;
import java.io.File;
import java.io.FileWriter;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class StringToObjectBuilder {
public String workdir = "/_web_workdir_tmp";
public StringToObjectBuilder(String workdir) {
super();
this.workdir = workdir;
}
public StringToObjectBuilder() {
super();
}
public Object build(String source, String classPath) {
String javaFilePath = classPath.replaceAll("\\.", "/") + ".java";
try {
File root = new File(workdir);
File sourceFile = new File(root, javaFilePath);
sourceFile.getParentFile().mkdirs();
new FileWriter(sourceFile).append(source).close();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());
URL[] urls = new URL[]{root.toURI().toURL()};
URLClassLoader classLoader = URLClassLoader.newInstance(urls);
Class<?> cls = Class.forName(classPath, true, classLoader);
Object instance = cls.newInstance();
return instance;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
StringBuffer srcbuf = new StringBuffer();
srcbuf.append("package net.csdn.blog.deltatang.jsp4me;").append("\n");
srcbuf.append("import net.csdn.blog.deltatang.jsp4me.Cat;").append("\n");
srcbuf.append("").append("\n");
srcbuf.append("public class CatImpl implements Cat {").append("\n");
srcbuf.append(" public void say() {").append("\n");
srcbuf.append(" System.out.println(\"I'm a cat!\");").append("\n");
srcbuf.append(" }").append("\n");
srcbuf.append("}").append("\n");
String source = srcbuf.toString();
System.out.println("source code : -------------------------------");
System.out.println(source);
System.out.println("---------------------------------------------");
String classPath = "net.csdn.blog.deltatang.jsp4me.CatImpl";
StringToObjectBuilder builder = new StringToObjectBuilder();
Cat cat = (Cat)builder.build(source, classPath);
cat.say();
}
}
运行:
source code : -------------------------------
package net.csdn.blog.deltatang.jsp4me;
import net.csdn.blog.deltatang.jsp4me.Cat;
public class CatImpl implements Cat {
public void say() {
System.out.println("I'm a cat!");
}
}
---------------------------------------------
I'm a cat!
同时,可以看到,
我们在效果上实现了 tomcat 对jsp说干的事情:)
现在,让我们来进行第二步工作,定义我们的标记语言格式。
同样,我们参考jsp已经有的标记做一个子集,如下所示:
<%@ page import="java.util.*"%>
<%
List datalist = new ArrayList();
datalist.add("row1");
datalist.add("row2");
datalist.add("row3");
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP - NOT java SERVER pages but java SIGN pages</title>
</head>
<body>
<h1>This is my JSP page. </h1>
<h1>My JSP - NOT java SERVER pages but java SIGN pages</h1>
<h2>out.println()</h2>
<%
for(int i = 0; i < datalist.size(); i++) {
out.println(datalist.get(i) + "<br/>");
}
%>
<h2>special marker</h2>
<%
for(int i = 0; i < datalist.size(); i++) {
%>
<%= datalist.get(i) %><br/>
<%
}
%>
</body>
</html>
我们将支持:
<%@ page import="java.util.*"%>
<%
代码片段
%>
<%= datalist.get(i) %>
out.print("testline");
这样的约束条件足够我们说明问题,并可以很容易的进行扩展:)
让我们先创建 一个接口:
package net.csdn.blog.deltatang.jsp4me;
import java.util.Map;
public interface JspHandler {
public String buildContent(Map<String, Object> context);
}
然后编译jsp文件,动态的实现这个接口,
注意,假设我们在方法内定义了一个返回结果result,那么我们要将
<%@ page import="java.util.*"%>
替换成java代码的: result += "import java.util.*;";
<%= datalist.get(i) %>
替换成: result += datalist.get(i);
out.print("testline");
替换成:result += "testline";
由此,我们获得了一下代码实现:
package net.csdn.blog.deltatang.jsp4me;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class JspToJavaLangBuilder {
public String convert(String jspPath, String className) {
StringBuilder source = new StringBuilder();
List<String> imports = new ArrayList<String>();
StringBuilder codes = new StringBuilder();
String content = getJspContent(jspPath);
int clen = content.length();
int fromIndex = 0;
int toIndex = -1;
while(fromIndex < clen) {
fromIndex = content.indexOf("<%", fromIndex);
String code = "";
if(toIndex > 0) {
toIndex += 2;
if(fromIndex < 0) {
code = content.substring(toIndex);
} else {
code = content.substring(toIndex, fromIndex);
}
} else {
if(fromIndex < 0) {
code = content;
} else {
code = content.substring(0, fromIndex);
}
}
code = code.replaceAll("\\\"", "\\\\\"");
String[] lines = code.split("\n");
for(String line : lines) {
line = line.trim();
if(line.equals("")) {
continue;
}
codes.append("source.append(\"").append(line).append("\");").append("\n");
}
if(fromIndex < 0) break;
fromIndex += 2;
toIndex = content.indexOf("%>", fromIndex);
String snippet = content.substring(fromIndex, toIndex);
fromIndex = toIndex + 2;
if(snippet.startsWith("@")) {
imports.add(snippet);
continue;
}
// snippet = snippet.replaceAll("\\\"", "\\\\\"");
if(snippet.startsWith("=")) {
code = snippet.substring(1);
codes.append("source.append(").append(code).append(");").append("\n");
continue;
}
snippet = snippet.replaceAll("out.println", "source.append");
codes.append(snippet);
}
//class start
source.append("package net.csdn.blog.deltatang.jsp4me;").append("\n");
//imports
source.append("import net.csdn.blog.deltatang.jsp4me.JspHandler;").append("\n");
for(String s : imports) {
int start = s.indexOf('"') + 1;
int end = s.indexOf('"', start);
String ipt = s.substring(start, end);
source.append("import ").append(ipt).append(";");
}
source.append("public class ").append(className).append(" implements JspHandler {").append("\n");
source.append(" public String buildContent(Map context) {").append("\n");
source.append(" StringBuilder source = new StringBuilder();").append("\n");
source.append(codes);
source.append(" return source.toString();").append("\n");
source.append(" }").append("\n");
//class end
source.append("}").append("\n");
return source.toString();
}
private String getJspContent(String jspPath) {
StringBuilder sb = new StringBuilder();
InputStream is = null;
try {
is = this.getClass().getResourceAsStream(jspPath);
byte buf[] = new byte[1024];
int len = 0;
while((len = is.read(buf)) > 0) {
sb.append(new String(buf, 0, len));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
public static void main(String[] args) {
String jspPath = "./test.jsp";
String java = new JspToJavaLangBuilder().convert(jspPath, "Test");
System.out.println(java);
}
}
最后,让我们整合以上的代码,完成我们的工作:
package net.csdn.blog.deltatang.jsp4me;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JspCompiler {
public static JspHandler compile(String jspPath, String classPath) {
String className = classPath.substring(classPath.lastIndexOf('.') + 1);
String source = new JspToJavaLangBuilder().convert(jspPath, className);
System.out.println(source);
StringToObjectBuilder builder = new StringToObjectBuilder();
JspHandler handler = (JspHandler)builder.build(source, classPath);
return handler;
}
public static void main(String[] args) {
String jspPath = "./test.jsp";
String classPath = "net.csdn.blog.deltatang.jsp4me.TestJsp";
Map<String, Object> context = new HashMap<String, Object>();
List datalist = new ArrayList();
datalist.add("data----------1");
datalist.add("data----------2");
datalist.add("data----------3");
context.put("datalist", datalist);
JspHandler handler = JspCompiler.compile(jspPath, classPath);
System.out.println(handler.buildContent(context));
}
}
run的结果为:
jsp模板内容为:
<%@ page import="java.util.*"%>
<%
List datalist = (List)context.get("datalist");
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP - NOT java SERVER pages but java SIGN pages</title>
</head>
<body>
<h1>This is my JSP page. </h1>
<h1>My JSP - NOT java SERVER pages but java SIGN pages</h1>
<h2>out.println()</h2>
<%
for(int i = 0; i < datalist.size(); i++) {
out.println(datalist.get(i) + "<br/>");
}
%>
<h2>special marker</h2>
<%
for(int i = 0; i < datalist.size(); i++) {
%>
<%= datalist.get(i) %><br/>
<%
}
%>
</body>
</html>
请注意模板中的context对象,显而易见,我用它取代了jsp中的上下文:page、session、request、application
同时,我们保留了servlet中的 Printer类型实例: out
动态生成的java文件:
package net.csdn.blog.deltatang.jsp4me;
import net.csdn.blog.deltatang.jsp4me.JspHandler;
import java.util.*;public class TestJsp implements JspHandler {
public String buildContent(Map context) {
StringBuilder source = new StringBuilder();
List datalist = (List)context.get("datalist");
datalist.add("row1");
datalist.add("row2");
datalist.add("row3");
source.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
source.append("<html>");
source.append("<head>");
source.append("<title>My JSP - NOT java SERVER pages but java SIGN pages</title>");
source.append("</head>");
source.append("<body>");
source.append("<h1>This is my JSP page. </h1>");
source.append("<h1>My JSP - NOT java SERVER pages but java SIGN pages</h1>");
source.append("<h2>out.println()</h2>");
for(int i = 0; i < datalist.size(); i++) {
source.append(datalist.get(i) + "<br/>");
}
source.append("<h2>special marker</h2>");
for(int i = 0; i < datalist.size(); i++) {
source.append( datalist.get(i) );
source.append("<br/>");
}
source.append("</body>");
source.append("</html>");
return source.toString();
}
}
main方法输出结果:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP - NOT java SERVER pages but java SIGN pages</title>
</head>
<body>
<h1>This is my JSP page. </h1>
<h1>My JSP - NOT java SERVER pages but java SIGN pages</h1>
<h2>out.println()</h2>
data----------1<br/>
data----------2<br/>
data----------3<br/>
<h2>special marker</h2>
data----------1<br/>
data----------2<br/>
data----------3<br/>
</body>
</html>
结果符合预期,我们造出了一个新的 模板工具~~!