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

来源:互联网 发布:淘宝神舟旗舰店 编辑:程序博客网 时间:2024/06/04 17:47

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, worldtest.CatImpl@578fd6I'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 startsource.append("package net.csdn.blog.deltatang.jsp4me;").append("\n");//importssource.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 endsource.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>

结果符合预期,我们造出了一个新的 模板工具~~!

0 0
原创粉丝点击