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 时序图帮助大家理解



面试题: 请简述servlet的生命周期(工作流程)
答: 标准版本:
①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.
精确路径匹配。例子:比如servletAurl-pattern/testservletBurl-pattern /*,这个时候,如果我访问的urlhttp://localhost/test,这个时候容器就会先进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。

  2.最长路径匹配。例子:servletAurl-pattern/test/*,而servletBurl-pattern/test/a/*,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB 

  3.扩展匹配,如果url最后一段包含扩展,容器将会根据扩展选择合适的servlet。例子:servletAurl-pattern*.action 

  4.如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。如果应用定义了一个defaultservlet,则容器会将请求丢给default servlet(什么是defaultservlet?请见:web.xml文件中缺省映射路径"/"问题以及客户端访问web资源的匹配规则)。 

    根据这个规则表,就能很清楚的知道servlet的匹配过程,所以定义servlet的时候也要考虑url-pattern的写法,以免出错。 

    对于filter,不会像servlet那样只匹配一个servlet,因为filter的集合是一个链,所以只会有处理的顺序不同,而不会出现只选择一个filterFilter的处理顺序和filter-mappingweb.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映射之后,requestservletContextPath , ServletPath , PathInfo 情况,可参照下面链接的文章http://blog.csdn.net/xh16319/article/details/8014193


jsp



1 0
原创粉丝点击