java_servlet_jsp
来源:互联网 发布:网络卫星电视机顶盒 编辑:程序博客网 时间:2024/06/06 12:59
基础知识
java->servlet->jsp [技术演变过程]
回顾一下我们现有的技术:
java 基础(面向对象,集合,界面,线程,文件,网络)
jdbc (java 的数据库编程)
oracle / mysql / sqlserver
html css javascript (web 开发) ->网页设计
xml
serlvet+jsp ->java web开发[使用java技术做 web开发]
bs 和 cs的比较
(1)BS:browserserver 浏览器服务器
(2)cs clientserver 客户服务
为什么需要servlet技术?
比如需求:我们希望用户可以贴,用户还可以回复 ....这样一些和用户可以交互的功能,用普通的java技术就完成不了, sun 就开发了 servlet技术供程序员使用.
servlet的介绍
servlet 其实就是java程序(java类)
该 java 程序(java 类)要遵循servlet开发规范
serlvet是运行在服务端
serlvet 功能强大,几乎可以完成网站的所有功能是学习jsp基础
tomcat 和 servlet 在网络中的位置
servlet的生命周期是怎样的/servlet究竟是怎样工作的
UML 时序图帮助大家理解
答: 标准版本:
①WEB服务器首先会检查是否已经装载并创建了该servlet实例对象。如果是直接进行第④步,否则执行第②步。
②装载并创建该Servlet的一个实例对象。
③调用Servlet实例对象的init()方法。
④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用service()方法并将请求和响应作为参数传递进去。
⑤WEB应用被停止或重启之前,Servlet引擎将卸载Servlet,在卸载之前调用Servlet的destroy()方法
1. 当serlvet 第一次被调用的时候,会触发init函数,该函数会把servlet实例装载到内存.init函数只会被调用一次
2. 然后去调用servlet 的 service 函数
3. 当第二次后访问该servlet 就直接调用 service 函数.
4. 当 web应用 reload 或者 关闭 tomcat 或者 关机 都会去调用destroy函数,该函数就会去销毁serlvet
5. Servlet的生命周期
当客户端第一次向web服务器发出一个servlet请求时,web服务器将会创建一个该servlet的实例,并且调用servlet的init()方法;如果当服务器已经存在了一个servlet实例,那么,将直接使用此实例;然后再调用service()方法,service()方法将根据客户端的请求方式来决定调用对应的doXXX()方法;当 web应用 reload 或者 关闭 tomcat 或者 关机,web服务器将调用destroy()方法,将该servlet从服务器内存中删除。
生命全过程:
1.加载
2.实例化
3.初始化
4.处理请求
5.退出服务
开发servlet有三种方法
1实现 Servlet接口
2通过继承 GenericServlet
3通过继承 HttpServlet
(1) 在软件公司 90%都是通过该方法开发.
(2) 举例说明; 还是显示 hello,world 当前日期
代码:
package com.hsp;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyHttpServlet extends HttpServlet
{
//在HttpServlet 中,设计者对post 提交和 get提交分别处理
//回忆 <form action="提交给?" method="post|get"/>,默认是get
protected void doGet(HttpServletRequest req,HttpServletResponse resp)throws ServletException,
java.io.IOException{
resp.getWriter().println("i am httpServet doGet()");
}
protected void doPost(HttpServletRequest req,HttpServletResponse resp)throws ServletException,
java.io.IOException{
resp.getWriter().println("i am httpServet doPost() post name="+req.getParameter("username"));
}
}
还有一个login.html
<html>
<body>
<form action="/hspWeb1/MyHttpServlet" method="post">
u:<input type="text" name="username"/>
<input type="submit" value="login"/>
</body>
</html>
小结 get 提交 和 post的提交的区别
① 从安全看 get<post 因为get 会把提交的信息显示到地址栏
② 从提交内容看 get<post get 一般不要大于2k, post理论上无限制,但是在实际开发中,建议不要大于64k
③ 从速度看 get>post
④ Get可以保留uri中的参数,利于收藏
使用ide来开发servlet
使用ide (eclipse[java se]+myeclipse[插件 可以支持jsp/servlet/struts/hibernate/spring..])开发servlet
需求:使用 ide 开发一个servlet ,该servlet 显示 hello,world, 和当前日期
开发步骤:
(1) 建立web工程
(2) 在Src 目录下创建了一个包 com.hsp.servlet
(3) 开发一个Servlet
MySerlvet 的代码:
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out =response.getWriter();
out.println("hello"+newjava.util.Date().toString() );
}
public void doPost(HttpServletRequest request,HttpServletResponse response)
throwsServletException, IOException {
this.doGet(request,response);
}
Servlet的细节问题
一个已经注册的Servlet可以被多次映射即:
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<!-- servlet的注册名 -->
<servlet-name>MyServlet1</servlet-name>
<!-- servlet类的全路径(包名+类名) -->
<servlet-class>com.hsp.servlet.MyServlet1</servlet-class>
</servlet>
<!-- 对一个已经注册的servlet的映射 -->
<servlet-mapping>
<!-- servelt的注册名 -->
<servlet-name>MyServlet1</servlet-name>
<!-- servlet的访问路径 -->
<url-pattern>/MyServlet1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MyServlet1</servlet-name>
<url-pattern>/hsp</url-pattern>
</servlet-mapping>
① 当映射一个servlet时候,可以多层 比如
<url-pattern>/servlet/index.html</url-pattern> ok
从这里还可以看出,后缀名是 html 不一定就是 html,可能是假象.
② 使用通配符在servlet映射到URL中
有两种格式:
第一种格式 *.扩展名 比如 *.do *.ss
第二种格式 以 / 开头 同时以 /* 结尾 比如 /* /news/*
通配符练习题:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
问题(面试题):
当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,哪个servlet响应
Servlet引擎将调用Servlet1。
当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应
Servlet引擎将调用Servlet3。
当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet1。
当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet2。
当请求URL为“/xxx/yyy/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet2。
在匹配的时候,要参考的标准:
(1) 看谁的匹配度高,谁就被选择
(2) *.do 的优先级最低
③ Servlet单例问题
当Servlet被第一次访问后,就被加载到内存,以后该实例对各个请求服务.即在使用中是单例.
因为 Servlet是单例,因此会出现线程安全问题: 比如:
售票系统. 如果不加同步机制,则会出现问题:
这里我给大家一个原则:
(1) 如果一个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制
synchronized (对象){
//同步代码
}
(2)如果一个变量不需要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题
④ servlet 中的 <load-on-startup> 配置
需求: 当我们的网站启动的时候,可能会要求初始化一些数据,(比如创建临时表), 在比如:
我们的网站有一些要求定时完成的任务[ 定时写日志,定时备份数据.. 定时发送邮件..]
解决方法: 可以通过 <load-on-startup> 配合 线程知识搞定.
先说明<load-on-startup>: 通过配置<load-on-startup> 我们可以指定某个Servlet 自动创建.
我们来模拟一个定时发送电子邮件的功能:
实现思路:
sendEmailTable
id content sendtime
1 “hello” 2011-11-11 20:11
2 “hello2” 2012-11-11 10:00
看看如何线程去完成任务:
这里的代码请参考项目:
SendMailThread.java
package com.hsp.model;
public class SendEmailThread extends Thread{
@Override
public void run() {
int i=0;
try {
while(true){
//每休眠一分钟,就去扫表sendmail, 看看那份信件应当被发出
Thread.sleep(10*1000);
System.out.println("发出 第"+(++i)+"邮件");//javamail
}
} catch (Exception e) {
e.printStackTrace();
// TODO: handle exception
}
}
}
MyInitServlet1.java
public void init() throws ServletException {
// Put your code here
System.out.println("MyInitServlet1 的init被调用..");
//完成一些初始化任务
System.out.println("创建数据库,表,读取参数");
//创建一个线程
SendEmailThread sendEmailThread=new SendEmailThread();
sendEmailThread.start();
}
说明:
<!-- 1表示该servlet被 init的顺序 -->
<load-on-startup>1</load-on-startup>
ServletConfig对象:该对象主要用于 读取 servlet的配置信息.
案例:
<servlet>
<servlet-name>ServletConfigTest</servlet-name>
<servlet-class>com.hsp.servlet.ServletConfigTest</servlet-class>
<!-- 这里可以给servlet配置信息,这里配置的信息,只能被该servlet 读取 -->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</servlet>
如何使用
String encoding=this.getServletConfig().getInitParameter("encoding");
补充说明:这种配置参数的方式,只能被某个Servlet独立使用.如希望让所有的Servlet都去读取某个参数,这样配置:
<!-- 如果这里配置参数,可被所有servlet读取 -->
<!--
<context-param>
<param-name></param-name>
<param-value></param-value>
</context-param>
-->
如果要把所有的参数都读取,则使用 如下方法 :
Enumeration<String> names=this.getServletConfig().getInitParameterNames();
while(names.hasMoreElements()){
String name=names.nextElement();
System.out.println(name);
System.out.println(this.getServletConfig().getInitParameter(name));
}
加入防止盗链下载.
HttpServletResponse的再说明
getWriter()
getOutputStream();
区别
1. getWriter() 用于向客户机回送字符数据
2. getOutputStream() 返回的对象,可以回送字符数据,也可以回送字节数据(二进制数据)
OutputStream os=response.getOutputStream();
os.write("hello,world".getBytes());
如何选择:
如果我们是回送字符数据,则使用 PrintWriter对象 ,效率高
如果我们是回送字节数据(binary date) ,则只能使用 OutputStream
☞ 这两个流不能同时使用.
比如:
OutputStream os=response.getOutputStream();
os.write("hello,world".getBytes());
PrintWriter out=response.getWriter();
out.println("abc");
就会报错:
java.lang.IllegalStateException: getOutputStream() has already been called for this response
不能同时使用printWriter和outputstream的原因
Web服务器会自动检查并关闭流
从该图,我们也可以看出. 为什么我们没有主动关闭流,程序也没有问题的原因.
当然:你主动关闭流,更好.
Servlet映射
一.url-pattern中通配符*的使用规则:
(1)同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。
(2)在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。
<servlet-mapping>
<servlet-name>AnyName</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AnyName</servlet-name>
<url-pattern>/action/*</url-pattern>
</servlet-mapping>
一、servlet容器对url的匹配过程:
当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://localhost/test/aaa.html,我的应用上下文是test,容器会将http://localhost/test去掉,剩下的/aaa.html部分拿来做servlet的映射匹配。这个映射匹配过程是有顺序的,而且当有一个servlet匹配成功以后,就不会去理会剩下的servlet了(filter不同,后文会提到)。其匹配规则和顺序如下:
1.精确路径匹配。例子:比如servletA的url-pattern为/test,servletB的url-pattern为 /*,这个时候,如果我访问的url为http://localhost/test,这个时候容器就会先进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。
2.最长路径匹配。例子:servletA的url-pattern为/test/*,而servletB的url-pattern为/test/a/*,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB。
3.扩展匹配,如果url最后一段包含扩展,容器将会根据扩展选择合适的servlet。例子:servletA的url-pattern:*.action
4.如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。如果应用定义了一个defaultservlet,则容器会将请求丢给default servlet(什么是defaultservlet?请见:web.xml文件中缺省映射路径"/"问题以及客户端访问web资源的匹配规则)。
根据这个规则表,就能很清楚的知道servlet的匹配过程,所以定义servlet的时候也要考虑url-pattern的写法,以免出错。
对于filter,不会像servlet那样只匹配一个servlet,因为filter的集合是一个链,所以只会有处理的顺序不同,而不会出现只选择一个filter。Filter的处理顺序和filter-mapping在web.xml中定义的顺序相同。
二、url-pattern详解
在web.xml文件中,以下语法用于定义映射:
l.以”/’开头和以”/*”结尾的是用来做路径映射的。
2.以前缀”*.”开头的是用来做扩展映射的。
3. “/” 是用来定义defaultservlet映射的。
4. 剩下的都是用来定义详细映射的。比如:/aa/bb/cc.action
所以,为什么定义”/*.action”这样一个看起来很正常的匹配在启动tomcat时会报错?因为这个匹配即属于路径映射,也属于扩展映射,导致容器无法判断。
三.示例(*.do的优先级别最低)
对于如下的一些映射关系:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
问题:
当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,哪个servlet响应?
Servlet引擎将调用Servlet1。
当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应?
Servlet引擎将调用Servlet3。
当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应?
Servlet引擎将调用Servlet1。
当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应?
Servlet引擎将调用Servlet2。
当请求URL为“/xxx/yyy/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应?
Servlet引擎将调用Servlet2。
另外,关于url-pattern映射之后,request的servletContextPath , ServletPath , PathInfo 情况,可参照下面链接的文章http://blog.csdn.net/xh16319/article/details/8014193
jsp