Servlet技术使用总结

本科期间很久之前学的了,好久没使用了,感觉知识有一些遗忘了(可能这也是大多数编程人的困绕吧)!所以打算抽时间,在学习新知识的同时也对过往的知识进行一个回顾。整个记录的过程可能不像是开发的过程一样,仅作为一个笔记。想到哪里就写到哪里吧(随着技术的不断迭代有些内容可能有些老了)!随心情了~~~

如何开发一个Servlet

步骤

  • 编写一个java类,继承HttpServlet
  • 重写doGet()和doPost()方法
  • Servlet程序交给服务器运行
    • servlet程序的class码拷贝到WEB-INF/classes目录
    • 在web.xml文件中进行配置(使用Eclipse的时候,可能不存在,这时候就要直接创建一个servlet的程序,eclipse会自动的在内部给你配置好,在类中更改要访问的名字就可以了)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      <!-- 配置一个servlet -->
      <!-- servlet的配置 -->
      <servlet>
      <!-- servlet的内部名称,自定义。尽量有意义 -->
      <servlet-name>FirstServlet</servlet-name>
      <!-- servlet的类全名: 包名+简单类名 -->
      <servlet-class>gz.itcast.a_servlet.FirstServlet</servlet-class>
      </servlet>
      <!-- servlet的映射配置 -->
      <servlet-mapping>
      <!-- servlet的内部名称,一定要和上面的内部名称保持一致!! -->
      <servlet-name>FirstServlet</servlet-name>
      <!-- servlet的映射路径(访问servlet的名称) -->
      <url-pattern>/first</url-pattern>
      </servlet-mapping>

如果是在Eclip se中将会是这样,我们在通过连接访问的时候就直接访问/indexServlet就可以了

1
2
3
4
5
6
/**
* Servlet implementation class indexServlet
*/
@WebServlet("/indexServlet")
public class indexServlet extends HttpServlet {
}

访问的时候 问次URL: http://localhost:8080/项目名/indexServlet
tomcat服务器启动时,首先加载webapps中的每个web应用的web.xml配置文件

  • http:// http协议
  • localhost 到本地的hosts文件中查找是否存在该域名对应的IP地址127.0.0.1 ,如果使用其他服务器的话,就需要将这部分转换成服务器的地址
  • 8080 服务器的端口
  • /项目名 这个是要与tomcat服务器的项目名进行对应的
  • /indexServlet 我们要访问的servlet文件,资源名称

整个的执行过程为:

1)在项目的web.xml中查找是否有匹配的url-pattern的内容(/indexServlet)
2)如果找到匹配的url-pattern,则使用当前servlet-name的名称到web.xml文件中查询是否相同名称的servlet配置
3)如果找到,则取出对应的servlet配置信息中的servlet-class内容:
通过反射:a)构造FirstServlet的对象b)然后调用FirstServlet里面的方法

Servlet的映射路径

1
2
3
4
5
6
<servlet-mapping>
<!-- servlet的内部名称,一定要和上面的内部名称保持一致!! -->
<servlet-name>FirstServlet</servlet-name>
<!-- servlet的映射路径(访问servlet的名称) -->
<url-pattern>/first</url-pattern>
</servlet-mapping>

精确匹配

url-pattern 浏览器输入
/first http://localhost:8080/项目名/first
/itcast/demo1 http://localhost:8080/项目名/itcast/demo1

模糊匹配

url-pattern 浏览器输入
/* http://localhost:8080/项目名/任意路径
/itcast/* http://localhost:8080/项目名/itcast/任意路径
*.后缀名 http://localhost:8080/项目名/任意路径.后缀名

注意:
1)url-pattern要么以 / 开头,要么以开头。 例如, itcast是非法路径。
2)不能同时使用两种模糊匹配,例如 /itcast/
.do是非法路径
3)当有输入的URL有多个servlet同时被匹配的情况下:
3.1 精确匹配优先。(长的最像优先被匹配)
3.2 以后缀名结尾的模糊url-pattern优先级最低!!!

缺省路径

servlet的缺省路径(/)是在tomcat服务器内置的一个路径。该路径对应的是一个DefaultServlet(缺省Servlet)。这个缺省的Servlet的作用是用于解析web应用的静态资源文件。
当我们提供了一个缺省路径的时候,那么服务器又该如何读取文件呢?

  1. 到当前应用下的web.xml文件查找是否有匹配的url-pattern。
  2. 如果没有匹配的url-pattern,则交给tomcat的内置的DefaultServlet处理
  3. DefaultServlet程序到项目应用的根目录下查找是存在一个名称为index.html的静态文件。
  4. 如果找到该文件,则读取该文件内容,返回给浏览器。
  5. 如果找不到该文件,则返回404错误页面
    注意:所以不建议使用/或/,因为Tomcat服务器会先找动态资源再找静态资源。如果是/或/就不会再访问到静态资源,如果是/.html或者/.css等路径格式,那么这类静态资源就不会被访问到了,所以慎用/或/ 和静态资源的后缀*

Servlet的生命周期(重点)

Servlet的生命是由Tomcat控制的!!!!

Servlet的四个生命周期方法

  • 构造方法: 创建servlet对象的时候调用。默认情况下,第一次访问servlet的时候创建servlet对象只调用1次。证明servlet对象在tomcat是单实例的。
  • init方法: 创建完servlet对象的时候紧接着调用。只调用1次。
  • service方法: 每次发出请求时调用。调用n次。
  • destroy方法: 销毁servlet对象的时候调用。停止服务器或者重新部署web应用时销毁servlet对象,只调用1次。

Tomtcat内部代码运行:

1)通过映射找到到servlet-class的内容,字符串: gz.itcast.a_servlet.FirstServlet
2)通过反射构造FirstServlet对象
    2.1 得到字节码对象
                Class clazz = class.forName("gz.itcast.a_servlet.FirstServlet");
    2.2 调用无参数的构造方法来构造对象
                Object obj = clazz.newInstance();     ---1.servlet的构造方法被调用
3)创建ServletConfig对象,通过反射调用init方法
    3.1 得到方法对象
                Method m = clazz.getDeclareMethod("init",ServletConfig.class);
    3.2 调用方法
                m.invoke(obj,config);             --2.servlet的init方法被调用
4)创建request,response对象,通过反射调用service方法
    4.1 得到方法对象
                Methodm m =clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class);
    4.2 调用方法
                m.invoke(obj,request,response);  --3.servlet的service方法被调用
5)当tomcat服务器停止或web应用重新部署,通过反射调用destroy方法
    5.1 得到方法对象
                Method m = clazz.getDeclareMethod("destroy",null);
    5.2 调用方法
                m.invoke(obj,null);            --4.servlet的destroy方法被调用

Servlet的自动加载

默认情况下,第一个访问Servlet的时候创建Servlet对象,如果Servlet在进行init()的时候或者构造方法的时候,执行了太多的逻辑代码,那么会导致用户在第一次请求的时候,会加载比较慢!
(‘我们可以通过改变一下访问对象的时机,将对象的创建的时机更改到加载Web应用的时候’)
这个配置是在Servlet的配置文件中进行配置的!

1
2
3
4
5
6
<servlet>
<servlet-name>LifeDemo</servlet-name>
<servlet-class>gz.itcast.c_life.LifeDemo</servlet-class>
<!-- 让servlet对象自动加载 -->
<load-on-startup>1</load-on-startup> 注意: 整数值越大,创建优先级越低!!
</servlet>

有参数的init方法和无参数的init方法

有参数的init方法是servlet的生命周期方法,一定会被tomcat服务器调用
注意:如果要编写初始代码,不需要覆盖有参数的init方法(默认调用无参的init方法)
无参数的init方法是servlet的编写初始化代码的方法。是Sun公司设计出来专门给开发者进行覆盖,然后在里面编写servlet的初始逻辑代码的方法。

Servlet的多线程并发问题

如果多个线程同时访问了Servlet的共享对象(成员对象)则会引发线程安全问题。Servlet是单实例,多线程的。

解决方案

  1. 将共享数据进行加锁 ,是用synchronized 或者锁对象
  2. 在使用过程中,尽量不要使用成员变量,或者共享资源

    ServletConfig对象

    ServletConfig对象: 主要是用于加载servlet的初始化参数。在一个web应用可以存在多个ServletConfig对象(一个Servlet对应一个ServletConfig对象)
    操作方式:
    可以通过调用init()方法进行获取,直接从有参数的init方法中得到!!
    Servlet的参数只能由当前的Servlet获取!!
    下面是我在doGet()方法中获取的该对象!!
    1
    2
    Enumeration<String> enum3 =   getServletConfig().getInitParameterNames();
    System.out.println(enum3.hasMoreElements());

ServletContext对象

ServletContext对象 ,叫做Servlet的上下文对象。表示一个当前的web应用环境。一个web应用中只有一个ServletContext对象。本身也是一个全局的域对象!
ServletContext对象的获取方式,目前我使用的主要有以下三种方式:

1
2
3
4
5
ServletContext context = getServletConfig().getServletContext();
ServletContext context1 = getServletContext();
ServletContext context2 = request.getServletContext();
System.out.println(context.equals(context1)); //true
System.out.println(context.equals(context2)); //true

ServletContext对象的核心API(作用)

java.lang.String getContextPath()   --得到当前web应用的路径(用在请求重定向的路径中)

java.lang.String getInitParameter(java.lang.String name)  --得到web应用的初始化参数
java.util.Enumeration getInitParameterNames()  

void setAttribute(java.lang.String name, java.lang.Object object) --域对象有关的方法
java.lang.Object getAttribute(java.lang.String name)  
void removeAttribute(java.lang.String name)  

RequestDispatcher getRequestDispatcher(java.lang.String path)   --转发(类似于重定向)

java.lang.String getRealPath(java.lang.String path)     --得到web应用的资源文件
java.io.InputStream getResourceAsStream(java.lang.String path)  

域对象

作用是用于保存数据,获取数据。可以在不同的动态资源之间共享数据!!
数据的传递方式:

  • 通过传递参数的形式,共享数据。局限:只能传递字符串类型
  • 使用域对象 可以使用域对象共享数据,好处:可以共享任何类型的数据!!!!!
    • 保存数据:void setAttribute(java.lang.String name, java.lang.Object object)
    • 获取数据: java.lang.Object getAttribute(java.lang.String name)
      • 删除数据: void removeAttribute(java.lang.String name)

转发与重定向

转发

代码使用:

1
request.getRequestDispatcher("/pingjiao.jsp").forward(request, response);

a)地址栏不会改变
b)转发只能转发到当前web应用内的资源
c)可以在转发过程中,可以把数据保存到request域对象中

重定向

代码使用

1
response.sendRedirect(request.getContextPath()+"/adminLogin.jsp");

a)地址栏会改变,变成重定向到地址。
b)重定向可以跳转到当前web应用,或其他web应用,甚至是外部域名网站。
c)不能再重定向的过程,把数据保存到request中。

结论: 如果要使用request域对象进行数据共享,只能用转发技术!!!