6、Serrvlet
6.1、Servlet简介
-
Servlet就是sun公司开发动态web的一门技术
-
sun公司在这些API中提供了一个接口叫做:Servlet 如果你想开发一个Servlet程序,只需要完成两个小步骤
-
编写一个类,实现Servlet接口
-
把开发好的Java类部署到web服务器中
-
把实现了Servlet接口的Java程序叫做 Servlet
6.2、Hello Servlet
Servlet接口有两个默认的实现类:HttpServlet,
-
构建一个Maven项目,删掉src目录,以后的学习就在这个项目建立moudel,这个空的工程就是Maven的主工程
-
关于Maven父子工程:
Maven环境优化
-
修改web.xml为最新的
-
将Maven的结构搭建完整
编写一个Servlet程序
-
编写一个普通类
-
实现Servlet接口,我们现在直接继承HttpServlet
-
package com.liu.servlet; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * @version 1.0 * @description:TODO * @Author: * @date :2021/9/15 17:20 */ public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ServletInputStream sis= req.getInputStream(); // ServletOutputStream sos = resp.getOutputStream(); PrintWriter writer = resp.getWriter(); //相应流 writer.println("Hello Servlet!"); } //由于get或者post只是请求实现的不同方式,可以相互调用,业务逻辑都一样 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
6.3、编写Servlet的映射
-
我们写的是Java程序,但是要通过浏览器访问,但是浏览器需要连接web服务器,所以我们需要在web服务中,注册我们写的Servlet,还需要给他一个浏览器能够访问的逻辑
package com.liu.servlet; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * @version 1.0 * @description:TODO * @Author: * @date :2021/9/15 17:20 */ public class HelloServlet extends HttpServlet { public static void main(String[] args) { } //由于get或者post只是请求实现的不同方式,可以相互调用,业务逻辑都一样 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ServletInputStream sis= req.getInputStream(); // ServletOutputStream sos = resp.getOutputStream(); PrintWriter writer = resp.getWriter(); //相应流 writer.println("Hello Servlet!"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } }
配置Tomcat
启动测试
6.4、Servlet原理
Mapping
请求映射的路径
-
一个Servlet请求可以指定一个映射路径
<!-- Serlvet的请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
一个Servlet请求可以指定多个映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello3</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello4</url-pattern> </servlet-mapping>
-
一个Servlet请求可以指定通用映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
localhost:8080/hello/asdkakldjakd 后面输什么都加入HelloServlet
-
默认请求路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
-
指定一些后缀或者前缀
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.liuzhengwei</url-pattern> </servlet-mapping>
注意,前面不能加映射的路径 不能写/hello/.liuzhengwei,但是可以写 hello/.liuzhengwei
localhost:8080/hello/*.liuzhengwei .前面加什么都进去HelloServlet
优先级
-
指定Mapping映射路径优先级最高 如 /hello
-
如果找不到就会走默认的处理请求
6.5、ServleteContext
Serlvet配置:web容器在启动的时候,会为每一个程序创建一个对应的Servlet对象,他代表了当前的web应用
共享数据:
两个servlet中的数据可以共享
-
放置数据
public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //this.getInitParameter(); 初始化参数 //this.getServletConfig(); Servlet配置 //this.getServletContext(); Servlet上下文 ServletContext context = getServletContext(); String username = "刘正伟"; //就一个数据保存在了ServletContext中 名字为username,值为username context.setAttribute("username", username); System.out.println("hello"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); }
}
-
取出数据
public class GetServlet extends HelloServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = getServletContext(); //创建一个ServletContext对象,此对象跟另一个Servlet中的对象是一个 String username = (String) context.getAttribute("username"); //读取对象中的数据,读取出来是Object,强转一下 resp.setContentType("text/html;charset=utf-8"); resp.getWriter().println("名字" + username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } }
-
配置xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true"> <!-- 注册Servlet--> <servlet> <servlet-name>hello2</servlet-name> <servlet-class>com.liu.Servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello2</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping> <servlet> <servlet-name>gt</servlet-name> <servlet-class>com.liu.Servlet.GetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>gt</servlet-name> <url-pattern>/gt</url-pattern> </servlet-mapping> </web-app>
测试结果: 需要先运行HelloServlete,这时context.setAutribute 设置context值为”刘正伟“,这边GetServlet才能获取Context中的值
获得初始化参数
<!--配置一些web应用初始化参数--> <context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:330/mybatis</param-value> </context-param>
获取参数 public class SCDemo extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = getServletContext(); //获得context String url = context.getInitParameter("url"); resp.getWriter().println("url:"+url); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } }
请求转发
getRequestDispatcher
//通过getRequestDispatcher 可以转发到另一个页面,但是是地址栏的地址不会改变
ServletContext context = getServletContext(); // RequestDispatcher requestDispatcher = context.getRequestDispatcher("/SCDemo"); // requestDispatcher.forward(req,resp); //调用方Servlet实现请求转发 context.getRequestDispatcher("/SCDemo").forward(req, resp);//作用同上两句 }
读取资源文件
Prop
<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources>
-
在Java目录下新建properties
-
在resources目录下新建properties
发现都被打包到了同一个路径下:classes,这个路径就是通常的classpath;
需要一个文件流(Properties)
public class ServletDemo05 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //通过getResourceAsStream把获得的资源转换为流,并且参数的第一个 / 不可以删除,表示当前目录下,相对定位 InputStream stream = getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties"); Properties properties = new Properties(); properties.load(stream); String username = properties.getProperty("username"); String password = properties.getProperty("password"); resp.getWriter().println("Username:"+username+"\nPassword:"+password); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
6.6、HttpServletRequest
web服务器接收到客户端的http请求,会针对这个请求分别创建一个代表请求的request对象,代表响应的response对象,
-
我们如果要获取我们客户端请求过来的参数:HttpServletRequest
-
如果要响应客户端信息:HttpServletResponse
简单分类:
负责向浏览器发送数据的方法
ServletOutputStream getOutputStream() throws IOException; PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
//ServletResponse void setCharacterEncoding(String var1); void setContentLength(int var1); void setContentLengthLong(long var1); void setContentType(String var1); void setBufferSize(int var1); //HttpServletResponse 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);
常见应用
-
向浏览器输出消息
-
下载文件
-
获取下载文件的路径
-
下载的文件名
-
设置浏览器能够支持下载我们要下载的文件
-
获取下载文件的输入流
-
创建缓冲区,获取OuputStream对象
-
将FileOutputStream流写入到Buffer中,
-
将缓冲区中的数据输出到客户端
-
案例
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1. 获取下载文件的路径 String path = "D:\\Java\\com.liuzhengwei.JavaWeb-Servlet\\Test3\\src\\main\\resources\\刘正伟.jpg"; System.out.println("下载文件路径:" + path); // 2. 下载的文件名 String filename = path.substring(path.lastIndexOf("//") + 1); // 3. 设置浏览器能够支持下载我们要下载的文件,URLEncoder不让中文名称乱码 resp.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(filename,"utf-8")); // 4. 获取下载文件的输入流 // 5. 创建缓冲区,获取OuputStream对象 FileInputStream fis = new FileInputStream(path); int len; byte[] buffer = new byte[1024]; ServletOutputStream sos = resp.getOutputStream(); // 6. 将FileOutputStream流写入到Buffer中,将缓冲区中的数据输出到客户端 while ((len = fis.read(buffer)) != -1) { sos.write(buffer, 0, buffer.length); } sos.close(); fis.close(); }
-
-
验证码实现
验证怎么来的
-
前端实现
-
后端实现,需要用到java的图片类,生产一个图库
-
public class ImageServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //如何让浏览器5秒刷新一次; resp.setHeader("refresh", "3"); //在内存中创建图片 BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB); //得到图片 Graphics2D g = (Graphics2D) image.getGraphics(); //笔 //设置图片背景颜色 g.setColor(Color.white); g.fillRect(0,0,80,20); //给图片写数据 g.setColor(Color.blue); //设置颜色 g.setFont(new Font(null,Font.BOLD,20)); g.drawString(makeNum(),0,20); //高数浏览器这个请求,用图片的方式打开 resp.setContentType("image/jpg"); //网站存在缓存,不让浏览器缓存,提高效率 resp.setDateHeader("expires",-1); resp.setHeader("Cache-Control","no-cache"); resp.setHeader("Pragma","no-cache"); //把图片写给浏览器 ImageIO.write(image,"jpg",resp.getOutputStream()); } private String makeNum(){ Random random = new Random(); String s = random.nextInt(9999999) + ""; //加一个“”字符串,将int转换为String; StringBuffer sb = new StringBuffer(); for (int i = 0; i < 7-s.length(); i++) { sb.append("0"); } String j = sb.toString() + s; return s; } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
-
实现重定向
-
一个web资源(B)收到客户端请求(A)后,他会通知客户端(A)去访问另一个web资源(C),这个过程叫做重定向
实现场景
-
用户登录
void sendRedirect(String var1) throws IOException;
-
代码测试
public class RedirectServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // resp.setStatus(302); // resp.setHeader("Location","/Test3_war/IS"); resp.sendRedirect("/Test3_war/IS"); }
面试题:
重定向与请求转发的区别:
-
相同点
-
页面都会实现跳转
-
-
不同点
-
请求转发的时候,url不会产生变化
-
重定向的时候,url地址栏会发生变化
-
-
-
6.7、HttpServletResponse
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求中的所有信息会被封装到HttpServletRequest中,通过这个HttpServletRequest的方法可以获得所有客户端信息;
String getAuthType(); Cookie[] getCookies(); long getDateHeader(String var1); String getHeader(String var1); Enumeration<String> getHeaders(String var1); Enumeration<String> getHeaderNames(); int getIntHeader(String var1); String getMethod(); String getPathInfo(); String getPathTranslated();
-
获取前端传递的参数
String getParameter(String s); String[] getParameterVales(String s);
package com.liu.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.util.Arrays; /** * @version 1.0 * @description:TODO * @Author: * @date :2021/9/18 20:53 */ public class logindemo extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //后排接收中文乱码问题 req.setCharacterEncoding("utf-8"); //获取客户端请求的参数 System.out.println("======================================="); String username = req.getParameter("username"); String password = req.getParameter("password"); String[] hobbys = req.getParameterValues("hobbys"); System.out.println(username); System.out.println(password); System.out.println(Arrays.toString(hobbys)); System.out.println("======================================="); System.out.println(req.getContextPath()); //请求转发 注意请求转发不要加前面的路径 如 /r/dello.jsp, 只需要加入文件的相对路径即可 req.getRequestDispatcher("/success.jsp").forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
重定向与请求转发的区别:
-
相同点
-
页面都会实现跳转
-
-
不同点
-
请求转发的时候,url不会产生变化
-
重定向的时候,url地址栏会发生变化
-
重定向需要加入绝对路径( /s1/hello.jsp ),请求转发只需要加入相对路径 ( /hello.jsp )
-
重定向代码 302 请求转发 307
-