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>
結果符合預期,我們造出了一個新的 模板工具~~!