简单的开场白:把我开发 Java Web 框架过程中想说的话,给说出来。先介绍下作者的背景,本人最先比较熟悉 JS,先是接触过 ASP(VBS)、ASP(用 JScript 编写,花时间不少)、ASP.net(不算太深入)、Java。最后确定了 Java 作为目标开发平台。小弟 2004 年学 MS Classic ASP 最老的那种 ASP,非 Net。到现在开始,正式使用 JSP。虽然小弟之前很早就接触 Java 了,大概知道一点点,却又不尽然。因此还是要边学边用。又因自学的,或者工作过程中自己摸索的,所以应该有非常多“小白”的地方,因此唐突不对的地方,请大家一一指正。是以为笔记吧。
我的 Web 开发知识始发于微软 ASP,自然比较熟悉,所以遇到其他 Web 开发平台,难免不作为比较。ASP 更多的考虑是页面怎么样输出 HTML,一个 *.asp 页面固定为一个 URL,也对应着一个请求。同时期的 JSP/PHP 也是相仿的道理,当然 JSP 可以编写自己的 Servlet 显得更强大一些。于是后来的 .net 提出的 WebForm、Code Behind 也是力求页面与逻辑分离,这种概念当时大行其道,无不充分实践 Web 三层架构的旗号。其中值得一提的是 Classic ASP 也就是我刚才说我入门的 ASP,属于比较古老的语言,但架构上可以支持 COM/COM+ 组件开发——为此我还特意地买了一本很厚的 O'Reilly 出版的书《ASP 组件开发》,专门介绍不同的语言开发 ASP dll 组件。实话说,这种架构很不错,对开发者也挺友好(囊括 C++/Java/Python/Perl/WSH/JavaScript 等语言,只要有解析器的就可以),但随着 .Net 大势,COM/COM+ 技术也逐渐没落……
表示层、业务层、数据层,不论不同语言和平台有着它们不同的名词,但大体上 Web 架构的划分就是从这三个大块开始的,意思差不多。
ASP/PHP/JSP 这类的共同点都是有个 Pages,都内置了 Web 开发所必须的几大对象,例如 ASP 的 Request、Response、Session、Server、Application,JSP 对应的都有!从 ASP 到 JSP,后者只是把 Visual Basic Script 换成了 Java,然后这几大对象的 API 有所不同而已,都有 Request、Response 等等,但页面生成方式还是也页面为优先切入点,不约而同的使用 <%=%> 输出动态内容。当然了,HTML还是 HTML,CSS 还是 CSS,这些知识无论 ASP/PHP/JSP 都是一致的,应该说是毫无“违和感”的。总之,早期类型的开发模型做起页面来都是很快手,适合于前端优先的学习路径,——以至对影响我很深,使得我一直不肯放弃 <%=XXX%> 大法,甚至还一度排斥 Servlet:认为“直接输出 HTML 很爽,容易看到效果”、“Java 又不用编译、写 Servlet 麻烦”,那时候,我写 Java 不是写一个个 Class,而是一个个 JSP 然后 include……这种“避重就轻”的状况坚持了很久,直到今天,我所设计的程序结构,虽然已大幅度改善,更多地利用 Java 语言传统优势和 Java Web 框架的固有优点,但必须承认,中间“说服自己”的过程(弯路)走了很久……
对于复杂的逻辑还是需要更高层次的划分,那便是 MVC 了。MVC 足够经典,所以很有必要回顾以一下 Servlet。早期 Servlet 模型很朴素,1、Web.xml 指定 url;2、继承 HttpServlet,重写 doGet/doPost 方法;3、数据库 SQL 操作,例如读取一条记录(此为业务逻辑范畴);4、得到数据,塞入 request.setAttribute("name", rs.getString("name")) 然后分发到 JSP 到:req.getRequestDispatcher("hello.jsp").forward(req, resp);;5、JSP 模板中使用 EL 表达式或者标签渲染最终 HTML 出来。简单说,乃是 Servlet+Java Bean + JSP。随后 Struts/FreeMaker/Spring MVC 均为 MVC 模式的实作。
随着 Servlet 的发展,到 3.0 的 Servlet 已经支持注解等诸多方便功能了,无须其他第三方库都可以完成一般的工作了。
推荐教程:《Servlet/JSP 學習筆記》http://openhome.cc/Gossip/ServletJSP/
我觉得,框架开发乃“摸着石头过河”,边写边修正目标,因此谈不上“超越 XX 框架”,或者更遑论“推广,介绍别人使用”!质言之,是为了记录下一些自问有价值的内容,是为笔记。根据自己的一点经验,初期的目标应该如下。
很多事物应该是做出来了,才算有某个特性吧。这些特性如果不太偏离当初的目标,就可以具体化了。现在这个目标是比较模糊的,——等框架差不多的时候,再罗列一次各项特点。
基本选型如下。
前端:HTML5/CSS/JavaScript/LESS/Sea.js/Step.js/Bigfoot.js
后端:Java/JSP/Tag Files/Rhino
数据库:SQLite/MySql
项目构建:Eclipse/Node.js/Grunt
测试框架:JUnit/Mockito
上面我谈到了“混合型编程”,那么,什么是混合型编程呢?下面讲讲我的看法,当然其中也借鉴了别人的观点,包括图片资源也是。
现在有种混合型编程的观点,正好符合我使用 Rhino 的想法。具体说,就是大家针对 Java 静态类型不太灵活,编译比较麻烦,部署需要重启服务器,而且 Java 语法相对冗长,不够简洁等观点,提出在 JVM 上多语言开发的这么一种编程方式。他们认为,对于金字塔顶端的语言,应该是侧重的是灵活性和快速部署能力,而一方面,比较务实的做法是利用 Java 丰富的 API 和类库完成底层繁重的工作。
JVM 上有不少备用语言可供选择,如 JRuby、Groovy、Scala 和 Rhino 等,它们使用范围非常广泛,都是有相对生命力和影响力的语言,之所以选择 Rhino,基于下面的原因。
实际上,我使用 ASP 开发之时,更多的是使用 JavaScript 的变种 JScript,生成 JSON 数据非常简单。JS 是动态语言,我认为在字符串处理(正则表达式)、反射、天然的 JSON 支持和函数式编程这几个方面有优势,就是十分便捷,Java 几十行的代码,JS 十几行或者更短就搞掂了。于是,我在这个 Java Web 框架中大量利用自带 JS 引擎 Rhino 来完成编码的工作。实际上,JavaScript 不负其名,果然是 Java 之脚本,既是 Java 的子集,也不与 Java 正面冲突,正好弥补 Java 静态语言的不足,定位十分鲜明准确。引申地想想,这不就像是 C 之于 C++,盖因很多人把 C 作为一种“脚本”了。
然而在实际尝试中,却走了一段弯路……
我当前使用 JS 的地方:
请求 Request/ 响应 Response 可以说是 Web 模型的两个基本概念,几乎每个 Web 框架都有这两个对象。
总结几种请求方式:
放弃笨重的 POJO 改为数据库表设计+Map 输出
SQL 语句编译器
异常是 Java 语言的大一特点,Java 拥有比较完善的异常机制。使用 try...catch 语句可以捕获任意类型的异常。如果catch()参数中 e 为所有异常的父类 Exception 或者接口 throwable,那么就可以在 catch 子语句中获取特定的异常。这样的做法缺点是不具体区分特定的异常,相当于强类型转换,会损失特定的方法或成员。
所以,我们建议,在一期生命周期中,由最后的 try……catch(Eexception e)捕获所有的异常,并输出。
Java 不像 JS,可以直接执行 throw 语句抛出异常,要么在 try...catch 中包裹着,要么在方法声明中 throws 对应的异常,否则 Java 会认为你没有异常(程序员偷懒了)。JS 中,同样有 try..catch 语句,不同的是,throw 可以在任意位置,只不过由 try...catch 堆栈中的最接近的那个接受这个异常。
相对来说,js 比较自由,Java 的强制性会大一些,而且提供了两种处理异常的基本机制:要么在 try...catch 中包裹着,要么在方法声明中 throws 对应的异常。我是这样理解的,如果编写一个方法,行数很多,try……catch 很多,那么一种简便的方法就是 throws 所有的异常,这样代码会显得清爽很多。throws 的作用,就是当前我已经预计了有这么一些异常,但先不处理,留给调用这个方法者来处理异常,也就是用 try...catch处理(这一步是必须的)。
Java 可以自定义异常类型,这个比 js 强大多。尽管 js 也不是不可以自定义异常类型,但是 catch 语句不能自动辨别类型。所以某些大型的 Java 系统,便有自己的异常框架和许许多多的自定义异常类型。但是在我这个框架中,则没那么多的异常类型。主要围绕以下几个点来设计。
我有一点疑惑:使用 e.printStack() 方法,不知道为什么,只能在当前上下文的 catch 语句中使用,才有出错堆栈打印出来。把 e.printStack() 放置其他地方貌似不能打印。
关于异常我还补充了《恶补 Java 系列之异常》。
一般 java web 会把出错信息放到 request 对象中,如 request.setAttribute('err', 上传图片不正确')
Tomcat 是 HTTP Server,也是 Web 容器。Tomcat 由以下的组件所构成:
开发者从对 Java 的热爱延伸到对 JSP 的热爱,同时 JSP 也是 J2EE 体系中最重要,而且又是最基础的一个组成部分,如果要体验 J2EE 带了的开发效率和优势,JSP 会是非常有效的入门方式。学习、使用 JSP 的好处如下:
请看官参阅以下网友资源:
java框架太多,感觉太复杂。web应用直接用Jsp+Servlet+JavaBean不是更简单?而且更灵活 实际上依然有公司在用纯Jsp 的script的形式写项目。
我觉得应用框架最大的作用就是尽量让项目变得灵活,同时让程序员不要那么灵活。因为项目灵活才能适应客户的需求变更,程序员加上框架的限制能够降低人员流动的成本。框架就像马鞍,给马套上框架不是为了让马更舒服,而是为了骑马的人更舒服。
bbs.csdn.net/topics/360099689
另外,坦率的说,此框架可能更适合有“经验”的开发人员,更适合开发需要复杂视觉表现力的产品级项目。对于真正实战用过Struts,JSF,Tapestry,Turbine,Webwork等框架的朋友,相信会找到一种久违的“自由”感觉。
敏捷开发,一个很老很年轻的话题,不得不谈而且必须谈的话题,无论你用ASP、PHP、JSP还是ROR,它随时都触动我们开发的神经,让我们在懵懵焦虑中度过一个个不眠之夜。困了,烦了,唉......与其在烦恼中困惑,不如在烦恼中爆发,让我们展开JSP敏捷开发的畅想吧!
……
泛组件化是敏捷开发的最大陷阱!我们的WEB必须要有视觉表现力,WEB2.0要求更甚,但这种表现力需要精烹细调,个中过程远非简单的HTML、CSS之类,开发人员随时可能更改程序部分,不仅仅美工的问题,如果更改部分是“组件”,那你的麻烦大了
……
WEB特色来自handy,而非component!为何ASP、PHP以及新近出现的ROR,无一例外都把JSP打的满地找牙?随便找三点原因:
1. 解释性语言简单明了,学习、开发、维护成本都很低廉,让JAVA学究式语言的规范整洁无用武之地。
2. WEB开发无需大量企业级应用组件,一般提供mysql编程接口即可,这让JAVA的海量组件形同虚设。
3. 语言本身决定了单纯动态页面的编码难度。排除语言本身的优势,VBS,PHP,RUBY相对都很简单,说白了,能完成简单逻辑和mysql编程即可。
再来看看RoR为何近年来很火!?它是OO类语言,这是潮流,看看这两年C#和PHP5都在OO方面大力跟进就知道了,但为何只有RoR有排山倒海之势呢?!甚至出现了Grails跟风。另外,后者会上演GoG大戏吗?因为它有Java这个OO鼻主当靠山啊。No,回答是否定的,原因何在呢?回答这个问题前,得先回答另外一个问题:如果没有Rails,Ruby还翱翔的翅膀吗?除了语言本身的简洁,Rails最大的特点就是:习惯约定优于配置。所以,与其说Ruby,还不如说是Rails给WEB开发带来了暂新的模式。正是这个满足八二原则的开发模式极大激发了WEB开发人员的热情。尤其是Java开发人员,他们身负“组件化”重任,饱受各种配置规范的折磨,这种handy风格让他们看到了解脱的希望。假如将这种handy风格应用到Jsp,又会出现怎样的情形?问题是Jsp是烂泥吗?能扶上墙吗?答案是:能!针锋相对的理由:
1. 语言规范整洁,符合企业严整风范
2. 超大规模的企业级应用组件,便于web应用未来扩大之后的进一步发展
3. 单纯JSP页面只包含显示逻辑,在IDEA、Ellipse插件支持下也能展开敏捷开发
http://www.jdon.com/33785
你可以说可爱的php ,可爱的ror ,可爱的python ,甚至可爱的.net ,但是javaee ?他太复杂了。相比前三种技术,javaee 的技术体系更全面、更规整也更复杂,他的复杂性也让很多厂商望而止步,宁可选择简单甚至简陋的php ,这充分说明快速开发是这个时代最迫切的需求。
javaee 的servlet 、javabean 、jdbc 规范给了我们组件和容器的唯一标准,而更高级的支持,jsf 、jdo 规范却没能给予我们唯一的框架级标准,他们被认可的程度远低于相同领域的开源框架。尽管开源社区给了我们最丰富的选择,但是相比.net 、php 、ror 的全栈式服务,javaee 开发者必须DIY 。DIY 不但需要时间而且需要冒险,这种发烧友做的事情是企业所不愿意做的。一段时间以来,公司javaee 方向的招聘几乎清一色的要求struts 、spring 、hibernate 这几种主流框架的能力就是一种证明。
……
“任何优秀的语言,都可以帮助开发者写出优秀的代码,但不能阻止开发者写出糟糕的代码”。在这一点上,无论是javaee ,.net ,ror ,php 都不会例外。而开发框架就像是“一间有很多屋梁的房子”,“框架的强大之处不是他能让你做什么,而是他不能让你做什么”,其实如同语言一样,框架虽然可以给予开发一定程度的规范指导,但是这种指导仍然是有限的,这真应了那句老话:事在人为。
http://javaonejcy.iteye.com/blog/505128
《使用spring-loaded 提高java 应用开发效率 》:
在 web 应用开发领域,java 平台相比较其它脚本语言有明显的性能优势。但在 java 平台开发 web 应用有一个缺点就是频繁系统重启带来的开发效率下降。php 之类的脚本语言开发 web 应用时都可以做到“click,refresh”的开发流程,代码修改后刷新立即就可以看到结果。java web 应用往往需要编译-重启-刷新,在这个过程中,重启是最耗时的环节,如果项目比较大或者使用了一些重量级的应用服务器,重启耗时更为明显。
虽然 jvm 提供了一定程度的代码替换功能,但有很大局限,仅能修改方法实现代码。某些应用服务器(例如 jetty)也提供了类的热加载功能,但经常会出现 PermGen OOM 错误,这些解决方案都不太理想。
总之,一个工具如果使用的好,取其长而去其短,自然可以飞花摘叶皆可伤人。
另请参阅 :
《极简风格Web架构,jsp+jdbc的二次复辟》http://www.blogjava.net/calvin/archive/2008/07/31/218903.html
https://github.com/spring-projects/spring-loaded
http://jinnianshilongnian.iteye.com/blog/1887788
一个例子,保存数据库:
先设置头内容:
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@page import="java.sql.*" %>再写 JAVA 代码,既有 class 部分,也有直接写 Java 的:
class Connect { public Connection getConn(String driver, String jdbcUrl, java.util.Properties props){ Connection conn = null; try { Class.forName(driver); } catch (ClassNotFoundException e) { System.out.println("创建数据库连接失败,请检查是否安装对应的 Driver"); e.printStackTrace(); } try { if(props == null){ conn = DriverManager.getConnection(jdbcUrl); }else{ conn = DriverManager.getConnection(jdbcUrl, props); } System.out.println("数据库连接成功:" + conn); } catch (SQLException e) { System.out.println("数据库连接失败!"); e.printStackTrace(); } return conn; } //demo public Connection getConnSqlite(){ return getConn("org.sqlite.JDBC", "jdbc:sqlite:D:\\yuetv2014.sqlite", null); } } Connection conn = new Connect().getConnSqlite(); System.out.println(conn.getMetaData().getURL()); Statement statement = conn.createStatement(); String name = new String(request.getParameter("name").getBytes("iso-8859-1"), "UTF-8"); String contact = new String(request.getParameter("contact").getBytes("iso-8859-1"), "UTF-8"); String msg = new String(request.getParameter("msg").getBytes("iso-8859-1"), "UTF-8"); name = "('" + name + "',"; contact = "'" + contact + "',"; msg = "'" + msg + "')"; String sql = "INSERT INTO freeback (name, contact, msg) VALUES " + name + contact + msg; System.out.println(sql); try{ statement.execute(sql); out.write("<script>alert('提交成功!');history.go(-1);</script>"); }catch(Exception e){ e.printStackTrace(); out.write("<script>alert('提交失败!');history.go(-1);</script>"); }finally{ statement.close(); conn.close(); }
JSP 页面内置对象,这都是自带的、默认的,每张 JSP页面都会提供的。和 ASP 类似,它们是:
ASP | JSP | |
返回服务端的一些信息 | Server | 实际类型: org.apache.catalina.core.StandardWrapperFacadeStandardWrapperFacade 实现接口javax.servlet.ServletConfig page |
程序作用域下的全局对象 | Application | 实际类型: 实现接口javax.servlet.ServletContext |
客户端的请求 | Request | 实际类型: org.apache.catalina.connector.RequestFacadeRequestFacade 实现接口javax.servlet.http.HttpServletRequest e.g:String username=request.getParameter("username"); if(username.equals(""))... |
响应客户端 | Response | 实际类型: org.apache.catalina.connector.ResponseFacadeResponseFacade 实现接口javax.servlet.http.HttpServletRequest response.sendRedirect(...); |
会话 | Seesion | 实际类型: org.apache.catalina.session.StandardSessionFacadeStandardSessionFacade 实现接口javax.servlet.http.HttpSession |
页面上下文 | ObjectContext | pageContext 实际类型: org.apache.jasper.runtime.PageContextImplPageContextImpl 继承类javax.servlet.jsp.PageContext |
异常 | 无 | exception |
这方面应该多差文档,例如官方的就不错。待项目深入逐渐学习各个 API 之用法。
最简单的一种 <%@ include file="public/class.jsp" %>,ASP 和 JSP 写法一样。
不过和 ASP 不一样,不能在方法中混搭 HTML,如下面的是不行的哦:
<% public static voidgetFooter(){ %> <div>...</div> <% } %>于是,得使用 JSP 标签。最妙的是可以使用 <%=var%> 读取上下文变量。
<jsp:include page="bar.jsp" > <jsp:param name="extraparam" value="TestTest" /> <jsp:param name="extraparam2" value="<%=a%>" /> </jsp:include>
被 include 页面,用 $ 读取参数:
888-----------${param.extraparam}------${param.extraparam2}-------888
虽然 JSP 页面不如 ASP 那般灵活,但好在每个 include 页面都是独立的上下文,此时可以通过 request、session、application 等作用域共享的对象作为中介媒体,传递对象。它们本质上 map,因此可以通过 setArrtibute() 方法设置任意类型的对象。如下所示:
<% String name="zhouhaigang"; request.setAttribute("name", name); %> ${ name }
更复杂一些数据类型……
// jsp页面开头要导入你的User类。 List<User> userList = (List<User>)request.getAttribute("你传来的List"); int len = userList.size(); for(...){ ... }
下面包为什么不需要通过 Class/ JAR,引入就可以在 JSP 页面中直接使用?
<%@page import="java.sql.Connection"%> <%@page import="java.sql.DriverManager"%> <%@page import="java.sql.Statement"%> <%@page import="java.sql.ResultSet"%>
答案:因为 tomcat 已经通过系统环境变量 Classpath 获取到你的 JDK/JRE 的包,所以直接 import 就可以了。
如何编译一个 Class 呢?好处是别人不会轻易地看到你的代码!
详见该文《Tomcat+JSP经典配置实例 》,写得不错哦。
要定义方法,必须在 <% 后加个感叹号 !
<%! //此处有感叹号 private void aMethod(){ ...; } // 也可以是属性 private String aVariableOfClassScope; …… %>
这样的做法相当于在 Servlet 中重新定义了一个方法;如果没感叹号,就是普通的那种情况,那表示在 Servlet.doService() 方法写逻辑,你在生成的 java 类中看到混搭 HTML / Java 的代码。
<% //此处无感叹号,代码将会在 doService() 方法中 ...; %>
奇怪的是类也可以写在 doService() 方法中????
设置可以捕获代码中显式抛出的异常:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" errorPage="error.jsp"%>接收异常的页面
<%@page isErrorPage="true"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> Error~! <%=exception.getMessage()%></body> </html>
对于未知的、非捕获的异常也就是 500 出错,应该用 tomcat 的 500 err 代码跳转,但却又不利于调试,怎么两者都兼顾呢?
调试手段:
System.out.println("676"+uid_Str);
mylist.getClass().getName();
out.write(str);
设置 Tomcat 兼容中文 url:
在 Server.xml 中加入的 Connector 节点中加入 URIEncoding="UTF-8" 属性:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" />
去除JSP页面自动生成的空行 加入 <%@ page trimDirectiveWhitespaces="true" %>
----------------------------
输出 JAVA 变量,代替 <%=%>,这样就不会因为JSP inchuld 找不到上下文而让 Eclipse 报错了。
<jsp:expression>Global_WebRoot</jsp:expression>
---------------------------
JSP Mapping
<servlet>
<servlet-name>live_detail</servlet-name>
<jsp-file>/live/detail.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>live_detail</servlet-name>
<url-pattern>/live/detail</url-pattern>
</servlet-mapping>
JSP 的路径 按照 URL 的虚拟路径为准,包括 <%@ include file="../../public/head.jsp" %> 都如是。
《为什么JSP的内置对象不需要声明》
《用TOMCAT作简单的jsp web开发》
《JSP 原理和 Tomcat 配置》
《Tomcat+JSP经典配置实例》 编写自己的 Class
Unknow
利用 JSP 2 提供的 SimpleTagSupport 开发自定义标签
为 JSP 文件。
头引用是:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ page import="java.net.*, java.util.*, java.io.*"%>
// 跨域用代理 request.setCharacterEncoding("UTF-8"); String url = null; StringBuffer params = new StringBuffer(); Enumeration enu = request.getParameterNames(); int total = 0; while (enu.hasMoreElements()) { String paramName = (String)enu.nextElement(); if(paramName.equals("url")){ url = request.getParameter(paramName); }else{ if(total == 0){ params.append(paramName).append("=").append(URLEncoder.encode(request.getParameter(paramName), "UTF-8")); } else { params.append("&").append(paramName).append("=").append(URLEncoder.encode(request.getParameter(paramName), "UTF-8")); } System.out.println("paramName:" + paramName + "/" + request.getParameter(paramName)); ++total; } } String param = params.toString(); // out.println(url); if(url != null){ // 使用GET方式向目的服务器发送请求 URL connect = new URL(url); HttpURLConnection connection = (HttpURLConnection)connect.openConnection(); connection.setConnectTimeout(3000); connection.setReadTimeout(3000); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-type","application/x-www-form-urlencoded;charset=utf-8"); connection.connect(); System.out.println("url:" + url); System.out.println("para:" + param); OutputStream os = connection.getOutputStream(); os.write(param.getBytes()); os.flush(); os.close(); BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8")); String line; while((line = reader.readLine()) != null){ out.println(line); } reader.close(); }
附详尽注释的 jsp,可支持 GET/POST:
<%@ page import="java.util.Enumeration"%> <%! // java.io.*,java.net.* // 使用GET方式向目的服务器发送请求 public static String HTTPRequest(String url) throws IOException{ String result = "", line; // 拼凑get请求的URL字串,使用URLEncoder.encode对特殊和不可见字符进行编码 // String getURL = GET_URL + "&activatecode=" + URLEncoder.encode(url); URL getUrl = new URL(url); // 根据拼凑的URL,打开连接,URL.openConnection函数会根据URL的类型,返回不同的URLConnection子类的对象,这里URL是一个http,因此实际返回的是HttpURLConnection HttpURLConnection connection = (HttpURLConnection) getUrl.openConnection(); // 进行连接,但是实际上get request要在下一句的connection.getInputStream()函数中才会真正发到服务器 connection.connect(); // 取得输入流,并使用Reader读取,并设置编码,否则中文乱码 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8")); while ((line = reader.readLine()) != null){ result += line; // lines = new String(line.getBytes(), "utf-8"); // System.out.println(line); } // System.out.println(result); reader.close(); connection.disconnect();// 断开连接 return result; } // 使用 POST 方式向目的服务器发送请求 public static String HTTPPost(String url, String params) throws IOException{ String result = "", line; URL connect = new URL(url); HttpURLConnection connection = (HttpURLConnection)connect.openConnection(); connection.setConnectTimeout(3000); connection.setReadTimeout(3000); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-type","application/x-www-form-urlencoded;charset=utf-8"); connection.connect(); System.out.println("url:" + url); System.out.println("para:" + params); OutputStream os = connection.getOutputStream(); os.write(params.getBytes()); os.flush(); os.close(); BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8")); while((line = reader.readLine()) != null){ result += line; // out.println(line); } reader.close(); return result; } public static String[] getPOST_Data() throws UnsupportedEncodingException{ String url = null, param; StringBuffer params = new StringBuffer(); Enumeration enu = req.getParameterNames(); int total = 0; while (enu.hasMoreElements()) { String paramName = (String)enu.nextElement(); if(paramName.equals("url")){ url = req.getParameter(paramName); }else{ String pair = URLEncoder.encode(req.getParameter(paramName), "UTF-8"); //String pair = req.getParameter(paramName); if(total == 0)params.append(paramName).append("=").append(pair); else params.append("&").append(paramName).append("=").append(pair); System.out.println("paramName:" + paramName + "/" + req.getParameter(paramName)); ++total; } } param = params.toString(); String [] arr = {url, param}; return arr; } %>
http://www.jiucool.com/java-sending-http-requests-get-and-post-method-request/
Java 发送 http 请求 (get 与 post 方法请求),以下代码经本人亲自调试可用!可以直接使用之。注意:通过 BufferedReader 读取远程返回的数据时,必须设置读取编码,否则中文会乱码!
package com.jiucool.www.struts.action; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; public class post_request { public static final String GET_URL = "http://www.jiucool.com/request.php?key=j0r56u2"; public static final String POST_URL = "http://www.jiucool.com/request.php"; public static void readContentFromGet() throws IOException{ // 拼凑get请求的URL字串,使用URLEncoder.encode对特殊和不可见字符进行编码 String getURL = GET_URL + "&activatecode=" + URLEncoder.encode("久酷博客", "utf-8"); URL getUrl = new URL(getURL); // 根据拼凑的URL,打开连接,URL.openConnection函数会根据URL的类型, // 返回不同的URLConnection子类的对象,这里URL是一个http,因此实际返回的是HttpURLConnection HttpURLConnection connection = (HttpURLConnection) getUrl .openConnection(); // 进行连接,但是实际上get request要在下一句的connection.getInputStream()函数中才会真正发到 // 服务器 connection.connect(); // 取得输入流,并使用Reader读取 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));//设置编码,否则中文乱码 System.out.println("============================="); System.out.println("Contents of get request"); System.out.println("============================="); String lines; while ((lines = reader.readLine()) != null){ //lines = new String(lines.getBytes(), "utf-8"); System.out.println(lines); } reader.close(); // 断开连接 connection.disconnect(); System.out.println("============================="); System.out.println("Contents of get request ends"); System.out.println("============================="); } public static void readContentFromPost() throws IOException{ // Post请求的url,与get不同的是不需要带参数 URL postUrl = new URL(POST_URL); // 打开连接 HttpURLConnection connection = (HttpURLConnection) postUrl .openConnection(); // Output to the connection. Default is // false, set to true because post // method must write something to the // connection // 设置是否向connection输出,因为这个是post请求,参数要放在 // http正文内,因此需要设为true connection.setDoOutput(true); // Read from the connection. Default is true. connection.setDoInput(true); // Set the post method. Default is GET connection.setRequestMethod("POST"); // Post cannot use caches // Post 请求不能使用缓存 connection.setUseCaches(false); // This method takes effects to // every instances of this class. // URLConnection.setFollowRedirects是static函数,作用于所有的URLConnection对象。 // connection.setFollowRedirects(true); // This methods only // takes effacts to this // instance. // URLConnection.setInstanceFollowRedirects是成员函数,仅作用于当前函数 connection.setInstanceFollowRedirects(true); // Set the content type to urlencoded, // because we will write // some URL-encoded content to the // connection. Settings above must be set before connect! // 配置本次连接的Content-type,配置为application/x-www-form-urlencoded的 // 意思是正文是urlencoded编码过的form参数,下面我们可以看到我们对正文内容使用URLEncoder.encode // 进行编码 connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // 连接,从postUrl.openConnection()至此的配置必须要在connect之前完成, // 要注意的是connection.getOutputStream会隐含的进行connect。 connection.connect(); DataOutputStream out = new DataOutputStream(connection .getOutputStream()); // The URL-encoded contend // 正文,正文内容其实跟get的URL中'?'后的参数字符串一致 String content = "key=j0r53nmbbd78x7m1pqml06u2&type=1&[email protected]" + "&activatecode=" + URLEncoder.encode("久酷博客", "utf-8"); // DataOutputStream.writeBytes将字符串中的16位的unicode字符以8位的字符形式写道流里面 out.writeBytes(content); out.flush(); out.close(); // flush and close BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));//设置编码,否则中文乱码 String line=""; System.out.println("============================="); System.out.println("Contents of post request"); System.out.println("============================="); while ((line = reader.readLine()) != null){ //line = new String(line.getBytes(), "utf-8"); System.out.println(line); } System.out.println("============================="); System.out.println("Contents of post request ends"); System.out.println("============================="); reader.close(); connection.disconnect(); } }
HttpURLConnection.connect函数,实际上只是建立了一个与服务器的tcp连接,并没有实际发送http请求。无论是post还是get,http请求实际上直到HttpURLConnection.getInputStream()这个函数里面才正式发送出去。
在readContentFromPost() 中,顺序是重中之重,对connection对象的一切配置(那一堆set函数)都必须要在connect()函数执行之前完成。而对 outputStream的写操作,又必须要在inputStream的读操作之前。这些顺序实际上是由http请求的格式决定的。
http 请求实际上由两部分组成,一个是http头,所有关于此次http请求的配置都在http头里面定义,一个是正文content,在connect()函数里面,会根据HttpURLConnection对象的配置值生成http头,因此在调用connect函数之前,就必须把所有的配置准备好。
紧接着http头的是http请求的正文,正文的内容通过outputStream写入,实际上outputStream不是一个网络流,充其量是个字符串流,往里面写入的东西不会立即发送到网络,而是在流关闭后,根据输入的内容生成http正文。
至此,http请求的东西已经准备就绪。在getInputStream()函数调用的时候,就会把准备好的http请求正式发送到服务器了,然后返回一个输入流,用于读取服务器对于此次http请求的返回信息。由于http请求在getInputStream的时候已经发送出去了(包括http头和正文),因此在getInputStream()函数之后对connection对象进行设置(对http头的信息进行修改)或者写入 outputStream(对正文进行修改)都是没有意义的了,执行这些操作会导致异常的发生。
2014.4-30:这是 IP 库的第二个公开版本,这个版本放开了国内市一级的数据。个人感觉更为实用,下载:http://bbs.ajaxjs.com/forum.php?mod=viewthread&tid=3703
感谢高春辉大大提供资源:)http://tool.17mon.cn/ipdb.html
原版是 Java 文件的,要转换为 JSP 形式的也容易,改一下字符串控制方法就可以了。源码如下:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@page import="java.io.*" %> <%@page import="java.util.*" %> <%@page import="java.net.*" %> <%@page import="java.nio.*" %> <%@ page trimDirectiveWhitespaces="true" %> <% class IPDataHandler { private String IP_DATA_PATH = "C:/project/wlsc/mmc/17monipdb.dat"; private DataInputStream inputStream = null; private long fileLength = -1; private int dataLength = -1; private Map<String, String> cacheMap = null; private byte[] allData = null; public IPDataHandler(){ prefix = prefix.replace("ipLocation.jsp", "17monipdb.dat"); File file = new File(prefix); try { inputStream = new DataInputStream(new FileInputStream(file)); fileLength = file.length(); cacheMap = new HashMap<String, String>(); if (fileLength >Integer.MAX_VALUE) { throw new Exception("the filelength over 2GB"); } dataLength = (int) fileLength; allData = new byte[dataLength]; inputStream.read(allData, 0, dataLength); dataLength = (int)getbytesTolong(allData, 0, 4,ByteOrder.BIG_ENDIAN); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } private long getbytesTolong(byte[] bytes, int offerSet,int size,ByteOrder byteOrder){ if ((offerSet+size) > bytes.length || size <= 0) { return -1; } byte[] b = new byte[size]; for (int i = 0; i < b.length; i++) { b[i] = bytes[offerSet+i]; } ByteBuffer byteBuffer = ByteBuffer.wrap(b); byteBuffer.order(byteOrder); long temp = -1; if (byteBuffer.hasRemaining()) { temp = byteBuffer.getInt(); } return temp; } private long ip2long(String ip) throws UnknownHostException { InetAddress address = InetAddress.getByName(ip); byte[] bytes = address.getAddress(); long reslut = getbytesTolong(bytes, 0, 4,ByteOrder.BIG_ENDIAN); return reslut; } private int getIntByBytes(byte[] b,int offSet) { if (b == null || (b.length < (offSet+3))) { return -1; } byte[] bytes = Arrays.copyOfRange(allData, offSet, offSet+3); byte[] bs = new byte[4]; bs[3] = 0; for (int i = 0; i < 3;i++) { bs[i]=bytes[i]; } return (int)getbytesTolong(bs, 0, 4, ByteOrder.LITTLE_ENDIAN); } public String findGeography(String address){ if (address == null) { return "illegal address"; } if (dataLength < 4 || allData == null) { return "illegal ip data"; } String ip = "127.0.0.1"; try { ip = Inet4Address.getByName(address).getHostAddress(); } catch (UnknownHostException e) { e.printStackTrace(); } // String[] ipArray = StringUtils.split(ip, "."); // String[] ipArray = ip.split("."); //String[] ipArray = {"8", "8", "8", "8"}; String[] ipArray = ip.split("\\."); int ipHeadValue = Integer.parseInt(ipArray[0]); if (ipArray.length !=4 || ipHeadValue < 0 || ipHeadValue > 255) { return "illegal ip"; } if (cacheMap.containsKey(ip)) { return cacheMap.get(ip); } long numIp = 1; try { numIp = ip2long(address); } catch (UnknownHostException e1) { e1.printStackTrace(); } int tempOffSet = ipHeadValue* 4 + 4; long start = getbytesTolong(allData, tempOffSet, 4,ByteOrder.LITTLE_ENDIAN); int max_len = dataLength - 1028; long resultOffSet = 0; int resultSize = 0; for (start = start*8 + 1024; start < max_len; start+=8) { if (getbytesTolong(allData, (int)start+4, 4,ByteOrder.BIG_ENDIAN) >= numIp) { resultOffSet = getIntByBytes(allData, (int)(start+4+4)); resultSize = (char)allData[(int)start+7+4]; break; } } if (resultOffSet <= 0) { return "resultOffSet too small"; } byte[] add = Arrays.copyOfRange(allData, (int)(dataLength+resultOffSet-1024), (int)(dataLength+resultOffSet-1024 + resultSize)); try { if (add == null) { cacheMap.put(ip, new String("no data found!!")); } else { cacheMap.put(ip, new String(add,"UTF-8")); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return cacheMap.get(ip); } } IPDataHandler obj = new IPDataHandler(); %> <%=obj.findGeography("18.248.9.180")%>
获取 ip 并调用之:
// 获取请求 ip String ip = request.getHeader("x-forwarded-for"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) ip = request.getHeader("Proxy-Client-IP"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) ip = request.getHeader("WL-Proxy-Client-IP"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) ip = request.getRemoteAddr(); // 获取文件的绝对路径 String adsoPath = request.getSession().getServletContext().getRealPath(request.getRequestURI()); IPDataHandler obj = new IPDataHandler(adsoPath); //out.write(obj.findGeography("18.248.9.180")); out.write(obj.findGeography(ip));
初学入门推荐:《良葛格學習筆記》http://openhome.cc/Gossip/
Servlet 3.0 学习
编辑web.xml文件,在里边添加如下内容:
<servlet> <servlet-name>Test</servlet-name> <servlet-class>com.nantian.jndi.Test</servlet-class> <init-param> <param-name>jndi</param-name> <param-value>java:comp/env/jdbc/test</param-value> </init-param> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Test</servlet-name> <url-pattern>/Test</url-pattern> </servlet-mapping>jsp 也可以接受 url mapping,
<servlet> <servlet-name>service</servlet-name> <jsp-file>/public/service.jsp</jsp-file> </servlet> <servlet-mapping> <servlet-name>service</servlet-name> <url-pattern>/service/projectcase/*</url-pattern> </servlet-mapping>
String.format("请求 URL 没有带有 %s 参数!", key);
IllegalArgumentException/NullPointerException
// 泛型的例子,调用 // bar obj = new bar<VideoInfo>(); // ArrayList<VideoInfo> list = obj.render(service, album.getId()); class bar<T>{ public ArrayList<T> render(Service service, int albumId){ AlbumInfoService videoList_Service = (AlbumInfoService)service.getServiceInCache(); Map<String, Object> videoList = videoList_Service.findVideosByAlbum(albumId, 0 , 10).getVideos(); ArrayList<T> list = (ArrayList<T>)videoList.get("results"); return list; // Long totalCount = (Long)videoList.get("totalCount"); } } /** * 获取任意的配置内容,读取某个节点的配置对象 * 用法:<%=Config.getAnyConfig("companyInfo.name")%> * @param key * @return */ public static String getAnyConfig(String node){ return getAnyConfig(node, String.class); } /** * Config.getConfigNode("companyInfo.name", String.class) * ${viewController.companyInfo.name} * @param node * @param classType * @return */ @SuppressWarnings("unchecked") public static <T> T getAnyConfig(String node, Class<T> classType){ if(isLaunchByListener){ if(!isConfig_Ready) throw new IllegalArgumentException("Application Level Error:未加载程序配置,请检查 Web.xml 是否已经添加 Listener"); node = "bf.app_config." + node; System.out.println("正在获取节点:"+node); try { return (T)jsRuntime.eval(node); } catch (ScriptException e) { ajaxjs.Util.catchException(e, "读取配置%s失败!"); return null; } }else{ return (T)get(node, simpleConfig); } }
泛型是没有数组的。编译器不承认 HashMap[]这种形式的
创建 MAP(匿名实例的写法)Map map = new HashMap() {{ put( "Name" , "Unmi" ); put( "QQ" , "1125535" ); }};遍历 MAP:
for(java.util.Map.Entry mapEntry : responseData.entrySet()) { Key key = mapEntry.getKey(); Value value = mapEntry.getValue(); } for (String key : cookie.keySet()) cookieStr += key + "=" + cookie.get(key) + ";";
class Article extends BaseService<ArticleInfo>{ public void f(){ Class clazz=this.getClass(); Method[] methods=clazz.getMethods(); for(Method method:methods){ if(method.getName().equals("findById")) { System.out.println("---"); System.out.print(method.getReturnType().getName()+" "+method.getName()+" "); Class<?>[] types=method.getParameterTypes(); for(Class<?> type:types){ System.out.print(type.getName()+" "); } System.out.println(); } } } }
Webwork 到 2.0 版本的时候就是 Struts。现在公司的 WAP 项目在用,也终于让我有了学习的机会。
学习的还是以模版语言为主。
基本流程语句:
<s:if test="%{false}"> <div>Will Not Be Executed</div> </s:if> <s:elseif test="%{true}"> <div>Will Be Executed</div> </s:elseif> <s:else> <div>Will Not Be Executed</div> </s:else>
字符长度超出限定控制。如果超出 28 个,则自动截取,并显示 ……。
<p class="listDescription"> <s:if test="description.length() > 28"> <s:property value="description.substring(0, 28) + '……'" /> </s:if> <s:else> <s:property value="description" /> </s:else> </p>
include 页面如何传参数?
<!--返回条--> <s:set name="variable" value="'专辑'" /> <s:set name="variable2" value="'详情'" /> <%@ include file="public_new/returnBar.jsp" %> <!--// 返回条-->被引入的页面如下:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!--返回条--> <div class="returnBar"> <div class="goBackBtn" title="返回上一页" onclick="history.go(-1);"></div> <a href="." title="返回首页"><div class="homeBtn"></div></a> <s:property value="#variable" /><s:property value="#variable2" /> </div> <!--// 返回条-->
工具篇:
在自定义标签的使用中,EL 表达式是必不可少的一环。官方的 API 需要到了最新版本(EL v2.2, JDK7、Tomcat 7)才可以提供调用方法的直接方式。在老版本的 Tomcat 下,如何才可以像 Strus 那样调用方法呢?国外一位开发者给出了答案。首先复制以下两个类 ELMethod.java 和 Call.java 到你的项目中(适当修改包名)。
http://www.vineetmanohar.com/2010/07/how-to-pass-parameters-in-el-methods/
http://www.vineetmanohar.com/2010/08/calling-static-methods-from-el/
ELMethod.java:
package com.vineetmanohar.util; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * Extend this class to implement a server side method that takes arguments that * you would like to call via expression language. * * @author Vineet Manohar */ public abstract class ELMethod extends HashMap<Object, Object> { private static final long serialVersionUID = 1L; private final int numArgs; /** * @param numArgs * number of arguments this method takes */ protected ELMethod(int numArgs) { this.numArgs = numArgs; } @Override public Object get(Object key) { // if exactly one argument, call the result() method if (numArgs == 1) { return result(new Object[] {key}); } // if more tha one argument return new Arg(this, key); } public int getNumArgs() { return numArgs; } /** * 1) Implement this method in the child class. This method becomes * accesible via expression language. * * 2) Call this method using map syntax, by treating the instance of the * child class as a map of map of maps... * * For example, you could extends this class and create a class called * FormatDate. In that class, the result method would expect 2 arguments, * format string and date object. * * ${FormatDate["MMM dd"][user.creationDate]}, where dateFormat is an * instance of the child class. * * @param args * @return */ public abstract Object result(Object[] args); public static class Arg extends HashMap<Object, Object> { private static final long serialVersionUID = 1L; private List<Object> args = new ArrayList<Object>(); private ELMethod parent; public Arg(ELMethod eLMethod, Object key) { this.parent = eLMethod; this.args.add(key); } @Override public Object get(Object key) { this.args.add(key); // if all the arguments have been received, invoke the result method if (args.size() == parent.getNumArgs()) { Object retVal = parent.result(args.toArray()); return retVal; } return this; } } }Call.java:
package com.vineetmanohar.util; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class lets you call static methods from EL * * Step 1) Create an instance of this class and bind it to the "call" variable * in your EL. For example, in a JSP do the following: * request.setAttribute("call", new Call()); * * Step 2) Call any static method as follows: * * ${call["some.package.SomeClass.methodName"]["arg1"][arg2]} * * The first argument is the fully qualified class and method name. The * remaining arguments are arguments to the method that you are calling. * * Note: method overloading not supported * * Note: method must be static * * @author Vineet Manohar */ public class Call extends HashMap<String, Object> { private static final long serialVersionUID = 1L; @Override public Object get(Object key) { String fullyQualifiedMethodName = (String) key; // format of key is package.Class.method Pattern pattern = Pattern.compile("(.+)\\.([^\\.]+)"); Matcher m = pattern.matcher(fullyQualifiedMethodName); if (m.matches()) { String fqClassName = m.group(1); String methodName = m.group(2); Class<Object> clazz; try { clazz = (Class<Object>) Class.forName(fqClassName); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Invalid method name: " + key, e); } Method[] methods = clazz.getMethods(); for (final Method method : methods) { if ((method.getModifiers() & Modifier.STATIC) == 0) { continue; } if (method.getName().equals(methodName)) { // return the first method found int numParameters = method.getParameterTypes().length; if (numParameters == 0) { return invokeMethod(method); } return new ELMethod(numParameters) { @Override public Object result(Object[] args) { return invokeMethod(method, args); } }; } } } throw new IllegalArgumentException("Invalid method name: " + key + ". Must be a fully qualified class and method name"); } private Object invokeMethod(final Method method, Object... args) { try { return method.invoke(null, args); } catch (IllegalArgumentException e) { throw new RuntimeException("Exception while executing method", e); } catch (IllegalAccessException e) { throw new RuntimeException("Exception while executing method", e); } catch (InvocationTargetException e) { throw new RuntimeException("Exception while executing method", e); } } }然后实例化 Call 对象。反正在 JSP 里随你喜欢即可,如 request.setAttribute("call", new Call()); 或 <jsp:useBean id="call" class="Call" scope="application" /> 都冇问题。
最后一步就是解决问题的目标,调用方法并传参数了。试写出表达式:${call["com.mycompany.util.DateUtils.formatDate"]["MMM, dd"][account.creationDate]},其中 com.mycompany.util.DateUtils.formatDate 是 Java 静态方法,要写全称。最后两项是传入的参数,分别可以是字符串和当然 EL 对象(如account.creationDate)。
观其上述源码,实际是利用了 Java 反射的原理调用方法。不要注意一点的是,不支持方法的重载哦(Overloaded)。
如果不是静态方法,实例方法可不可以调用呢?按照作者的介绍也是可以的。