学习JAVAWEB(四)——SERVLET
前言:
承接前面的内容来学习JavaWeb中的Servlet
什么是Servlet
- Servlet是JavaEE规范之一。规范,就是接口。
- Servlet是JavaWeb三大组件之一。(三大组件:Servlet程序、Filter过滤器、Listener监听器)
- Servlet是运行在服务器上的一个Java小程序,它可以接收客户端发送过来的请求,并相应数据给客户端。
创建Servlet程序
- 编写一个类去实现Servlet接口
- 实现Service方法,处理请求并相应数据
- 到web.xml中去配置servlet程序的访问地址
在上次的项目下,在包下面新建一个Java类,继承自Servlet,按下Alt+Insert来实现接口的几个方法。
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
//services方法是专门用来处理请求和响应的
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet!");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
然后我们来修改WEB-INF/web.xml,添加内容:
<!--servlet标签给Tomcat配置servlet程序-->
<servlet>
<!--给servlet程序起一个别名(一般是类名)-->
<servlet-name>HelloServlet</servlet-name>
<!--程序的全类名-->
<servlet-class>com.javaweb.test.HelloServlet</servlet-class>
</servlet>
<!--给servlet程序配置访问地址-->
<servlet-mapping>
<!--告诉服务器当前配置的地址给哪个Servlet程序使用-->
<servlet-name>HelloServlet</servlet-name>
<!--配置访问地址-->
<!--
/ 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径
/Hello 表示地址为 http://ip:port/工程路径/Hello
-->
<url-pattern>/Hello</url-pattern>
</servlet-mapping>
最后,我们启动程序,在自动打开的页面的url后加上 Hello访问,然后在IDEA控制台中,就可以看到输出Hello Servlet!
尚硅谷的这个流程图总结的非常好,我们来看看:
Servlet 学习
Servlet生命周期
在第一次访问时(创建实例)会调用:
- Servlet 构造器方法
- init 初始化方法
在每次访问都会调用
- service 方法
在 web 工程停止的时候调用
- destroy 方法(释放内存)
Servlet 请求分发处理
我们需要判断请求的不同类型来做不同的处理
//services方法是专门用来处理请求和响应的
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException {
System.out.println("Hello Servlet!");
//类型转换(因为它有GetMethod()方法)
HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest;
String method=httpServletRequest.getMethod();
if("GET".equals(method)){
doGet();
}else if("POST".equals(method)){
doPost();
}
}
public void doGet(){
System.out.println("get");
}
public void doPost(){
System.out.println("post");
}
我们可以写一个简单的页面来测试:
<body bgcolor="#f4a460">
<form action="http://localhost:8080/web_war_exploded/Hello" method="post">
<input type="submit" />
</form>
</body>
通过继承HttpServlet类实现
一般在实际项目开发中,都是使用继承 HttpServlet 类的方法去实现Servlet程序。
- 编写一个类去继承HttpServlet类
- 根据业务需要重写doGet或doPost方法
- 到web.xml中的配置Servlet程序的访问地址
我们来重写一个类来做示例,重创建一个 HelloServlet2,并且让它继承自 HttpServlet 。
在HttpServlet中,已经自动帮助我们根据请求方式分发好了get请求处理和post请求处理。
public class HelloServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doget");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的dopost");
}
}
然后我们去web.xml中配置,加上
<servlet>
<servlet-name>HelloServlet2</servlet-name>
<servlet-class>com.javaweb.test.HelloServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet2</servlet-name>
<url-pattern>/Hello2</url-pattern>
</servlet-mapping>
然后将Test.html中表单的发送地址发送给Hello2,然后测试一下,和上面效果一样。
使用IDEA直接生成
在需要生成Servlet文件的包那里,右击,下面有个创造Servlet文件,点击即可。
非常方便。
它会在对应的web.xml中生成<Servlet></Servlet>,你再给它写一下servlet-mapping即可
<servlet-mapping>
<servlet-name>HelloServlet3</servlet-name>
<url-pattern>/Hello3</url-pattern>
</servlet-mapping>
Servlet继承体系
ServletConfig类
基本了解
我们学一下另一个东西换换脑——ServletConfig类
ServletConfig类是Servlet程序的配置信息类。
三大作用
- 可以获取Servlet程序的别名——servlet-name的值
- 获取初始化参数 init-param
- 获取 ServletContext 对象
回头实现Servlet接口的那个类——HelloServlet,看看初始化的方法 init,我们就会发现init的参数就是一个ServletConfig对象。
我们首先可以修改xml配置文件来添加初始化参数,以我们的第一个类为例:
<!--servlet标签给Tomcat配置servlet程序-->
<servlet>
<!--给servlet程序起一个别名(一般是类名)-->
<servlet-name>HelloServlet</servlet-name>
<!--程序的全类名-->
<servlet-class>com.javaweb.test.HelloServlet</servlet-class>
<!--init-param是初始化参数-->
<init-param>
<!--参数名-->
<param-name>name</param-name>
<!--参数值-->
<param-value>root</param-value>
</init-param>
<init-param>
<!--参数名-->
<param-name>url</param-name>
<!--参数值-->
<param-value>is-hash.com</param-value>
</init-param>
</servlet>
然后,修改类中的init方法:
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("HelloServlet程序的别名是"+servletConfig.getServletName());
System.out.println("初始化参数username的值是"+servletConfig.getInitParameter("name"));
System.out.println("初始化参数url的值是"+servletConfig.getInitParameter("url"));
System.out.println(servletConfig.getServletContext());
}
然后,启动项目(http://localhost:8080/web_war_exploded/Hello)就会看到打印:
HelloServlet程序的别名是HelloServlet
初始化参数username的值是root
初始化参数url的值是is-hash.com
aaa@qq.com
这个ServletContext我们后面会介绍。
补充:
Servlet程序和ServletConfig对象都是由Tomcat负责创建,我们负责使用。
Servlet程序默认是第一次访问的时候创建,ServletConfig是每个Servlet程序创建时就创建一个对应的ServletConfig对象。
不止init,在其他方法中也可以得到ServletConfig对象。
例如我们的HelloServlet2中,在doGet方法内可以使用如下命令得到HelloServlet2对应的ServletConfig对象:
ServletConfig servletConfig=getServletConfig();
一定要注意:这里得到的ServletConfig一定是对应的自己的那个Servlet类的ServletConfig。
还有一点,如果在子类(HttpServlet )中调用init方法,一定要加上下面的语句:
super.init(config);
原因很简单,在父类(GenericServlet)的init方法中,定义了这样:
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
如果重写init而不执行父类的init,意味着父类的init被完全覆盖,也就不会将config保存给自己的属性config,这样得到的config就不能使用,是一个空指针。
ServletContext类
基础了解:
上面我们出现了一个ServletContext对象,那么这个是什么呢?
ServletContext也是一个接口,他表示Servlet上下文对象。
一个web工程只有一个ServletContext对象实例。
ServletContext对象是在Web工程启动时创建,在停止时销毁(重新部署就会销毁这个对象),而只要你有这个对象没被销毁,那么可以工程的任何地方得到这个对象。
ServletContext对象是一个域对象
域对象:是可以像Map一样存取数据的对象。
这里的域指的是存取数据的操作范围(这里是整个web工程)。
我们来看看他和Map对象的对比:
存数据 | 取数据 | 删除数据 | |
---|---|---|---|
Map | put | get | remove() |
域对象 | setAttribute() | getAttribute() | removeAttribu |
ServletContext类的四个作用
- 获取web.xml中配置的上下文参数
- 获取当前的工程路径(/工程名)
- 获取工程部署后在服务器磁盘上的绝对路径
- 像Map一样存储数据
我们来新建一个Servlet文件来做实验,记得配置好xml文件:
<servlet-mapping>
<servlet-name>ContextServlet</servlet-name>
<url-pattern>/Context</url-pattern>
</servlet-mapping>
然后创建一个 “上下文参数” 来做示例,直接在web.xml中添加:
<context-param>
<param-name>Context</param-name>
<param-value>ContextValue</param-value>
</context-param>
下面是ContextServlet.java的内容:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletContext context=getServletConfig().getServletContext();
//根据键得到Context-param上下文参数的值
//别被getInitParameter这个误导,他与一个Servlet对象的Init-param是两回事
String username=context.getInitParameter("Context");
System.out.println("上下文参数Context的值是:"+username);
//得到当前路径
System.out.println("当前工程路径:"+context.getContextPath());
//得到主机中的具体路径
/*
这里解释一下为什么是"/",http://ip:port/工程名,"/"就是
映射到IDEA代码的web目录(目录名web,不是模块名)
*/
System.out.println("工程部署的路径是"+context.getRealPath("/"));
}
直接访问(Get请求)/工程名/Context,控制台输出:
上下文参数Context的值是:ContextValue
当前工程路径:/web_war_exploded
工程部署的路径是F:\Idea的平时作品\JavaWeb\out\artifacts\web_war_exploded\
下面我们再来验证第四个功能,像Map一样存储数据:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取ServletContext对象
ServletContext context=getServletContext();
context.setAttribute("key1","value1");
System.out.println("Context1 key1的值是:"+context.getAttribute("key1"));
}
输出:
Context1 key1的值是:value1
HTTP
什么是http协议
又回到基础问题了
什么是协议?
协议,是指双方或多方相互约定好,大家都需要遵守的规则,叫协议。
所谓HTTP协议,就是指客户端和服务器之间通信时发送的数据,需要遵守的规则,叫HTTP协议。
Http协议中的数据又叫报文
GET请求
- 请求行
- 请求的方式:GET
- 请求的资源路径(+?+请求参数)
- 请求的协议的版本号:HTTP/1.1
- 请求头
- key-value组成,不同的键值对表示不同的含义
POST请求
- 请求行
- 请求的方式:POST
- 请求的资源路径(+?+请求参数)
- 请求的协议的版本号:HTTP/1.1
- 请求头
- key-value组成,不同的键值对表示不同的含义
- 请求体:就是发送给服务器的数据
常用请求头
- Accept:表示客户端可以接受的数据类型
- Accept-Languege:表示客户端可以接收的语言类型
- User-Agent:表示客户端浏览器的信息
- Host:表示请求时的服务器ip和端口号
常见请求方式
GET请求
- form标签 method=“get”
- a 标签
- link标签引入css
- Script标签引入js文件
- img标签引入图片
- iframe引入html页面
- 在浏览器输入url访问
POST请求
- form标签 method=“post”
响应的HTTP协议介绍
- 响应行
- 响应的协议和版本号
- 响应状态码
- 响应状态描述符
- 响应头
- key-value组成,不同的键值对表示不同的含义
- 响应体:就是回传给客户端的数据
常见HTTP响应码:
- 200:表示请求成功
- 302:表示请求重定向
- 404:表示请求服务器已经收到了,但是你要的数据不存在(找不到页面)
- 500:表示服务器已经收到请求,但是服务器内部错误(代码出问题)
MIME类型说明
MIME是HTTP协议中数据类型。
MIME的英文全称是“Multipurpose Internet Mail Extensions”(多功能Internet邮件扩充服务)。
MIME类型的格式是“大类型/小类型”,并与某一种文件的扩展名相对应。
常见知识点:
HttpServletRequest
每次有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的Http协议信息封装到Request对象中。 然后传递到service方法(doGet和doPost)中给我们使用。我们可以通过HttpServletRequest对象,获取到所有请求的信息。
我们可以在doGet/doPost里面得到这个参数
常用方法:
- getRequestRUI( ):获取请求的资源路径
- getRequestURL():获取请求的统一资源定位符(绝对路径)
- getRemoteHost( ):获取客户端的ip地址
- getHeader( ):获取请求头
- getParameter( ):获取请求的参数
- getParameterValues( ):获取请求的参数(多个值的时候使用)
- getMethod( ):获取请求的方式GET或者POST
- setAttribute(key,value):设置域数据
- getAttribute(key):获取域数据
- getRequestDispatcher():获取请求转换对象
如何获取客户端请求的参数
利用好API方法即可,示例:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//改变编码方式需要在获取参数前调用
request.setCharacterEncoding("UTF-8");
//通过name基本属性来得到value
String username=request.getParameter("username");
String password=request.getParameter("password");
String[] hobby=request.getParameterValues("hobby");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.asList(hobby));
}
request.setCharacterEncoding("UTF-8");
常常用来解决POST请求中文乱码问题。
请求的转发
请求转发是指,服务器收到请求之后,从一次资源跳转到另一个资源的操作叫做请求转发。
下面来看看示例:
ContextServlet.java:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username=request.getParameter("username"); System.out.println("ContextServlet得到用户名:"+username); //设置一个域变量 request.setAttribute("key","first"); //请求转发必须要以斜杠打头 //斜杠代表地址是:http://ip:port/工程名/ //Dispatcher可以理解为调度 RequestDispatcher requestDispatcher =request.getRequestDispatcher("/Hello3"); //进行请求转发 requestDispatcher.forward(request,response); }
HelloServlet3.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username=request.getParameter("username");
System.out.println("HelloServlet3得到用户名:"+username);
//查看Context是否有记录域变量
Object key1= request.getAttribute("key");
System.out.println("ContextServlet是否有记录"+key1);
//处理自己的业务
System.out.println("HelloServlet3 处理自己的业务");
}
然后我们使用一个表单随便输入点东西然后提交给/Context(映射到ContextServlet类)
控制台打印:
ContextServlet得到用户名:奥术大师多
HelloServlet3得到用户名:奥术大师多
ContextServlet是否有记录first
HelloServlet3 处理自己的业务
base标签
如果利用请求转发去转发一个页面,由于路径问题可能会存在这样的一种情况
解决这个问题,就可以使用base标签
base标签可以设置当前页面中所有相对路径工作时,参照哪个路径来进行跳转
示例:
这个就设置好了本页面中所有相对路径的参考地址
HttpServletResponse类
HttpServletResponse类的作用
HttpServletResponse类和HttpServletRequest类一样。每次请求进来,Tomcat服务器都会创建一个Response对象传递给Servlet程序去使用。
我们如果需要设置返回给客户端的信息,就可以通过 HttpServletResponse 对象来设置。
两个输出流的说明
- 字节流–getOutputStream():常用于下载(传递二进制数据)
- 字符流–getWriter():常用于回传字符串(常用)
两个流同时只能使用一个。
如何往客户端回传字符数据
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//得到字符流
PrintWriter writer=response.getWriter();
writer.println("response content");
}
然后访问/Context,映射到这个类里面,客户端页面就会显示 response content
这里注意一下,直接这样使用是不能回传中文,如果你通过流输出中文,会显示成问号,我们可以打印回传字符集看一下:
System.out.println(response.getCharacterEncoding());
打印:ISO-8859-1
字符集不支持中文
我们可以尝试设置一下字符集:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
//得到字符流
PrintWriter writer=response.getWriter();
writer.println("有意思");
}
再做测试我们会发现,客户端浏览器已经不现实问号了,但是显示了乱码。
这是因为我们只是把服务器端的字符集改变了,浏览器的字符集与服务器端的字符集不统一,所以会造成这个情况。
我们可以在客户端浏览器上去改,但是这样很不现实,因为以后项目展示出来不可能让客户一个一个去改。
所以我们可以直接在服务器端下手,添加响应头,这样浏览器就可以按照UTF-8来显示了:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
//添加响应头,设置浏览器也使用UTF-8字符集
response.setHeader("Content-Type","text/html;charset=UTF-8");
//得到字符流
PrintWriter writer=response.getWriter();
writer.println("有意思");
}
或者,你可以直接设置内容类型:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//它会设置服务器和客户端都使用UTF-8字符集,同时设置响应头
//此方法一定要在获取流对象之前设置才有效!
response.setContentType("text/html;charset=UTF-8");
//得到字符流
PrintWriter writer=response.getWriter();
writer.println("有意思");
}
响应头信息:
Content-Length: 11
Content-Type: text/html;charset=UTF-8
Date: Fri, 24 Apr 2020 11:25:56 GMT
Server: Apache-Coyote/1.1
请求重定向
第一种方法
请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些新地址,你去新的地址访问(因为之前的地址可能已经被废弃)。
我们来测试:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("经过Context文件");
//设置响应状态码302,表示重定向
response.setStatus(302);
//设置响应头,说明新的地址在哪里
response.setHeader("Location","http://127.0.0.1:8080/web_war_exploded/Hello3");
}
在Hello3对应的那个类里面写上:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("经过Hello3");
}
然后我们访问Context,控制台就会输出:
经过Context文件
经过Hello3
请求转发和请求重定向可能很像,但其实很不一样:
本质区别:请求转发只是发了一次请求,请求重定向发了两次请求。
- 地址栏
- 请求转发:地址栏还是当初请求的地址栏;
- 请求重定向:地址栏不会是初次的地址栏,地址栏最后一次相应的地址栏。
- request对象
- 请求转发:在最终的servlet中,request对象和中转的request对象是同一个对象;
- 请求重定向:在最终的servlet中,request对象和中转的request对象不是同一个对象。
- /的意义:
- /代表当前web应用的根目录http://localhost:8080/servlet02,是当前web的根目录;
- 请求重定向:/代表当前web站点的根目录http://localhost:8080是当前web站点。
- 范围
- 请求转发:只能转到当前web资源;
- 请求重定向:可以重定向到外部资源。
302请求重定向的特点:
- 浏览器地址栏会发生变化
- 两次请求
- 不共享Request域中数据
- 不能访问WEB-INF下的资源
- 可以访问工程外的资源
第二种方法
一行代码搞定:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("经过Context文件");
response.sendRedirect("http://127.0.0.1:8080/web_war_exploded/Hello3");
}
补充:
JavaEE项目的三层架构
分层是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护与升级。
分层常常对应包:
- web层
- com.atluoluo.web/servlet/controller
- service层
- com.atluoluo.services:Service 接口包
- com.atluoluo.services.impl:Service 接口实现类
- dao持久层
- com.atluoluo.dao:Dao接口包
- com.atluoluo.dao.impl:Dao接口实现类
- 实体bean对象
- com.atluoluo.pojo/entity/domain/bean:JavaBean类
- 测试包
- com.atluoluo.test/junit
- 工具类
- com.atluoluo.utils
商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢
推荐阅读
-
PHP 面向对象程序设计(oop)学习笔记 (四) - 异常处理类Exception
-
javaweb学习--jsp
-
python网络编程学习笔记(四):域名系统
-
JavaWeb学习日记----XML的解析
-
学习使用Material Design控件(四)Android实现标题栏自动缩放、放大效果
-
javaweb学习讲解之CSS_文本外观
-
C#多线程学习之(四)使用线程池进行多线程的自动管理
-
Sql学习第四天——SQL 关于with cube,with rollup和grouping解释及演示
-
linux学习日记四 文件与目录管理
-
王之泰201771010131《面向对象程序设计(java)》第四周学习总结