Web框架是开发者在使用某种语言编写Web应用服务端项目时关于架构的最佳实践。很多Web框架是从实际的Web项目抽取出来的,仅和Web的请求和响应处理有关,形成一个基础,在开发别的应用项目的时候则可以从这个剥离出来的基础做起,让开发者更关注更具体的业务问题,而不是Web的请求和响应的控制。
框架很多,但套路基本类似,帮你隐藏很多关于HTTP协议细节内容,专注功能开发。
但对一个初学者来说,过早的接触框架往往是事倍功半!同样一个问题,换一种框架你可能需要从头开始研究。
下面是针对初学Java开发Web过程一些个人见解和思路,高手可略过。
1.基本要求:Java编程基础
有良好的Java语言编程基础,这是必须的,在讨论Web开发技术时提了一个Java编程基础的问题会被鄙视的。
2.环境准备(Eclipse+Tomcat)
选择一个你喜爱的Servlet容器,或者说大一点就是应用服务器,推荐Tomcat、Resin或者Jetty这些轻量级的产品。这三个产品下载zip包解压后就可以用了。如果你不熟悉Tomcat的话请不要使用exe版本的Tomcat,那会徒增很多烦恼。
把应用服务器启动起来并能访问到其默认的页面为准。
关于开发工具:不推荐使用MyEclipse和Eclipse的JEE版本,徒增烦恼、运行缓慢而且还让你无法了解Web项目的结构。普通的Eclipse或者你喜欢的开发工具就足够了,能支持普通Java项目开发即可。
为了方便,我做了一个最基本的Java项目——ServletDemo.zip(评论区见链接),你可将它导入到Eclipse里就是一个完整的、最简单的Web项目。
然后将下面XML内容替换Tomcat下的conf/server.xml文件:
其中D:\WORKDIR\ServletDemo替换为你导入的项目路径,再次启动Tomcat后在浏览器打开http://localhost:8080/hello便可看到HelloWorld的输出信息。
3.了解Servlet和Filter
好了,我已经把环境搭起来了,接下来该干嘛呢?
前面的步骤为的是搭建一个测试的环境,然后让你了解一个最基本的JavaWeb项目的结构。
一个最基本的JavaWeb项目所需的jar包只需要一个servlet-api.jar,这个jar包中的类大部分都是接口,还有一些工具类,共有2个包,分别是javax.servlet和javax.servlet.http。我把这个jar包放到了webapp目录外的一个独立packages文件夹里,这是因为所有的Servlet容器都带有这个包,你无需再放到Web项目里,我们放到这里只不过是编译的需要,运行是不需要的。如果你硬是把servlet-api.jar放到webapp/WEB-INF/lib目录下,那么Tomcat启动时还会报一个警告信息。
JavaWeb项目还需要一个非常重要的配置文件web.xml,在这个项目中已经被我最小化了,只保留有用的信息:
hello_worlddemo.HelloServlet1hello_world/hello
每个servlet都必须在web.xml中定义并进行URL映射配置,早期Java开发Web在没有框架满天飞的时候,这个文件会定义了大量的servlet,或者有人为了省事干脆来一个/servlet/*来通过类名直接调用Servlet。
Servlet规范里还有另外一个非常重要而且非常有用的接口那就是Filter过滤器。
下面是一个最简单的Filter类以及相应的定义方法:
packagedemo;importjava.io.IOException;importjavax.servlet.Filter;importjavax.servlet.FilterChain;importjavax.servlet.FilterConfig;importjavax.servlet.ServletException;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServletRequest;publicclassHelloFilterimplementsFilter{@Overridepublicvoidinit(FilterConfigarg0)throwsServletException{System.out.println("Filter初始化");}@OverridepublicvoiddoFilter(ServletRequestreq,ServletResponseres,FilterChainchain)throwsIOException,ServletException{HttpServletRequestrequest=(HttpServletRequest)req;System.out.println("拦截URI="+request.getRequestURI());chain.doFilter(req,res);}@Overridepublicvoiddestroy(){System.out.println("Filter结束");}}
在web.xml中的配置必须放在Servlet的前面:
访问http://localhost:8080/hello时看看Tomcat控制台有何输出信息。
4.Servlet和HTTP的对应关系
Servlet是J2EE最重要的一部分,有了Servlet你就是J2EE了,J2EE的其他方面的内容择需采用。而Servlet规范你需要掌握的就是servlet和filter这两项技术。绝大多数框架不是基于servlet就是基于filter,如果它要在Servlet容器上运行,就永远也脱离不开这个模型。
为什么Servlet规范会有两个包,javax.servlet和javax.servlet.http,早先设计该规范的人认为Servlet是一种服务模型,不一定是依赖某种网络协议之上,因此就抽象出了一个javax.servlet,同时在提供一个基于HTTP协议上的接口扩展。但是从实际运行这么多年来看,似乎没有发现有在其他协议上实现的Servlet技术。
javax.servlet和javax.servlet.http这两个包总共加起来也不过是三十四个接口和类。你需要通过J2EE的JavaDoc文档熟知每个类和接口的具体意思。特别是下面几个接口必须熟知每个方法的意思和用途:
HttpServlet
ServetConfig
ServletContext
Filter
FilterConfig
FilterChain
RequestDispatcher
HttpServletRequest
HttpServletResponse
HttpSession
一些Listenser类
再次强调HttpServletRequest和HttpServletResponse这两个接口更应该是烂熟于心。
如果你从字面上无法理解某个方法的意思,你可以在前面那个项目的基础上做实验看看其输出,再不行你可以到讨论区提问,这样的提问非常明确,很多人都可以帮到你。
为什么我这么强调HttpServletRequest和HttpServletResponse这两个接口,因为Web开发是离不开HTTP协议的,而Servlet规范其实就是对HTTP协议做面向对象的封装,HTTP协议中的请求和响应就是对应了HttpServletRequest和HttpServletResponse这两个接口。
你可以通过HttpServletRequest来获取所有请求相关的信息,包括URI、Cookie、Header、请求参数等等,别无它路。因此当你使用某个框架时,你想获取HTTP请求的相关信息,只要拿到HttpServletRequest实例即可。
而HttpServletResponse接口是用来生产HTTP回应,包含Cookie、Header以及回应的内容等等。
5.再谈谈Session
HTTP协议里是没有关于Session会话的定义,Session是各种编程语言根据HTTP协议的无状态这种特点而产生的。其实现无非就是服务器端的一个哈希表,哈希表的Key就是传递给浏览器的名为jsessionid的Cookie值。
当需要将某个值保存到session时,容器会执行如下几步:
a.获取jsessionid值,没有的话就生成一个,也就是request.getSession()这个方法b.拿到的HttpSession对象实例就相当于一个哈希表,你可以往哈希表里存放数据(setAttribute)c.你也可以通过getAttribute来获取某个值
而这个名为jsessionid的Cookie在浏览器关闭时会自动删除。把Cookie的MaxAge值设为-1就能达到浏览器关闭自动删除的效果。
6.关于JSP
首先我已经不用JSP很多年了,现在一直是使用Velocity模板引擎。
任何一个JSP页面在执行的时候都会编译成一个Servlet类文件,如果是Tomcat的话,这些生成的java文件会放置在{TOMCAT}/work目录下对应项目的子目录中,例如Tomcat生成的类文件如下:
packageorg.apache.jsp;importjavax.servlet.*;importjavax.servlet.http.*;importjavax.servlet.jsp.*;importjava.util.*;publicfinalclasstest_jspextendsorg.apache.jasper.runtime.HttpJspBaseimplementsorg.apache.jasper.runtime.JspSourceDependent{privatestaticfinalJspFactory_jspxFactory=JspFactory.getDefaultFactory();privatestaticjava.util.List_jspx_dependants;privatejavax.el.ExpressionFactory_el_expressionfactory;privateorg.apache.tomcat.InstanceManager_jsp_instancemanager;publicjava.util.ListgetDependants(){return_jspx_dependants;}publicvoid_jspInit(){_el_expressionfactory=_jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();_jsp_instancemanager=org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());}publicvoid_jspDestroy(){}publicvoid_jspService(finalHttpServletRequestrequest,finalHttpServletResponseresponse)throwsjava.io.IOException,ServletException{finalPageContextpageContext;HttpSessionsession=null;finalServletContextapplication;finalServletConfigconfig;JspWriterout=null;finalObjectpage=this;JspWriter_jspx_out=null;PageContext_jspx_page_context=null;try{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("\r\n");out.write("\r\n");out.write("Test\r\n");out.write("\r\n");out.write("\r\n");out.write("
TestDemo(oschina)\r\n");out.write("\r\n");EnumerationNames=request.getHeaderNames();while(Names.hasMoreElements()){Stringname=(String)Names.nextElement();Stringvalue=request.getHeader(name);out.write("\r\n");out.write("
\r\n");out.write("");out.print(name);out.write("\r\n");out.write("");out.print(value);out.write("\r\n");out.write("\r\n");out.write("\r\n");out.write("");}out.write("\r\n");out.write("\r\n");out.write("\r\n");out.write("");}catch(Throwablet){if(!(tinstanceofSkipPageException)){out=_jspx_out;if(out!=null&&out.getBufferSize()!=0)try{out.clearBuffer();}catch(java.io.IOExceptione){}if(_jspx_page_context!=null)_jspx_page_context.handlePageException(t);}}finally{_jspxFactory.releasePageContext(_jspx_page_context);}}}
在servlet中有一个包javax.servlet.jsp是跟JSP相关的一些接口规范定义。JSP比Servlet方便的地方在于可直接修改立即生效,不像Servlet修改后必须重启容器才能生效。
因此JSP适合用来做视图,而Servlet则适合做控制层。
7.总结
罗哩罗嗦一大堆,归纳一下就是下面几点:
熟知Servlet规范之前,请不要学习任何框架
使用最简单的工具,不要任何向导和可视化
熟知HTTP协议
等你真的掌握了Servlet规范再去看框架,便会觉得一些都小菜。总之一点:不要被框架牵着鼻子走,框架是你的工具,它应该听你的!