[Java]Servlet编程(持续更新)

1.Servlet属于JavaEE的部分,所以JavaSE的文档是没有Servlet API的。JavaEE API又包括很多其他技术,找一个专门的Servlet API文档。

2.Servlet程序运行在服务器端,由服务器调用,所以先在服务器端建立一个Web应用,根据Web应用的组织结构,把java程序放在webapps\workday10\WEB-INF\classes目录中。

3.导包,查阅Servlet API,编写测试程序:

package cn.itcast;

import java.io.*;
import javax.servlet.*;

//服务器端运行程序,客户程序请求服务器,服务器调用
public class  FirstServlet extends GenericServlet
{
	public void service(ServletRequest req,
                             ServletResponse res)
                      throws ServletException,
                             java.io.IOException
	{
		OutputStream out=res.getOutputStream();
		out.write("hello servlet!!!".getBytes());
	}
}

4.编译

直接编译的结果:

D:\Program Files\apache-tomcat-7.0.69\webapps\workday10\WEB-INF\classes>javac Fi
rstServlet.java
FirstServlet.java:4: 错误: 程序包javax.servlet不存在
import javax.servlet.*;
^
FirstServlet.java:7: 错误: 找不到符号
public class  FirstServlet extends GenericServlet
                                   ^
  符号: 类 GenericServlet
FirstServlet.java:9: 错误: 找不到符号
        public void service(ServletRequest req,
                            ^
  符号:   类 ServletRequest
  位置: 类 FirstServlet
FirstServlet.java:10: 错误: 找不到符号
                             ServletResponse res)
                             ^
  符号:   类 ServletResponse
  位置: 类 FirstServlet
FirstServlet.java:11: 错误: 找不到符号
                      throws ServletException,
                             ^
  符号:   类 ServletException
  位置: 类 FirstServlet
5 个错误

原因:Servlet相关的包不在Java环境的JavaSE API中,要正确编译程序,需要将Servlet API相关jar包所在目录加入到classpath当中(编译器到classpath中寻找Class文件),而Tomcat服务器可以运行Servlet,其中必有Servlet的jar包(想想Eclipse编程,那些第三方jar包,同样要导入到所写程序环境路径下),故将其jar包所在目录加入到classpath中重新编译:

D:\Program Files\apache-tomcat-7.0.69\webapps\workday10\WEB-INF\classes>set clas
spath=D:\Program Files\apache-tomcat-7.0.69\lib\servlet-api.jar

D:\Program Files\apache-tomcat-7.0.69\webapps\workday10\WEB-INF\classes>javac -d
 . FirstServlet.java

D:\Program Files\apache-tomcat-7.0.69\webapps\workday10\WEB-INF\classes>

5.在该应用的web.xml中为Servlet配置对外访问路径(借鉴Tomcat的示例):





    
        FirstServlet
        cn.itcast.FirstServlet
    

    
        FirstServlet
        /FirstServlet
    




(注:servlet-name为名称,servlet-class要带上包名,servlet-mapping将名为FirstServlet的Servlet映射到一个对外访问路径,/代表workday10)

一个Servlet完成。

6.启动Tomcat服务器,用浏览器访问:

http://localhost:8080/workday10/FirstServlet

结果:

hello servlet!!!


Eclipse开发Servlet

Tomcat启动时会加载所有的应用,上面的程序是手写,用jdk1.6编译的,而即将用Eclipse开发的Servlet设置成了jdk1.5(运行),故Tomcat会加载jdk1.6编译,导致编译环境版本高于运行环境,所以要删除上面那个Web应用。

1.准备工作:设置服务器----->Window--->Preferences-->Servers--Tomcat-->Tomcat6.x导入本机Tomcat服务器所在目录,注意一定设置成Enabled,可选JDK版本,仍然注意运行环境要高于编译环境。

2.建立一个Web工程workday10,此为一个Web应用的实际根目录,在Eclipse创建工程的选项卡中,workday10内部的目录Web root folder实际保存着该Web应用的组织结构,Context root URL为该Web应用的虚拟目录映射,所以实际访问时访问/workday10/xxx.注意下面还有JavaEE环境的选择(导入JavaEE的许多jar包)

3.在src目录下建立Servlet程序,带上包名,直接继承GenericServlet,实现service方法,注意参数出现问题是因为没有导入用到的JavaEE的源码,去找apache-tomcat-6.0.45-src文件夹,tomcat源码自带用到的JavaEE的源码。导入后重新实现该方法,参数正常。

4.部署启动服务器,注意选择第一步配置的服务器版本,Eclipse启动服务器,仍然选择第一步配置的版本。

5.配置(练习用,以后可用Eclipse自动配置)WEB_INF目录下的web.xml文件:



	
  
  	ServletDemo1
  	cn.itcast.ServletDemo1
  
  
  	ServletDemo1
  	/ServletDemo1
  
	
  
    index.jsp
  


注意Servlet的url-pattern./仍然代表workday10.

6.开启浏览器,访问:

http://localhost:8080/workday10/ServletDemo1

结果:

Hello,Servlet...

访问成功。


Servlet调用过程和生命周期

客户端访问Servlet流程图:

[Java]Servlet编程(持续更新)_第1张图片

[Java]Servlet编程(持续更新)_第2张图片

[Java]Servlet编程(持续更新)_第3张图片

(注:关于创建Servlet对象,只在第一次访问这个Servlet时才创建,此后一直驻留在内存中,直到web服务器停止或删除此应用;向客户机输出的数据是先写到response对象中,再由服务器取出发送给浏览器)


Servlet生命周期:第一次访问时Servlet对象创建,调用init方法,处理服务器请求调用service方法,此后一直驻留在内存中不再重新创建,destroy方法在web服务器停止时,或把此应用删除时执行,摧毁Servlet对象。(经典面试题,还有可能询问所有其他和web服务相关对象的生命周期)


HttpServlet:复写了service方法,其内容是判断处理各种类型的用户请求,其中我们常用的只有doGet,doPost,所以我们继承了HttpServlet后,一般几乎不会复写service方法,而只复写doGet,doPost方法。

一些重要细节

1.改错误的类名------>不在代码中改,用重构功能,这样其他程序也跟着改;

2.在Eclipse中改类模板----->去MyEclipse根目录搜索servlet.java文件,改成自动生成自己想要的样子;

3.注意同一个应用下不同Servlet的url-pattern;拷贝工程时,需要在Properties-->MyEclipse-->Web中改Web Context-root;

4.用多个servlet-mapping将同一个Web资源映射成多个url地址,比如1.html-->伪静态;

5.服务器会自动监测web.xml的改动,所以只改动web.xml不需要重新发布--->在服务器的context.xml中配置WatchedResource,让所有的web应用自动加载;

6.Servlet对象只创建一次,而有多少次客户端请求就调用多少次service方法,创建多少个ServletRequest,ServletResponse对象

7.url-pattern的/*与*xxx,通配符匹配,找长的最像的,*xxx优先级低于所有其他模式

8.缺省Servlet:处理其他Servlet都不处理的请求,将url-pattern配置成/

!注意:访问所有web资源包括静态资源,实际都是访问Servlet(这也是前面虚拟目录映射的原理),比如1.html,服务器会先寻找有没有哪个Servlet映射到这个地址,如果没有,调用默认缺省Servlet在web应用目录下查找这个资源,如果有则调出这个资源返给浏览器,如果你自己配置缺省Servlet,则覆盖掉默认的,将所有其他Servlet不处理的请求映射到自己的缺省Servlet.举例:

直接在web目录下建立一个1.html,访问:

http://localhost:8080/workday10/1.html

这是服务器的自动虚拟目录映射,实际就是调用默认缺省Servlet.

在应用的web.xml中配置自己的缺省Servlet:


    ServletDemo3
    /servlet/ServletDemo3
  
  
  
    ServletDemo3
    /
  

注意一个Servlet可以映射多个。上面第二个映射为缺省映射。

这时访问:

http://localhost:8080/workday10/servlet/ServletDemo3

正常显示结果:

Hello,Servlet Still Here...

访问:

http://localhost:8080/workday10/1.html

服务器调用你的缺省Servlet,结果:

Hello,Servlet Still Here...

随意访问其他不存在的页面也是一样:

http://localhost:8080/workday10/dgdeggegerge

结果:

Hello,Servlet Still Here...

服务器默认缺省Servlet在其/Conf/web.xml中设置,被所有应用加载:

    
        default
        org.apache.catalina.servlets.DefaultServlet
        
            debug
            0
        
        
            listings
            false
        
        1
    

其中:

        1

服务器启动时即创建。

映射:

    
        default
        /
    

!不要设置自己的缺省Servlet,这样你的服务器中所有的静态资源别人都无法访问了!(懂原理一带而过)


线程安全问题:

多个客户端并发访问同一个Servlet,Web服务器会为每个客户端请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就可能产生线程安全问题。

举例:

public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		int i=0;
		i++;
		
		System.out.println(i);
	}

不会有安全问题。

int i=0;
	
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		
		i++;
		
		System.out.println(i);
	}

会有线程安全问题:多个客户端线程并发访问同一个资源(i)

还要注意的就是static字段,比如一个static集合可能导致内存溢出!每次添加完数据后要记得移除数据!


测试多线程:

	int i=0;
	
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		
		i++;
		
		//睡10秒
		try {
			Thread.sleep(1000*10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		response.getOutputStream().write((i+"").getBytes());
	}

多浏览器同时访问,前面访问的可能输出后面访问+1后的值。

解决:放入同步代码块中,但在实际中根本不能用---多用户访问网页!

Servlet规范中解决方式:implements SingleThreadModel标记接口,访问冲突时创建另一个Servlet提供服务。

你可能感兴趣的:(Java,Web)