Java Web学习--Servlet
Java Web学习--Servlet
什么是Servlet?
Servlet含义是服务器端的小程序
Servlet容器是一个web服务器,用来解析浏览器发出的请求,并且调用Java类中的特定的方法.并且将处理完数据响应给浏览器
在整个Web应用中,Servlet主要负责处理请求、协调调度功能。我们可以把Servlet称为Web应用中的『控制器』
Servlet如何运行?
当我们点击浏览器页面上面的一个超链接的时候,由Servlet来处理这个请求.
所以我们要创建一个当我们点击这个超链接的时候或者我们打开某个页面的时候,就会给我们的Servlet去发送请求.
比如在pro07-javaweb-begin这个项目里面当我们输入好数据之后点击提交会交给我们的Servlet组件去处理.同时为了我们点击超链接可以找到对应的Servlet组件,我们需要在配置文件:WEB-INF/web.xml里面配置让其可以正确的找到Servlet组件.
这是前端html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="add" method="post">
名称:<input type="text" name="fname"/><br/>
价格:<input type="text" name="price"/><br/>
库存:<input type="text" name="fcount"/><br/>
备注:<input type="text" name="remark"/><br/>
<input type="submit" value="添加" />
</form>
</body>
</html>
当我们点击添加给add发请求的时候就需要用到xml文件
<!-- 配置Servlet本身 -->
<servlet>
<!-- 全类名太长,给Servlet设置一个别名 -->
<servlet-name>AddServlet</servlet-name>
<!-- 这里是servlets的全类名-->
<servlet-class>com.atguigu.servlets.AddServlet</servlet-class>
</servlet>
<servlet-mapping>
<!-- 这里是刚才起的别名 -->
<servlet-name>AddServlet</servlet-name>
<!-- 这里对应刚才html页面上的add-->
<url-pattern>/add</url-pattern>
</servlet-mapping>
<!--
1. 用户发请求,action=add
2. 项目中,web.xml中找到url-pattern = /add -> 第12行
3. 找第11行的servlet-name = AddServlet
4. 找和servlet-mapping中servlet-name一致的servlet , 找到第7行
5. 找第8行的servlet-class -> com.atguigu.servlets.AddServlet
6. 用户发送的是post请求(method=post) , 因此 tomcat会执行AddServlet中的doPost方法
-->
这样就找到了AddServlet这个类,因为我们表单发送数据的方式为post所以我们必须要重写AddServlet的dopost方法否则会出错.
public class AddServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
FruitDAO fruitDAO = new FruitDAOImpl();
boolean flag = fruitDAO.addFruit(new Fruit(0 , fname , price , fcount , remark));
System.out.println(flag ? "添加成功!" : "添加失败!");
}
}
上面的代码就是说我们从request请求里面得到了,发送的信息,然后调用FruitDao将数据发送添加进了数据库,如下图所示
但是我们发现发送的中文的在数据库里面是乱码所以我们要在Servlet里面设置编码方式.
下面是post请求方式设置编码
post求方式
request.setCharacterEncoding("UTF-8");
tomcat8开始,设置编码,只需要针对post方式
request.setCharacterEncoding("UTF-8");
注意:
需要注意的是,设置编码(post)这一句代码必须在所有的获取参数动作之前
request.setCharacterEncoding("UTF-8");
Servlet的生命周期
先来看Servlet的接口和相关方法
void init(config) - 初始化方法
void service(request,response) - 服务方法
void destory() - 销毁方法
其中最重要的就是服务方法
HttpServlet 抽象子类中的service方法不是抽象的在这个子类中将service根据请求方式的不同调用不同的do方法.
继承关系为:HttpServlet -> GenericServlet -> Servlet
服务方法: 当有请求过来时,service方法会自动响应(其实是tomcat容器调用的)
所以在HttpServlet中我们会去分析请求的方式:到底是get、post、head还是delete方法
我们必须要实现对应的doxxx方法如果不进行实现的话就会报405错误.所以我们在新建Servlet时,我们才会去考虑请求方法,从而决定重写哪个do方法
Servlet的生命周期
- 从出生到死亡的过程就是生命周期。对应Servlet中的三个方法:init(),service(),destroy()
- 默认情况下:第一次接受请求的时候,Servlet才会进行实例化(调用构造方法)初始化(调用init()方法),然后进行服务.当容器关闭的时候,其中的Servlet实例会调用销毁方法被销毁.也就是说Servlet在第一次接收到请求的时候才开始创建对象.然后在tomcat中一直都是这一个实例来响应.
- Tomcat中,每一个请求会被分配一个线程来处理,所以可以说:Servlet是单实例,多线程方式运行的。
- .Servlet的初始化时机:
- 默认是第一次接收请求时,实例化,初始化. 但是我们仍然可以通过web.xml文件里面的配置来来设置servlet启动的先后顺序,数字越小,启动越靠前,最小值0.
<!-- 配置Servlet启动顺序 -->
<load-on-startup>1</load-on-startup>
名称 | 时机 | 次数 |
---|---|---|
创建对象 | 默认情况:接收到第一次请求 修改启动顺序后:Web应用启动过程中 | 一次 |
初始化操作 | 创建对象之后 | 一次 |
处理请求 | 接收到请求 | 多次 |
销毁操作 | Web应用卸载之前 | 一次 |
Servlet在容器中是:单例的、线程不安全的
单例:所有的请求都是同一个实例去响应
线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化
- 我们已经知道了servlet是线程不安全的,给我们的启发是: 尽量的不要在servlet中定义成员变量。如果不得不定义成员变量,那么不要去:①不要去修改成员变量的值 ②不要去根据成员变量的值做一些逻辑判断
Servlet的请求转发和重定向
现在我们将一个请求发给了Servlet但是在绝大部分情况下,Servlet不能独自完成一切,需要把接力棒继续传递下去,此时我们就需要请求的『转发』或『重定向』
服务器内部转发
- 在请求的处理过程中,Servlet完成了自己的任务,需要把请求转交给下一个资源继续处理。
//基本格式
request.getRequestDispatcher("...").forward(request,response);
-
转发的核心操作时在服务器端完成的,所以客户端是感觉不到的,客户端只需要发一次请求即可
-
一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的
-
浏览器地址栏没有变化
客户端重定向
- 在请求的处理过程中,Servlet完成了自己的任务,然后以一个响应的方式告诉浏览器:“要完成这个任务还需要你另外再访问下一个资源”。
//基本格式
response.sendRedirect("....");
-
由于重定向操作的核心部分是在浏览器端完成的,所以整个过程中浏览器共发送两次请求。也就是说客户端先给A发送请求然后A让客户端再给B发送请求.
-
客户端肯定知道请求URL有变化,地址栏有变化
对比
转发 | 重定向 |
---|---|
一次请求 | 两次请求 |
浏览器地址栏显示的是第一个资源的地址 | 浏览器地址栏显示的是第二个资源的地址 |
全程使用的是同一个request对象 | 全程使用的是不同的request对象 |
在服务器端完成 | 在浏览器端完成 |
目标资源地址由服务器解析 | 目标资源地址由浏览器解析 |
目标资源可以在WEB-INF目录下 | 目标资源不能在WEB-INF目录下 |
目标资源仅限于本应用内部 | 目标资源可以是外部资源 |
保存作用域
原始情况下作用域有4个
- page(页面级别,现在几乎不用)
- request(一次请求响应范围)
- session(一次会话范围)
- application(整个应用程序范围)
1)request:一次请求响应范围
2) session:一次会话范围有效
3) application: 一次应用程序范围有效
request:一次请求响应范围
因为request只对一次请求响应有效所以客户端重定向打印的值为null.而第二个为服务器内部转发打印的值为lili.
session一次会话范围有效
只要是一个同一个客户端具有的session值是相同的那么就可以在一次会话中获取到对应的值,不管是服务器重定向还是服务器内部转发都可以打印出lili.
application一次应用程序范围有效
在application中只要保存了数据其他不同的客户端都能获取到这个数据.打印出lili.相当于是共有的
路径问题
记得以后都使用绝对路径