6. Servlet
6.1、 Servlet简介
·Servlet就是Sun公司开发动态Web的一门技术
·Sun在这些API中提供一个接口叫做:Servlet,如果你要开发一个Servlet程序,只需要完成两个小步骤:
·编写一个类,实现Servlet接口
·把开发好的java类部署到web服务器中
把实现了Servlet接口的java程序叫做Servlet
Servlet接口Sun公司默认有两个实现类HttpServlet,GenericServlet
6.2、HellowServlet
1.构建一个Maven项目
2.下一步写好名称和路径。
3.删除Src,以后我们的学习就在这个项目使用Moudel。空的工程就是主工程。
4.找一些依赖导入
5.复制导入到Maven中
6.新建模块
7.因为Servlet是web模块,所以先建一个Web模块
8.输入模块名称,下一步
9.保持好Maven主路径,错了要修改
10.关于Maven父子工程的理解
·父项目会出现
·子项目会出现
父项目中的java---->子项目能直接使用
子项目的父项目不能使用
11.新建目录,构成初步项目目录结构
12.根据自动提示新建src\main\java。
13.根据自动提示新建src\main\resources。
14.可忽略的步骤,添加部署web模块,修改部署路径(为了和狂神目录结构一致,因为没配置好运行环境,自动创建没弄好,这里是手动设置的一步,到后面才发现问题原因。)
15.可忽略的步骤,配置web资源目录,(为了和狂神目录结构一致,因为没配置好运行环境,自动创建没弄好,这里是手动设置的一步,到后面才发现问题原因。)
16.可忽略的步骤,新建index.Jsp文件(为了和狂神目录结构一致,因为没配置好运行环境,自动创建没弄好,这里是手动设置的一步,到后面才发现问题原因。)
17.将想要部署的tomcat服务器版本的根路径复制到web.xml,(为了更好地兼容即将部署的服务器)
18.完成后结果如下图所示
19.编写一个Servlet程序,一个Servlet程序一定要实现Servlet接口。
新建一个包和类
20.继承HttpServlet
21.接口方法关系图
22.编写完类
23.编写映射
我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务注册我们写的Sevelt
24.配置tomcat,启动部署项目
25.遇到问题!!!
发现,无法启动tomcat,无法读取jar包,原因,我在更新Maven路径后,没有更改配置。
解决方案:
重新配置Java版本和JDK路径,jdk环境变量
重新配置Maven版本和路径,重新配置Maven环境变量
在IDEA中重新更改Maven设置
6.3、 Servlet原理
servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会:
6.4、 Mapping原理
1.一个Servlet可以指定一个映射路径,如访问“localhost:8081/webapp/use”
2.一个Servlet可以指定多个映射路径,
如访问“localhost:8081/webapp/use”,“localhost:8081/webapp/use1”
“localhost:8081/webapp/use2”等等
3.一个Servlet可以指定通用映射路径
如访问“localhost:8081/webapp/use/任意字符”
4.默认路径,会盖住index.jsp作用,最好不要这么写
5.指定一些后缀或者前缀......
如:访问"localhost:8081/webapp/后缀名为".liZhaoYuan"
6.优先级问题
指定了固有的映射路径的优先级最高,如果找不到就会走默认的。
6.5、 ServletContext
web容器在启动时,它会为每一个web程序都创建一个对应的Servlet对象,它代表了当前的web应用。
·共享数据
我在这个Servlet保存的数据,可以在另外的一个Servlet中拿到;
原理图:
6.5.1、一个共享数据的Servlet程序
(1)保存数据的程序
package com.kuang.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//共享数据,在这个程序写入要保存的数据
public class HellowServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//this.getInitParameter();--------->初始化参数,也可以在web.xml中配置
//this.getServletConfig();---------->Servlet配置
//this.getServletContext();--------->Servlet上下文
req.setCharacterEncoding("UTF-16");
resp.setCharacterEncoding("UTF-16");
ServletContext Context = this.getServletContext(); //创建了一个 ServletContext 对象,这个对象每个web程序都有
String Name="李照垣"; //假设有一个数据
Context.setAttribute("userName",Name); //以键值对的形式存储Name对象,("key",value),就将一个数据保存到了ServletContext当中
// //用响应头把键写出去
String userName = (String)Context.getAttribute("userName");
resp.getWriter().print("asda");
// System.out.println("hellow");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
(2)读取数据的程序
package com.kuang.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//共享数据,在这个程序中读取HellowServlet保存的数据
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext Context = this.getServletContext(); //建立ServletContext对象
String userName = (String) Context.getAttribute("userName"); //通过键获取值,因为我们知道保存的是String,强转类型后赋值
resp.setContentType("text/html"); //设置文本类型,通过浏览器可见
resp.setCharacterEncoding("utf-8"); //设置编码格式,通过浏览器可见
resp.getWriter().print("名字"+userName);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
6.5.2、获取初始化参数
1.在web.xml中写入以下配置
<!-- 设置web程序初始化参数-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306</param-value>
</context-param>
2.使用Servlet程序获取
package com.kuang.servlet;
import javax.servlet.ServletContext;
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.util.Properties;
//一个获取web程序配置的初始化参数
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext Context = this.getServletContext(); //建立ServletContext对象
String url = Context.getInitParameter("url"); //---->获取web.xml中的初始化参数,键值“url”
resp.getWriter().print(url); //---->打印web.xml中的初始化参数,值“jdbc:mysql//localhost:3306”
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
6.5.3、请求转发
1.过程图:
A访问只能通过B访问C的资源
package com.kuang.servlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//一个用来实现请求转发的程序
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext Context = this.getServletContext(); //建立ServletContext对象
System.out.println("进入了Demo04");//----------->测试程序是否进入这个doget方法
RequestDispatcher requestDispatcher = Context.getRequestDispatcher("/getIn");//getRequestDispatcher-->获取请求转发方法路径,// 有一个参数,这个参数就是我们要转发的地址
requestDispatcher.forward(req,resp);//调用forward()方法实现请求转发,所有的请求是第一个参数,所有的响应是第二个参数,它起到转发的作用
//
// Context.getRequestDispatcher("/getIn").forward(req,resp);//----->上面两行语句合并写法,
// //这样程序就通过Demo04程序访问了Demo03页面,路访问径是/getRe,但是页面访问是Demo03
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
6.5.4、读取资源文件
1.Properties类
·在java目录下新建properties
·在resource目录下新建properties
发现都被打包到同一路径下
我们俗称这个路径为类路径“classespath;”(没有aa.properties是因为资源打包失败,习惯大于约束,在pom.xml的building中配置)
知道路径后就可以读取了
思路:需要一个文件流,方法很多这只举了一个。
2.读取db.properties资源文件
package com.kuang.servlet;
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.io.InputStream;
import java.util.Properties;
//一个用来读取资源文件的程序
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream resourceAsStream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");//------这个方法把资源变成一个流,参照打包的写相对路径,这里”/“的意思是指向Servlet-02这个程序,
// 访问Servlet-02程序下的WEB-INF的classes的db.properties
Properties properties = new Properties(); //新建一个properties类
properties.load(resourceAsStream); //加载上面的流
String username = properties.getProperty("username");//获取资源的键
String password = properties.getProperty("password");//获取资源的键
resp.getWriter().print(username+":"+password); //响应到页面
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
6.6、HttpServletResponse
6.6.1、HttpServletResponse的一些方法
web服务器收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的HttpServletresponse;
·我们如果要获取客户端请求过来的参数:找HttpServletRequet
·如果要给客户端响应一些信息:找HttpServletResponse
1.简单分类
负责向浏览器发送数据的方法
·ServletOutputStream getOutputStream() throws IOException;
写一般流
·PrintWriter getWriter() throws IOException;
写中文用,其他的用会造成字符串损失。
负责向浏览器发送响应头的方法
· void setCharacterEncoding(String var1);
设置响应的编码
· void setContentLength(int var1);
设置响应的字符串长度
· void setContentLengthLong(long var1);
设置长度?
· void setContentType(String var1);
设置类型
· void setDateHeader(String var1, long var2);
· void addDateHeader(String var1, long var2);
· void setHeader(String var1, String var2);
· void addHeader(String var1, String var2);
· void setIntHeader(String var1, int var2);
· void addIntHeader(String var1, int var2);
响应的状态码:
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
6.6.2、常见应用
1.向浏览器输出信息
2.下载文件
字符串处理
·要获取下载的文件的路径
·下载的文件名
·设置想办法让浏览器能够支持我们下载的东西
resp.setHeader("Content-disposition","attachment;filename"+ URLEncoder.encode(filename));
·获取下载文件的输入流
IO流操作
·创建缓冲区
·获取OutputStream对象
·将FileOutputStream流写入到buffer缓冲区
·使用OutputStream将缓冲区中的数据输出到客户端
6.6.3、浏览器让客户端下载文件
package com.kuang.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
//实现让浏览器可以下载文件
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 字符串处理
//·要获取下载的文件的路径,假设有这个文件
// String realPath = this.getServletContext().getRealPath("/1.jpg");
String realPath = "C:\\Users\\a\\Desktop\\新项目\\javaweb-02-Servlet\\response\\target\\classes\\1.jpg";
System.out.println("获取下载的文件的路径"+realPath);
//·下载的文件名
String filename = realPath.substring(realPath.lastIndexOf("\\") + 1);//------->这是一个极其精巧的方法,使用substring截取字符串,从“/”的后一位开始获取后缀子串。
//·设置想办法让浏览器能够支持我们下载的东西,百度搜索web下载文件的头消息,获取resp.setHeader("Content-disposition","attachment;filename"+ URLEncoder.encode(filename));
//浏览器能够支持("Content-disposition")我们需要下载的东西
resp.setHeader("Content-disposition","attachment;filename="+filename);//-->通过设置响应头让浏览器给客户端下载文件
// resp.setHeader("Content-disposition","attachment;filename="+ URLEncoder.encode(filename,"UTF-8"));--->当文件名有中文,需要进行编码转换
//·获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
//IO流操作
//·创建缓冲区
int len=0;
byte[] buffer = new byte[4096];
//·获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
//·将FileOutputStream流写入到buffer缓冲区
while ((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
in.close();
out.close();
//·使用OutputStream将缓冲区中的数据输出到客户端
resp.getWriter().print(out);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
6.6.4、验证码功能
·前端实现
·后端实现,需要使用java的图片类,生产一个图片
package com.kuang.servlet;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
//实现验证码的程序
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器5秒自动刷新一次
resp.setHeader("refresh","3");
//在内存中创建一个图框,BufferedImage(图片的长,图片的宽,图片的颜色格式);
BufferedImage Image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
//得到到笔,绘制2D图片
Graphics2D graphics =(Graphics2D) Image.getGraphics();//---->graphics想成一支笔
//设置图片的背景图片,通过参数设置笔芯为白色
graphics.setColor(Color.white);
//填充一个矩形
graphics.fillRect(0,0,80,20);
//给图片写数据,通过参数设置笔芯为蓝色
graphics.setColor(Color.BLUE);
//设置字体的类型,BOLD粗体,大小20像素
graphics.setFont(new Font(null,Font.BOLD,20));
//画一些东西,在任意的位置开始绘制字符串makeNum
graphics.drawString(makeNum(),0,20);
//告诉浏览器这个请求用图片的方式打开
resp.setContentType("image/jpg");
//网站存在缓存,不让浏览器缓存,在百度搜索"响应头属性exprise -1"
resp.setDateHeader("exprise",-1);
//设置缓存控制属性
resp.setHeader("Cache-Control","no-cache");
//设置属性,具体我不清楚是啥,作用也是缓存控制
resp.setHeader("Pragma","no-cache");
//把图片写给浏览器ImageIO专门用来些图片
boolean write = ImageIO.write(Image,"jpg", resp.getOutputStream());
}
//生成随机数
private String makeNum(){
Random random = new Random();
//生成七位整数的验证码,(添加一个空的字符串,自动引入局部变量,转化成字符串)
String num = random.nextInt(9999999) + "";
//若生成的随机数位数小于7位,生成足够的0,用来补位数
StringBuffer sB = new StringBuffer();
for (int i = 0; i < 7-num.length(); i++) {
sB.append("0");
}
//补足位数
num = sB.toString() + num;
return num;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
6.6.5、页面重定向
一个web资源收到客户端请求后,他会通知客户端去访问另外一个web资源,这个过程叫做重定向。
常见场景:
·用户登陆
·void sendRedirect(String var1) throws IOException;
面试题:请你聊聊重定向和转发区别:
相同点:页面都会跳转
不同点:
·请求转发的时候,页面url不会发生变化
·在重定向的时候,页面url会发生变化
页面重定向
package com.kuang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//resp.sendRedirect()--------->实现页面重定向的方法
public class Redirect extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这两行代码等价于resp.sendRedirect()
// resp.setHeader("Location","/img");
// resp.setStatus(302);
resp.sendRedirect("/webapp/img");//resp.sendRedirect()--------->实现页面重定向的方法,参数就是重定向的位置
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
6.7、 HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest方法,获取客户端的所有信息。
request的一些方法:
6.7.1、获取前端传入的参数
package com.kuang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//处理前端请求的程序,获取前端传入的参数,前端页面是index.xml
public class RequestTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入这个请求了");
//处理请求,使用getParameter()方法通过参数获取前端的值
String username = req.getParameter("username");
String password = req.getParameter("password");
//输出获取的值
System.out.println(username+":"+password);
//获取到账户密码进行页面重定向
resp.sendRedirect("/webapp/success.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
6.7.2、获取参数,请求转发
package com.kuang.servlet;
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.lang.reflect.Array;
import java.util.Arrays;
//获取前端的参数,并实现请求转发.
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
String username = req.getParameter("username");//---->获取index.jsp的username
String password = req.getParameter("password");//---->获取index.jsp的password
String[] hobbys = req.getParameterValues("hobbys");//---->以字符数组获取多选框“hobbys”的值
System.out.println("===============================================================");
System.out.println(username);//---->打印输出用户名
System.out.println(password);//---->打印输出密码
System.out.println(Arrays.toString(hobbys));//----->以字符串打印输出数组
System.out.println("===============================================================");
// resp.sendRedirect("/success.jsp");//重定向转发
req.setCharacterEncoding("utf-8");
req.getRequestDispatcher("/success.jsp").forward(req,resp);//请求转发
// this.getServletContext();//共享
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}