Servlet

一.Servlet概述
编码格式的设置:
Request: 请求编码格式
doget()方法中:不需要设置
dopost()方法中:request.setCharactorEncoding(“utf-8”)
response 响应编码格式(以字符流的形式响应正文时都要设置)
response.setContentType(“text/html;charset=utf-8”)

1.用用再说!
我们先不要去管Servlet是个什么东西,先用用再说!在使用之后,你就会对Servlet有一个简单的了解。
使用IDEA创建hello项目;
在src下创建com.kgc.AServlet类;
让AServlet实现javax.servlet.Servlet接口,这个接口中一共5个方法,而我们只需要去实现service()方法;
打开web.xml文件,部署AServlet;
发布hello项目;
打开浏览器访问http://localhost/hello/AServlet。
Servlet的原始创建方式

public class AServlet implements Servlet {
public void init(ServletConfig config) throws ServletException {}
public ServletConfig getServletConfig() {return null;}

public void service(ServletRequest req, ServletResponse res)
		throws ServletException, IOException {
	res.getWriter().print("Hello JavaWeb!");
}

public String getServletInfo() {return null;}
public void destroy() {}

}

1.JavaWeb三大组件
Servlet是JavaWeb三大组件之一,它是我们学习JavaWeb最为基本的组件,也就是说你一定要100%的掌握它。
其它两种:Filter(拦截器)、Listener(观察者模式)

2.Servlet的作用
Servlet,即Server Let的意思,用来处理用户请求。当客户端发出请求后,由Tomcat去找到可以处理这一请求的Servlet来处理。
也就是说,用户的请求是由Servlet来处理的!例如用户发出注册请求,那么就应该有处理注册的Servlet来处理;用户发出登录请求,那么就应该有登录Servlet来处理。
也就是说,Servlet是由我们自己来完成的!但Servlet一定要实现javax.servlet.Servlet接口,并且还要在web.xml文件中部署!不然Tomcat是找不到我们写的Servlet的。

one
com.kgc.web.AServlet


one
/AServlet

当我们写好了com.kgc.web.AServlet类后,还需要在web.xml文件中添加上面的内容。这就是把AServlet部署到项目中去了!

3.JavaWeb请求响应原理
当Tomcat接收到请求(http://localhost:8080/hello/AServlet)后,Tomcat会找到hello项目中的web.xml文件,然后通过AServlet这个请求路径,查找处理这个请求的Servlet类型。这刚好与/AServlet匹配,这说明存在一个可以通过这个请求的Servlet。然后再通过/AServlet查找到one,然后再通过one查找到 com.kgc.web.AServlet。这时Tomcat已经得到了一个Servlet类名字(一个字符串而已)。
Tomcat通过Servlet类名字去查找Servlet缓存池中是否已经存在了这个Servlet对象,如果存在,那么就不用再去创建,直接获取这个Servlet实例,调用它的service()方法完成请求!
如果这个Servlet不存在,那么Tomcat会通过反射来创建Servlet实例,并把Servlet实例存放到Servlet池中,再去调用Servlet的service方法处理请求。
画图分析:从浏览器的请求到服务器调用service方法

4.Servlet接口
javax.servlet.Servlet接口中方法如下:
void init(ServletConfig servletConfig):当Tomcat创建Servlet实例后,马上调用init()方法。这个方法只在创建后调用一次!用来做Servlet初始化工作!一个Servlet实例只被创建一次,所以init()方法也只被调用一次!(本方法编写对Servlet的初始化代码)
void service(ServletRequest request, ServletResponse response):Servlet实例在每次处理请求时都调用service()方法。
void destroy():当Tomcat要销毁Servlet实例时,会先调用destroy()方法,再销毁它。所谓销毁Servlet,其实就是在Servlet缓存池中把Servlet移除!一般只有Tomcat关闭时,才会销毁Servlet实例!
ServletConfig getServletConfig():这个方法返回ServletConfig对象,但我们不能自己去创建ServletConfig对象,所以一般我们会在init()方法中把init()方法的参数保存起来,然后再在本方法中返回它。ServletConfig对象对应web.xml中当前Servlet实例的配置信息。
String getServletInfo():这个方法只是返回一个字符串,用来说明当前Servlet。基本没用!

5.Servlet生命周期
javax.servlet.Servlet接口中,有三个方法说明了Servlet的生命周期:
void init(ServletConfig):创建后马上调用init()完成初始化;
void service(ServletRequest,ServletResponse):每次处理请求时调用service()方法;
void destroy():当Tomcat要销毁Servlet实例时,先调用destroy()方法。

现在你应该已经清楚了,Servlet的实例不由我们创建,Servlet的方法不由我们来调用,这一切都是由Tomcat来完成!!!这就是说由Tomcat来管理Servlet,而我们只需要去编写Servlet实现类,并将其部署到web.xml文件中去!
再次提醒,只有这三个方法是生命周期中的方法。也就是说,生命周期方法会被Tomcat在不同的时间点来调用!而其它方法就不会被调用了!!!如果你在自己写的Servlet中添加了其他方法,那么Tomcat也是不会去调用它们的!但你可以让生命周期方法去调用你自己写的方法就OK了!

6.请求对象和响应对象的基本使用(不是今天的重点)
在Tomcat调用Servlet的service()方法时,需要给service()方法传递参数,这两个参数一个是ServletRequest,另一个是ServletResponse。其实Tomcat传递的是HttpServletRequest和HttpServletResponse对象。因为我们的请求都是基于HTTP协议的!
HttpServletRequest是ServletRequest的子接口;
HttpServletResponse是HttpServletResponse的子接口。
Tomcat会把所有与请求相关的数据封装到HttpServletRequest对象中。例如请求的是POST还是GET,所有请求头信息,以及请求参数,这些数据都封装到了HttpServletRequest对象中!!!当我们需要使用这些信息时,可以去调用HttpServletRequest的相关方法来获取。
HttpServletRequest方法:
String getParameter(String paramName):获取指定请求参数的值;
String getMethod():获取请求方法,例如GET或POST;
String getHeader(String name):获取指定请求头的值;
void setCharacterEncoding(String encoding):设置请求参数编码!只对POST请求中的参数有效。当调用request.setCharacterEncoding(“utf-8”)之后,再通过getParameter()方法获取参数值时,那么参数值都已经通过了转码,即转换成了UTF-8编码。所以,这个方法必须在调用getParameter()方法之前调用!

Tomcat会传递给service()方法一个HttpServletResponse对象,这个对象我们称之为响应对象。它可以完成与响应相关的工作。例如向客户端发送响应状态码,向客户端发送响应头信息,向客户端发送响应正文等等!反正只要是与响应相关的方法,你就去HttpServletResponse对象中去查找。
void setContentType(String contentType):设置响应内部的类型,所谓响应类型,就是给客户端浏览器用来识别响应内部的类型,如果响应类型是text/html,那么浏览器就会显示它,如果响应类型为appliction/avi,那么浏览器就会弹出下载对象框。所以,响应类型是不能写错的。例如:response.setContentType(“text/html;charaset=utf-8”),这是告诉构造器,响应内部是html代码,使用的编码是UTF-8。
void addHeader(String name, String value):向客户端添加响应头信息;
void sendError(int code, String errorMsg):向客户端发送状态码,以及错误消息。例如给客户端发送404:response(404, “您要查找的资源不存在!”)。
PrintWriter getWriter():获取response的输出流,使用该流可以向客户端响应正文内容。例如response.getWriter().print(“

Hello JavaWeb!

”);

7.ServletConfig
ServletConfig对象对应web.xml文件中的元素。例如你想获取当前Servlet在web.xml文件中的配置名,那么可以使用servletConfig.getServletName()方法获取!

你不能自己去创建ServletConfig对象,Servlet的init()方法的参数就是ServletConfig类型的。Tomcat在调用init()方法时,会传递ServletConfig对象。你可以在init()方法中使用它!
在元素中还可以配置初始化参数:

BServlet

com.kgc.web.BServlet


paramName1
paramValue1


paramName2
paramValue2

添加了两个初始化参数,第一个参数的名称为paramName1,第一个参数的值为paramValue1;第二个参数的名称为paramName2,第二个参数值为paramValue2。
在元素中可以加载多个,每个表示一个参数。下有两个子元素:和,其中表示参数的名称,而元素参数的值。
注意,是添加到元素中,而不是中。
使用ServletConfig对象的getInitParameter(String paramName)方法可以获取指定参数名称的参数值。getInitParameterNames()方法返回所有参数的名字,返回值类型为Enumeration。

二.Servlet 补充

1.Servlet的限制
Servlet必须要有一个公有无参构造器,因为Tomcat会调用这个构造器来创建Servlet对象。
反射
2.第一次惩罚
前面已经说过了Servlet的生命周期。
当第一次访问一个Servlet时,Tomcat会创建Servlet实例,然后把Servlet实例存放到Servlet缓存池中,在第二次访问Servlet时,就不会再次创建Servlet了。这说明第一次需要创建,第一次就比较慢!!!这种第一次比较慢的情况被称之为“第一次惩罚”。

3.Tomcat启动时创建Servlet
有些Servlet需要在Tomcat启动时就被创建,而不是第一次访问时被创建,那么可以在web.xml文件中配置元素。
在元素中添加子元素元素!
这个元素的值必须是一个大于等于0的整数!!!

One
com.kgc.web.AServlet
0


Two
com.kgc.web.BServlet
1

所有添加了子元素的Servle,都会在Tomcat启动时被创建!当然,只是被创建,但没有处理请求!但我们知道在Servlet生命周期中init()方法会在创建后被调用,所以你可以在init()方法中做一些输出,查看是否在Tomcat启动时调用了它。
元素的值是一个序号,Tomcat会使用这个序号给多个Servlet排序!然后在Tomcat启动时会按这个顺序来创建Servlet实例对象!

4.单例的Servlet
因为Servlet实例是由Tomcat来创建的,但Tomcat只会创建一个Servlet实例,所以Servlet就是单例的!这与我们自己写的单例模式不太一样。因为这种单例是通过容器来管理而实现的!
(不是只能创建一个servlet,而是一个Servlet类在内存中只能有一个对象)
一个实例需要在同一个时间点上处理多个请求!
同步就是安全,但效率太低!–加一个线程锁
Servlet是线程不安全的!处理办法:
不写属性;
不写可以存储数据的属性!

三.通用Servlet 父类
–servlet创建的第二种方式
1.java.awt.MouseListener
在java.awt包中,有一个鼠标监听器,下面我们来使用一下鼠标监听器。
public class AWTTest {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 200);
f.addMouseListener(new MyMouseListener());
f.setVisible(true);
}
}

class MyMouseListener implements MouseListener {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println(“点击了”);
}

@Override
public void mousePressed(MouseEvent e) {
	System.out.println("压下了");
}

@Override
public void mouseReleased(MouseEvent e) {
	System.out.println("释放了");
}

@Override
public void mouseEntered(MouseEvent e) {
	System.out.println("进入了");
}

@Override
public void mouseExited(MouseEvent e) {
	System.out.println("退出了");
}

}

假如现在我们需要对鼠标点击做出响应,而其他事件不关心时,那么我们的监听器类也要给出其他几个实现,只是空实现而已。
class MyMouseListener implements MouseListener {
public void mouseClicked(MouseEvent e) {
System.out.println(“点击了”);
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}

是否可以不给出这些空实现呢?看来是不行,因为实现一个接口,必须把所有方法都实现了才行啊。
但我们可以自己写一个完全空现实的类,让子类去继承。这样已后所有的类都去继承我们自己写的空实现类,不用再去实现MouseListener。子类具有父类所有方法,所以子类只需要去覆盖想去覆盖的方法,而其它方法就不用写了。
class MyMouseListener extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
System.out.println(“点击了”);
}
}
class MouseAdapter implements MouseListener {
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}

你可能会说,这不是更麻烦!多写了一个类!!!
没错,但这个MouseAdapter只需要写一次,以后就不用再写了。这么算来还是方便了很多!

2.为Servlet编写通用父类
你也知道,Servlet接口中有5个方法,平时我们只对其中一部分方法感兴趣,但还是要把所有方法都覆盖了,那么我们不如写一个通用父类,从此以后,所有人都去继承这个通用父类,而不是实现Servlet接口。
public class MyServlet implements Servlet {
public void init(ServletConfig config) throws ServletException {}
public ServletConfig getServletConfig() {return null;}

public void service(ServletRequest req, ServletResponse res)
		throws ServletException, IOException {
}

public String getServletInfo() {return null;}
public void destroy() {}

}
public class OneServlet extends MyServlet {
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
res.getWriter().print(“Hello JavaWeb!”);
}
}

其实在Servlet方法中,init()和getServletConfig()方法我们可以完成了。因为我相信我的实现是所有人都想要的实现。如果我不能肯定这一点,那么我也不敢去实现这两个方法。
public class MyServlet implements Servlet {
private ServletConfig config;
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() {}
public ServletConfig getServletConfig() {
return config;
}

public void service(ServletRequest req, ServletResponse res)
		throws ServletException, IOException {
}
public String getServletInfo() {return null;}
public void destroy() {}

}

多么完美的MyServlet类啊。在init(ServletConfig)方法中把Tomcat传递过来的ServletConfig对象保存了起来(赋值给属性)。在getServletConfig()方法中返回它!!!
如果子类想添加初始化工作,去覆盖init(ServletConfig)方法后,那么this.config=config这句就不会被执行了,所以建议子类不要去覆盖init(ServletConfig)方法,而应该去覆盖init()方法。因为init(ServletConfig)方法中会调用init()方法。

当调用子类对象OneServlet的init(ServletConfig)方法会执行什么内容?
当调用getServletConfig()方法返回的是什么?

3.GenericServlet
javax.servlet.GenericServlet与我们自己写的MyServlet类很相似,你现在使用它应该已经没有问题了吧!

四.专注HTTP请求的Servlet

1.写一个专门处理HTTP请求的Servlet
因为现在我们的请求都是基于HTTP协议的,所以我们应该专门为HTTP请求写一个Servlet做为通用父类。
对于专注于HTTP的Servlet,我们需要处理以下几个问题:
service()方法的参数ServletRequest和ServletResponse,但因为所有的请求都是HTTP请求,所以传递给service()就去的参数其实是HttpServletRequest和HttpServletResponse对象。如果子类希望使用的是HttpServletRequest,而不是ServletRequest,那么它需要自己去做强转。我们的MyHttpServlet这个通用父类可以把强转的工作完成!
public class MyHttpServlet extends GenericServlet {
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest httpReq = (HttpServletRequest)req;
HttpServletResponse httpRes = (HttpServletResponse)res;
service(httpReq, httpRes);
}
// 子类应该去覆盖这个方法,而不是上面的方法
// 这个方法的参数是HTTP的
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}

在MyHttpServlet类中的service(ServletRequest,ServletResponse)方法调用了本类中自己写的方法service(HttpServletRequest,HttpServletResponse)。子类应该去覆盖后者!

2.doGet()和doPost()
我们知道,在HTTP中请求的方法最为常见的就是GET和POST。往往我们需要在service()方法中去判断客户端的请求方法,然后为GET写一种处理请求的方案,再为POST写一种处理请求的方法。这说明我们需要在service()方法中写if语句来做判断!为什么我们的MyHttpServlet不把这个工作完成了呢?
public class MyHttpServlet extends GenericServlet {
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest httpReq = (HttpServletRequest)req;
HttpServletResponse httpRes = (HttpServletResponse)res;
service(httpReq, httpRes);
}
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String method = request.getMethod();
if(method.equals(“GET”)) {
doGet(request, response);
} else if(method.equals(“POST”)) {
doPost(request, response);
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

}
public void doPost(HttpServletRequest request, HttpServletResponse response) 
		throws ServletException, IOException {
	
}

}

3.子类不能覆盖这个类的service()方法
子类不能去覆盖MyHttpServlet类的service()方法!如果覆盖了这个方法,那么它就不能去调用service(HttpServletRequest,HttpServletResponse)方法了,那么也就不会通过请求方式去调用doGet()或doPost()方法了。
注意,现在已经有两个service()方法了,这两个service()方法的关系一定要搞清楚!!!
doGet()和doPost()与service()方法的关系是怎样的也一定要搞清楚!

4.HttpServlet
没错,Java中已经存在了javax.servlet.http.HttpServlet类。它与我们自己写的MyHttpServlet类很相似。打开源代码看看这个类!它比我们自己的MyHttpServlet复杂一点,但最为常用的特性都已经写在MyHttpServlet中了,如果你会用了MyHttpServlet,那么HttpServlet你也就会用了。
我们以后写的Servlet基本都是从HttpServlet开始派生!

5.HTTP请求方法
HTTP请求方法不只是GET和POST,还有其他的方法,但基本上用不上。这里只是简单介绍一下。HTTP请求除了GET和POST之外还有别的:
GET通过请求URI得到资源
POST用于添加新的内容
PUT用于修改某个内容
DELETE,删除某个内容
CONNECT用于代理进行传输,如使用SSL
OPTIONS询问可以执行哪些方法
PATCH部分文档更改
RACE用于远程诊断服务器
HEAD类似于GET, 但是不返回body信息,用于检查对象是否存在,以及得到对象的元数据
TRACE用于远程诊断服务器

6.HttpServlet中doXXX()的默认实现
在HttpServlet中提供了7种不同的请求方式处理的方法:
doGet():当请求为GET时调用这个方法;
doPost():当请求为POST时调用这个方法;
doPut():当请求为PUT时调用这个方法;
doDelete():当请求为DELETE时调用这个方法;
doOptions():当请求为OPTIONS时调用这个方法;
doTrace():当请求为TRACE时调用这个方法;
doHead():当请求为HEAD时调用这个方法。

7.HTTP状态码
HTTP的状态码我们也需要了解一下!
我们现在已经知道的状态码有:200、206、404、416、500。
下面深入了解一下HTTP状态码:
1XX:信息类(Information),表示收到Web浏览器请求,正在进一步的处理中;
2XX:成功类(Successful),表示用户请求被正确接收,理解和处理例如:200 OK;
3XX:重定向类(Redirection),表示请求没有成功,客户必须采取进一步的动作;
4XX:客户端错误(Client Error),表示客户端提交的请求有错误。 例如:404 Not Found,意味着请求中所引用的文档不存在;
5XX:服务器错误(Server Error)表示服务器不能完成对请求的处理:如 500。

五.注册功能

1.功能说明
没有人不知道什么叫注册功能吧!
我们这里的注册功能很简单,给用户提供一个页面,在页面中给出一个注册表单,用户输入自己的注册信息(用户名和密码)。然后提交表单,把请求发送到Servlet,Servlet会把用户的注册信息保存到数据库中。完毕!
我们也不打算让Servlet做太多的事情,所以真正保存信息的工作还是交给DAO来做吧!所以在Servlet中只是获取DAO对象,然后调用DAO的方法来完成保存信息!

DAO – Data Access Object(数据的存取工作,由这个对象来完成!)
User :这是我们注册功能的唯一数据类!
不能用业务数据做主键(唯一标识!)
username:用户名是唯一的!
password
email:唯一的!

2.项目分析
用户通过注册页面的表单向Servlet发送请求,Servlet获取DAO对象,然后调用DAO对象的方法save()方法完成向数据库中保存注册信息。
reigst.jsp:包含注册表单的页面,提交表单会把请求交给RegistServlet来处理;
RegistServlet:注册Servlet,包含doPost()方法。获取DAO对象,然后调用DAO对象的save()方法传递User对象(注册信息领域对象)过去,由DAO完成保存注册信息;
RegistDao:只有一个save(User)方法,会把User的属性值保存到数据库;
RegistDaoImpl:为了更好的处理耦合的问题,我们把RegistDao定义为接口,而RegistDaoImpl为其实现类;
User:领域对象,用来封装注册信息。最终还需要把它存到数据库,使数据持久化!
3.从注册页面开始

<h1>测试页面</h1>
<form action="servlet路径" method="post">
	用户名:<input type="text" name="username"/><br/>
	密 码:<input type="password" name="password"/><br/>
	<input type="submit" value="提交"/>
</form>

4从底层开始(javabean对象)

package com.kgc.bean;

public class User {
private String username;
private String password;

}

5.创建数据库
6.RegistDao
RegistDao只是一个接口,它定义了save(User user)方法。
public interface RegistDao {
public void save(User user) throws Exception;
}

7.RegistDaoImpl
RegistDaoImpl是RegistDao的实现类!
8.RegistServlet
只编写doPost()方法即可。

public class RegistServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
req.setCharacterEncoding(“UTF-8”);
String username = req.getParameter(“username”);
String password = req.getParameter(“password”);
User user = new User(username, password);

		RegistDao registDao = new RegistDaoImpl();
		registDao.save(user);

		resp.setContentType("text/html;charset=UTF-8");
		resp.getWriter().print("注册成功!!!");
	} catch (Exception e) {
		throw new RuntimeException(e);
	}
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值