java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaEE, JavaME, JavaSE)的总称。本站提供基于Java框架struts,spring,hibernate等的桌面应用、web交互及移动终端的开发技巧与资料

保持永久学习的心态,将成就一个优秀的你,来 继续搞起java知识。

一 空Servlet类模板

import java.io.IOException;mport java.io.PrintWriter;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

public class ServletDemo1 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

}

}

二 web.xml配置

ServletDemo1

xxx.yyy.zzz.ServletDemo1

ServletDemo1

/servlet/ServletDemo1

ServletDemo1

/*

/abc/*

/*

/abc

*.do

invoker

org.apache.catalina.servlets.InvokerServlet

1

如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。

凡是在web.xml文件中找不到匹配的元素的URL,它们的访问请求都将交给缺省Servlet处理,

ServletDemo2

xxx.yyy.zzz.ServletDemo2

1

ServletDemo2

/

\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为了缺省Servlet。

三 Servlet的线程安全问题

  当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。不存在线程安全问题的代码:

public class ServletDemo3 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

/**

* i变量被多个线程并发访问,但是没有线程安全问题,因为i是doGet方法里面的局部变量,

* 当有多个线程并发访问doGet方法时,每一个线程里面都有自己的i变量,

* 各个线程操作的都是自己的i变量,所以不存在线程安全问题

* 多线程并发访问某一个方法的时候,如果在方法内部定义了一些资源(变量,集合等)

* 那么每一个线程都有这些东西,所以就不存在线程安全问题了

*/

int i=1;

i++;

response.getWriter().write(i);

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

存在线程安全问题的代码:

public class ServletDemo3 extends HttpServlet {

int i=1;

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

i++;

try {

Thread.sleep(1000*4);

} catch (InterruptedException e) {

e.printStackTrace();

}

response.getWriter().write(i+"");

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

把i定义成全局变量,当多个线程并发访问变量i时,就会存在线程安全问题了;同时开启两个浏览器模拟并发访问同一个Servlet,本来正常来说,第一个浏览器应该看到2,而第二个浏览器应该看到3的,结果两个浏览器都看到了3。

如何解决?

public class ServletDemo3 extends HttpServlet {

int i=1;

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

/**

* 加了synchronized后,并发访问i时就不存在线程安全问题了,

* 假如现在有一个线程访问Servlet对象,那么它就先拿到了Servlet对象的那把锁

* 等到它执行完之后才会把锁还给Servlet对象,由于是它先拿到了Servlet对象的那把锁,

* 所以当有别的线程来访问这个Servlet对象时,由于锁已经被之前的线程拿走了,后面的线程只能排队等候了

*

*/

synchronized (this) {//在java中,每一个对象都有一把锁,这里的this指的就是Servlet对象

i++;

try {

Thread.sleep(1000*4);

} catch (InterruptedException e) {

e.printStackTrace();

}

response.getWriter().write(i+"");

}

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

现在这种做法是给Servlet对象加了一把锁,保证任何时候都只有一个线程在访问该Servlet对象里面的资源,这样就不存在线程安全问题了

这种做法虽然解决了线程安全问题,但是编写Servlet却不能用这种方式处理线程安全问题,假如有9999个人同时访问这个Servlet,那么这9999个人必须按先后顺序排队轮流访问。

针对Servlet的线程安全问题,Sun公司提供有解决方案的:让Servlet去实现一个SingleThreadModel接口,如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。

查看Sevlet的API可以看到,SingleThreadModel接口中没有定义任何方法和常量,在Java中,把没有定义任何方法和常量的接口称之为标记接口,经常看到的一个最典型的标记接口就是"Serializable",这个接口也是没有定义任何方法和常量的,标记接口在Java中有什么用呢?主要作用就是给某个对象打上一个标志,告诉JVM,这个对象可以做什么,比如实现了"Serializable"接口的类的对象就可以被序列化,还有一个"Cloneable"接口,这个也是一个标记接口,在默认情况下,Java中的对象是不允许被克隆的,就像现实生活中的人一样,不允许克隆,但是只要实现了"Cloneable"接口,那么对象就可以被克隆了。

让Servlet实现了SingleThreadModel接口,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。

对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。

实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。

四 常用功能代码

1 hello world

public void init() throws ServletException{

message = "Hello World";

}

public void doGet(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException

{

response.setContentType("text/html");

PrintWriter out = response.getWriter();

out.println("

" + message + "

");

}

2 在客户端输出一个html文档

response.setContentType("text/html");PrintWriter out = response.getWriter();

out.println("");

out.println("");

out.println(" A Servlet");

out.println(" ");

out.print(" This is ");

out.print(this.getClass());

out.println(", using the GET method");

out.println(" ");

out.println("");

out.flush();

out.close();

3 处理用户登陆的servlet实现方法

Login.javapackage com.bai;

import javax.servlet.http.*;

import java.io.*;

public class Login extends HttpServlet{

public void doGet(HttpServletRequest req,HttpServletResponse res){

try{req.setCharacterEncoding("gb2312");

res.setContentType("text/html;charset=gb2312");

PrintWriter pw=res.getWriter();

pw.println("");

pw.println("");

pw.println("

登陆界面

");

pw.println("

");

pw.println("用户名:
");

pw.println("密码:
");

pw.println("
");

pw.println("

");

pw.println("");

pw.println("");

}

catch(Exception e){

e.printStackTrace();

}

}

public void doPost(HttpServletRequest req,HttpServletResponse res){

this.doGet(req,res);

}

}

LoginCl.java

package com.bai;

import javax.servlet.http.*;

import java.io.*;

import java.sql.*;

public class LoginCl extends HttpServlet{

public void doGet(HttpServletRequest req,HttpServletResponse res){

Connection conn=null;

Statement stmt=null;

ResultSet rs=null;

String sql = "select username,passwd from users where username = ? and passwd = ?";

try{//req.setCharacterEncoding("gb2312");

String user=req.getParameter("username");

String password=req.getParameter("passwd");

Class.forName("com.mysql.jdbc.Driver");

conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/sqdb","root","root");

// stmt=conn.createStatement();

PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setString(1, user);

pstmt.setString(2, password);

rs = pstmt.executeQuery();

// rs=stmt.executeQuery("select top 1 * from users where username='"+user

// +"' and passwd='"+password+"'");

if(rs.next())

{

HttpSession hs=req.getSession(true);

hs.setMaxInactiveInterval(60);

hs.setAttribute("name",user);

res.sendRedirect("welcome?&uname="+user+"&upass="+password);

}

else{

res.sendRedirect("login"); //url

}

}

catch(Exception e){

e.printStackTrace();

}finally{

try{

if(rs!=null){

rs.close();

}

if(stmt!=null){

stmt.close();

}

if(conn!=null){

conn.close();

}

}catch(Exception e){

e.printStackTrace();

}

}

}

public void doPost(HttpServletRequest req,HttpServletResponse res){

this.doGet(req,res);

}

}

注:

上面这个处理用户名密码带有明显注入漏洞,可以根据用户名从数据库取密码,用取出的密码和用户输入的密码比较

sql=select passwd from users where username = ? limit 1

if(rs.next())

{

String passwd=rs.getString(1);

if(passwd.equals(password))

//密码正确

else //密码错误

}

Welcome.java

package com.bai;

import javax.servlet.http.*;

import java.io.*;

public class Welcome extends HttpServlet{

public void doGet(HttpServletRequest req,HttpServletResponse res){

HttpSession hs=req.getSession();

String val=(String)hs.getAttribute("pass");

if(val==null){

try{

System.out.print(1);

res.sendRedirect("login");

}catch(Exception e){

e.printStackTrace();

}

}

String u=req.getParameter("uname");

String p=req.getParameter("upass");

try{//req.setCharacterEncoding("gb2312");

PrintWriter pw=res.getWriter();

pw.println("welcome! "+u+"&pass="+p);

}

catch(Exception e){

e.printStackTrace();

}

}

public void doPost(HttpServletRequest req,HttpServletResponse res){

this.doGet(req,res);

}

}

4 servlet中session

在servlet中,session是封装在javax.servlet.http.HttpSession这个接口中的,这个接口是构建在cookie或者URL重写的基础上,要得到一个HttpSession的实例,就可以通过HttpServletRequest的getSession()方法来获得HttpServletRequest有两个重载的getSession()方法,一个接受一个boolean的类型的值,另一个不带任何参数,getSession()方法和getSession(true)方法功能一样,就是如果对应的客户端已经产生过一个session,那么就会返回这个旧的session,否则,这个方法将会产生一个session ID并且和对应的客户端绑定在一起,而如果getSession(false)表示如果对应的客户端已经有对应的session,那么返回这个旧的session,否则不会产生新的session。可以使用HttpSession对象上的isNow()方法来判定这个session是否为新建的

HttpSession常用方法

public void setAttribute(String name,Object value)

将value对象以name名称绑定到会话

public object getAttribute(String name)

取得name的属性值,如果属性不存在则返回null

public void removeAttribute(String name)

从会话中删除name属性,如果不存在不会执行,也不会抛处错误.

public Enumeration getAttributeNames()

返回和会话有关的枚举值

public void invalidate()

使会话失效,同时删除属性对象

public Boolean isNew()

用于检测当前客户是否为新的会话

public long getCreationTime()

返回会话创建时间

public long getLastAccessedTime()

返回在会话时间内web容器接收到客户最后发出的请求的时间

public int getMaxInactiveInterval()

返回在会话期间内客户请求的最长时间为秒

public void setMaxInactiveInterval(int seconds)

允许客户客户请求的最长时间

ServletContext getServletContext()

返回当前会话的上下文环境,ServletContext对象可以使Servlet与web容器进行通信

public String getId()

返回会话期间的识别号

一个保存信息到session的例子

sessionlogin.html

用户名:

密码: