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

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

2016-05-05 10:05 525 查看
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的示例):

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at
 http://www.apache.org/licenses/LICENSE-2.0 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">

<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>cn.itcast.FirstServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/FirstServlet</url-pattern>
</servlet-mapping>

</web-app>


(注: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文件:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>cn.itcast.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/ServletDemo1</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>


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

6.开启浏览器,访问:
http://localhost:8080/workday10/ServletDemo1
结果:

Hello,Servlet...


访问成功。

Servlet调用过程和生命周期

客户端访问Servlet流程图:







(注:关于创建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:

<servlet-mapping>
<servlet-name>ServletDemo3</servlet-name>
<url-pattern>/servlet/ServletDemo3</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>ServletDemo3</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>


注意一个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中设置,被所有应用加载:

<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>


其中:

<load-on-startup>1</load-on-startup>


服务器启动时即创建。

映射:

<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>


!不要设置自己的缺省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提供服务。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: