您的位置:首页 > 编程语言 > Java开发

拿起笔来做刀枪 · 之三 再造一个jsp(java sign pages)

2014-07-28 14:49 302 查看
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>


结果符合预期,我们造出了一个新的 模板工具~~!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: