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对象
- out对象是JspWriter类的实例,是向客户端输出内容常用的对象
5.page对象
- page对象就是指向当前JSP页面本身,有点象类中的this指针,它是
- java.lang.Object类的实例
- 序号方法说明
- classgetClass 返回此Object的类
- inthashCode() 返回此Object的hash码
- booleanequals(Objectobj) 判断此Object是否与指定的Object对象相等
- voidcopy(Objectobj) 把此Object拷贝到指定的Object对象中
- Objectclone() 克隆此Object对象
- StringtoString() 把此Object对象转换成String类的对象
- voidnotify() 唤醒一个等待的线程
- voidnotifyAll() 唤醒所有等待的线程
- voidwait(inttimeout) 使一个线程处于等待直到timeout结束或被唤醒
- voidwait() 使一个线程处于等待直到被唤醒
- voidenterMonitor() 对Object加锁
- 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 的顺序进行调用的。