servlet 执行顺序

时间:2023-03-09 07:23:17
servlet 执行顺序
public class TestServelt {

    public static void main(String[] args) {
ChildServlet childServlet = new ChildServlet();
childServlet.service();
} } class FatherServlet{ public void service(){
System.out.println("父类service");
doGet(); }
public void doGet(){
System.out.println("父类goGet");
} } class ChildServlet extends FatherServlet{ public void service(){
System.out.println("子类service");
super.service(); }
public void doGet(){
System.out.println("子类goGet");
} }

1、初始化阶段  调用init()方法

2、响应客户请求阶段。调用service()方法,由service()方法根据提交的方式选择执行doGet()或者doPost()方法

3、终止阶段  调用destroy()方法--关闭tomcat的时候

<servlet>
      <servlet-name>myServlet</servlet-name>
      <servlet-class>com.myServlet</servlet-class>
      <load-on-startup>1</load-on-startup>   启动tomcat的时候初始化当前servlet 调用init,数字越小越先加载
</servlet>

<context-param>
           <param-name>context/param</param-name>
           <param-value>avalible during application</param-value>
  </context-param>

参数通过getServletContext().getInitParameter("context/param")获取

<servlet>
    <servlet-name>MainServlet</servlet-name>
    <servlet-class>com.wes.controller.MainServlet</servlet-class>
    <init-param>
       <param-name>param1</param-name>
       <param-value>avalible in servlet init()</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
</servlet>

只能在servlet的init()方法中通过this.getInitParameter("param1")获取

Servlet不是线程安全的。

要解释为什么Servlet为什么不是线程安全的,需要了解Servlet容器(即Tomcat)使如何响应HTTP请求的。

当Tomcat接收到Client的HTTP请求时,Tomcat从线程池中取出一个线程,之后找到该请求对应的Servlet对象并进行初始化,之后调用service()方法。要注意的是每一个Servlet对象再Tomcat容器中只有一个实例对象,即是单例模式。如果多个HTTP请求请求的是同一个Servlet,那么着两个HTTP请求对应的线程将并发调用Servlet的service()方法。

上图中的Thread1和Thread2调用了同一个Servlet1,所以此时如果Servlet1中定义了实例变量或静态变量,那么可能会发生线程安全问题(因为所有的线程都可能使用这些变量)。

比如下面的Servlet中的 name 和 i变量就会引发线程安全问题。

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date; public class ThreadSafeServlet extends HttpServlet { public static String name = "Hello"; //静态变量,可能发生线程安全问题
int i; //实例变量,可能发生线程安全问题
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); @Override
public void init() throws ServletException {
super.init();
System.out.println("Servlet初始化");
} @Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.printf("%s:%s[%s]\n", Thread.currentThread().getName(), i, format.format(new Date()));
i++;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s:%s[%s]\n", Thread.currentThread().getName(), i, format.format(new Date()));
resp.getWriter().println("<html><body><h1>" + i + "</h1></body></html>");
}
} 在Tomcat中启动这个Servlet并在浏览器发起多个HTTP访问,最后会发现变量 i 是多线程共享的。 如果需要更加深入透彻地了解Tomcat接收HTTP的细节,以及与Servlet交互的细节,可以深入看看Tomcat的架构和源码。 jsp 9大内置对象 1.request对象
2.response对象
3.session对象
4.out对象
  1. out对象是JspWriter类的实例,是向客户端输出内容常用的对象

5.page对象

  1. page对象就是指向当前JSP页面本身,有点象类中的this指针,它是
  2. java.lang.Object类的实例
  3. 序号方法说明
  4. classgetClass 返回此Object的类
  5. inthashCode() 返回此Object的hash码
  6. booleanequals(Objectobj) 判断此Object是否与指定的Object对象相等
  7. voidcopy(Objectobj) 把此Object拷贝到指定的Object对象中
  8. Objectclone() 克隆此Object对象
  9. StringtoString() 把此Object对象转换成String类的对象
  10. voidnotify() 唤醒一个等待的线程
  11. voidnotifyAll() 唤醒所有等待的线程
  12. voidwait(inttimeout) 使一个线程处于等待直到timeout结束或被唤醒
  13. voidwait() 使一个线程处于等待直到被唤醒
  14. voidenterMonitor() 对Object加锁
  15. voidexitMonitor() 对Object开锁

6.application对象

application对象实现了用户间数据的共享,可存放全局变量。它开始于服务器

的启动,直到服务器的关闭,在此期间,此对象将一直存在;这样在用户的前

后连接或不同用户之间的连接中,可以对此对象的同一属性进行操作;在任何

地方对此对象属性的操作,都将影响到其他用户对此的访问。服务器的启动和

关闭决定了application对象的生命。它是ServletContext类的实例。

7.exception对象

exception对象是一个例外对象,当一个页面在运行过程中发生了例外,就产

生这个对象。如果一个JSP页面要应用此对象,就必须把isErrorPage设为true,

否则无法编译。他实际上是Java.lang.Throwable的对象

8.pageContext对象

pageContext对象提供了对JSP页面内所有的对象及名字空间的访问,也就是

说他可以访问到本页所在的SESSION,也可以取本页面所在的application的

某一属性值,他相当于页面中所有功能的集大成者,它的本类名也叫

pageContext。

9.config对象

config对象是在一个Servlet初始化时,JSP引擎向它传递信息用的,此信息包括Servlet初始化时所要用到的参数(通过属性名和属性值构成)以及服务器的有关信息(通过传递一个ServletContext对象)

HttpSession

是基于Cookie  实现的

HttpSession的创建时间

jsp中   显示调用时会创建session

servlet  中调用  request.getSession时会创建session


自定义标签

<?xml version="1.0" encoding="UTF-8" ?>

<taglib 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-jsptaglibrary_2_1.xsd"
version="2.1"> <description>testjsp 1.1 core library</description>
<display-name>testjsp core</display-name>
<tlib-version>1.1</tlib-version>
<short-name>testjsp</short-name>
<uri>http://tj.com/testjsp</uri> <tag>
<!-- 描述 HelloSimpleTag的名称-->
<name>hello</name>
<tag-class>com.tag.TestJspFragment</tag-class>
<body-content>scriptless</body-content> <attribute>
        <name>value</name>
        <!-- 是否必须 -->
        <required>true</required>
        <!-- 是否支持EL表达式 -->
        <rtexprvalue>true</rtexprvalue>
     </attribute> <!-- 标签体 -->
<!-- 没有标签体 -->
<!-- <body-content>empty</body-content> -->
<!-- 不经过处理直接把内容交给标签处理器类 -->
<!-- <body-content>tagdependent</body-content> --> <!-- 可以有EL${} 不可以有<%=%> 大部分情况下取值为scriptless-->
<body-content>scriptless</body-content> </tag>
</taglib> public class TestJspFragment extends SimpleTagSupport{
    
    
    @Override
    public void doTag() throws JspException, IOException {
        //当定义标签体的时候会调用setJspBody(JspFragment arg0) {
        //把JspFragment传递过来
    
        JspFragment fragment = getJspBody();
        
        StringWriter sw = new StringWriter();
        //若为NULL输出到页面 等同于fragment.invoke(getJspContext().getOut());
        fragment.invoke(sw);
        //把内容写到sw中
        
        String content = sw.toString().toUpperCase();
        //把内容转换成大写写入到页面
        getJspContext().getOut().print(content);
        
        
    } } //InputStream inputStream = pageContext.getServletContext().getResourceAsStream("src");//根路径 //如果有标签体就会调用此方法,把JspFragment传过来
    public void setJspBody(JspFragment arg0) {
        System.out.println("setJspBody");
    } public void setParent(JspTag arg0) {
        //父标签获取不到子标签,子标签可以获取到父标签的引用
        System.out.println("setParent");
        
        //arg0指的就是父标签的引用,需要强转
        
    }
/*Cookie cookie = new Cookie("name", "aaaa");
//cookie 的作用范围默认为当前文件路径的子目录
cookie.setPath("/");//站点的根目录
cookie.setMaxAge(30);//最大秒数,存在磁盘对应当前浏览器,同一个站点最多存20个cookie,每个cookie最大4k //客户端第一请求时 服务端给客户端返回一个cookie 在请求头中set-cookie
//第二次客户端访问服务端会带着cookie
response.addCookie(cookie);*/ //response.encodeURL("session/a.jsp"); response.encodeUrl("session/a.jsp");//实现URL的重写,在后边加上;jeesessionid=331232131321 //服务端获取客户端cookie /*Cookie []cookies = request.getCookies(); if(cookies!=null){ for (Cookie coo : cookies) {
System.out.println(coo.getName()+":"+coo.getValue());
} }*/
 
//http://localhost:8080/spring1/IndexServlet
String method = request.getServletPath();//IndexServlet try {
Method me = getClass().getDeclaredMethod("add", request.getClass(),response.getClass());
me.invoke(this, request,response);
//cs.getDeclaredMethod("add", null).invoke(this, null);
} catch (Exception e) {
e.printStackTrace();
}

自定义标签函数

之前例子已经写好了,由于时间关系一直没有发布,这次带来的是关于taglib中的<tag>的基本功能已经介绍完毕了,在taglib中我们发现有一个标签叫做<function>,这次简单介绍<function>标签的基本用法,<function>标签可以做什么呢?它可以让我们在jsp用直接调用某个方法,根据自定义的方法返回指定的值,兼容jstl标签,省去了在jsp中直接使用<%!%>来定义方法体再调用的繁琐.如果你用过el语言的话估计会很快上手,其实<function>标签就是一个拥有方法体的el语言.注意:function所定义的方法必须需要是静态的,如果不是静态的话jstl是不能识别所定义的方法.

Java代码如下:

package org.lxh.taglib;

import java.util.List;

public class FunctionTag {

	public static String hello(String name) {

		return name;
} public static Integer bbq(List list) { return list.size();
}
}

方法必须静态,可以定义有返回值或者void类型的方法.

tld:

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>my</short-name>
<uri>http://lauedward.spaces.live.com</uri>
<function>
<!--EL页面调用名字-->
<name>hello</name>
<!--指定标签的处理类,指定了标签由哪个Java类来处理。-->
<function-class>org.lxh.taglib.FunctionTag</function-class>
<!--指定EL页面调用名字中实际调用的方法.指定处理类的实际方法.参数和回调函数要写完整路径-->
<function-signature>java.lang.String hello(java.lang.String)</function-signature> </function> <function>
<name>bbq</name>
<function-class>org.lxh.taglib.FunctionTag</function-class>
<function-signature>java.lang.Integer bbq(java.util.List)</function-signature>
</function>
</taglib>

注意:在<function-signature>需要写完整的类名,假如是String类型的话就必须写java.lang.String这样字,不支持泛型的定义如java.util.List<java.lang.String>eclipse会把<>当作xml的格式来判断的,所以就省略该泛型的定义.

jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="my" uri="/WEB-INF/tld/testFunction.tld"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<%@page import="java.util.*"%><html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<%
    List<String> list = new ArrayList<String>();
    list.add("aa");
    list.add("bb");
    list.add("cc");
    request.setAttribute("list", "helloword");
    request.setAttribute("name", list);
    Map map = new HashMap();
    map.put("1","a");
    map.put("2","b");
    map.put("3","c");
    map.put("4","d");
%>

<br>
${my:hello(list)}
<br>
${my:bbq(name)}
<br>

</body>
</html>

注意:调用方法的时候必须类型要传入相同类型的值,否则的话会报错,不过对于方法体是String的话,是可以传入list,set,map那些,因为传入后会直接调用list的.toString()方法直接当字符串输出.


HttpSessionListener

HttpSessionAttributeListener

ServletContextListener

web.xml 的加载顺序是:context-param -> listener -> filter -> servlet ,而同个类型之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。