老冯笔记Servlet
1.Tomcat服务器
1.1 介绍
软件分为B/S架构与C/S架构,而我们学习的是B/S架构
C/S(客户端/服务端)架构如:QQ、微信、抖音等
特点:
1、使用前必须安装。
2、更新的时候,服务器和客户端必须同时更新。
3、不能跨平台使用
4、客户端和服务器通信采用的是自有协议,相对来说比较安全。B/S(浏览器/服务端)本质上也是C/S,只不过B/S架构的软件,使用浏览器作为客户端。B/S架构软件通过使用浏览器访问网页的形式,来使用软件。比如:淘宝、微博、百度等
特点:
1、软件不需要安装,直接访问指定的网址即可。
2、软件更新时客户端不需要更新。
3、软件可以跨平台,只要系统中有浏览器就可以使用。常见的web应用服务器
Tomcat:Apache开源组织下的,免费
webLogic:是Oracle,收费
webSphere:是IBM的,收费
Nginx:后面要学习的,免费的,一般用于负载均衡
Apache:Apache开源组织下的,免费
web资源
在web应用服务器上可以提供给外界访问的资源就是web资源,比如:html,css,图片,mp3,......
动态资源:web页面中供人浏览的数据,由程序产生的,比如:jsp,php,Servlet
静态资源:web页面中供人浏览的数据,不变的。比如:html,css,图片,mp3,......
1.2 下载与安装Tomcat
Tomcat下载页面地址:
Tomcat与JDK版本对应关系
下载页的说明
1.3 tomcat目录结构说明
bin 存放二进制文件,脚本文件的目录,运行tomcat的脚本也在里面
startup.bat 启动tomcat的批处理文件
shutdown.bat 停止tomcat的批处理文件
conf 配置文件的目录
logging.properties 日志的配置文件
server.xml 核心配置文件
tomcat-users.xml 用户权限配置文件
web.xml 全局的项目默认配置文件,局部只对单个项目有效,全局对所有项目有效
lib tomcat依赖的jar以及tomcat提供的API存放的目录
logs 存放日志的目录
temp 临时文件存放的目录
webapps web应用发布的目录
work tomcat处理jsp的工作目录
1.4 启动tomcat
双击D:\apache-tomcat-8.5.82\bin\startup.bat,就会出现一个黑窗口,如果黑窗口出现闪退情况,证明启动失败。
启动成功后,你可以在浏览器上访问http://localhost:8080/,回出现下图效果
启动出现闪退的常见原因:
1.没有正确的配置JDK的环境变量,如果你没有配置JAVA_HOME这个环境变量,那么就会启动闪退
2.端口冲突,tomcat默认使用的是8080端口,如果8080端口被其他的程序所占用,那么tomcat会启动失败
1.5 eclipse关联tomcat
2.HTTP协议
超文本传输协议(HTTP),它是一个简单的请求-响应协议,它通常运行在TCP之上,它是TCP/IP的子协议。
HTTP协议由HTTP请求和HTTP响应组成。
HTTP协议的特点:
1.基于请求与响应模型的协议,请求和响应是成对出现的。
2.此协议的默认端口为80。
HTTP请求包括:请求行,请求头,请求体
HTTP响应包括:响应行,响应头,响应体
HTTP协议的版本
HTTP/1.0 发送请求的时候,创建一次连接,获得一个web资源,短连接
HTTP/1.1 发送请求的时候,创建一次连接,获得多个web资源,长连接
当你通过域名访问网站的时候,其实就是你利用浏览器向网站所在服务器发送了一次请求
请求头的参数说明:
请求头是客户端发送给服务端的一些信息,这些信息是以key-value形式成对出现的
Accept:浏览器可以支持的MIME类型,文件类型的一种描述方式
Accept-Encoding:浏览器支持的数据压缩格式,这里是gzip
Accept-Language:浏览器支持的语言,zh-CN代表中文
Connection:连接状态,上图的keep-alive代表是连接中的,如果是close就是关闭状态
Content-Length:请求体的长度
Content-Type:请求数据的类型
Host:请求的服务端的主机名和端口
Origin:域名地址
Referer:当前请求来自何处,可以利用它做防盗链
Use-Agent:发送请求的浏览器得相关信息
当网站所在服务器接收到了客户端(浏览器)所发送的请求后,就会响应数据给客户端(浏览器)
3.HTTP常见状态码
状态码在请求行或者响应行中看,比如:HTTP/1.1 200 OK,这里的200就是状态机
常见的状态码:
200 请求成功
302 请求重定向
304 请求资源没有改变,访问本地缓存
404 请求的资源找不到或者压根就不存在
500 服务器内部错误,一般是代码出问题了
更详细的状态码,请参考:https://www.runoob.com/http/http-status-codes.html
4.创建web工程
5.Servlet介绍
Servlet是运行在服务端的Java小程序,是Sun公司提供的一套规范(接口),用于处理客户端请求以及响应的动态资源
Servlet规范主要包含了三大技术点:
Servlet技术
Filter过滤器技术
Listener监听器技术
一般我们创建一个类然后继承HttpServlet,并覆盖doGet和doPost方法。
你在eclipse中创建了一个名叫Demo的web工程,然后你Run on Server后,项目就会被发布到Tomcat安装目录下的webapps目录里面。webapps目录里面的工程的结构:
Demo文件夹
META-INF文件夹
WEB-INF文件夹
classes文件夹(存放着你写的Java代码编译出来的字节码文件)
、 lib文件夹(存放第三方依赖Jar包的,放在这里的Jar会自动导入,不用build path了)
web.xml文件(配置Servlet的配置文件)
一些静态资源......
注意:WEB-INF目录是一个受保护的目录。不能被外界直接访问
6.创建Servlet的方式
7.设置Servlet的模板
模板代码
package ${enclosing_package};
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ${primary_type_name} extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("DarkSnow...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
你在新建完Servlet之后,清除掉原来的内容,然后输入servlet,再按alt + /即可生成代码
8.Servlet的生命周期(重要)
Servlet接口中的方法:
1.public void init(ServletConfig config) 默认只执行一次
(1)什么时候执行?
Servlet对象创建的时候执行
(2)Servlet对象什么时候创建?
默认第一次访问Servlet时创建
(3)ServletConfig代表的是该Servlet对象的配置信息(web.xml的信息)
2.public void service(ServletRequest req, ServletResponse res)
(1)什么时候执行?
每次请求的时候都会执行(每次访问的时候)
(2)ServletRequest
代表请求对象,内部封装了HTTP请求的数据信息
(3)ServletResponse
代表响应对象,内部封装了HTTP响应的数据信息
3.public void destroy()
(1)什么时候执行?
Servlet销毁的时候执行
(2)Servlet什么时候销毁?
服务器停止时销毁
9.ServletConfig对象
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Demo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<description></description>
<display-name>MyServletSoft</display-name>
<servlet-name>MyServletSoft</servlet-name>
<servlet-class>com.darksnow.MyServletSoft</servlet-class>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://test</param-value>
</init-param>
<init-param>
<param-name>name</param-name>
<param-value>darksnow</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>MyServletSoft</servlet-name>
<url-pattern>/darksnow</url-pattern>
</servlet-mapping>
</web-app>
我们在public void init(ServletConfig config)中能够看到有一个ServletConfig对象,此对象是Tomcat帮你创建的,所以你可以直接用。
package com.darksnow;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServletSoft extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
@Override
public void destroy() {
}
@Override
public void init(ServletConfig config) throws ServletException {
//ServletConfig对象是Servlet配置对象
//获取了web.xml中的<servlet-name>MyServletSoft</servlet-name>
String servletName = config.getServletName();
System.out.println(servletName);
//根据Servlet的初始化参数name(<param-name>url</param-name>)来获取对应value
String initParameter = config.getInitParameter("url");
System.out.println(initParameter);
//获取Servlet的所有初始化参数
Enumeration<String> initParameterNames = config.getInitParameterNames();
while(initParameterNames.hasMoreElements()) {
String key = initParameterNames.nextElement();
String value = config.getInitParameter(key);
System.out.println(key + "---" + value);
}
//通过ServletConfig可以获得ServletContext对象,也叫Servlet上下文对象
ServletContext servletContext = config.getServletContext();
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("DarkSnow...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
10.web.xml的配置
<!--
欢迎页面的设置
假设我访问http://localhost:8080/Demo/
如果Demo项目里面有下面所示名称的文件,那么就可以有页面显示
否则报404
-->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
Servlet默认第一次访问时创建,但是加上下面这个配置,Servlet对象就会在Tomcat服务器启动时就创建。
数字代表优先级,数字越小优先级越高!
注意:该配置项写在<init-param>配置后面,如果没有配置<init-param>,则写在<servlet-class>配置后面
<load-on-startup>2</load-on-startup>
11.ServletContext
ServletContext也叫做上下文对象。
如何获取ServletContext对象?
1.通过ServletConfig对象获取(具体参考ServletConfig的笔记)
2.通过this.getServletContext();的方式获取,
主要是因为你写的Servlet会从GenericServlet将getServletContext方法给继承过来。
本质上还是通过ServletConfig对象调用的。
注意:如果你是通过第二种方式获取ServletContext对象,要不你就不重写init方法,要不你就重写不带参数的init方法,如果你要从写init(ServletConfig config)方法,那么在你的方法体中的第一行一定要写上super.init(config); 否则会报空指针异常
ServletContext的作用:
(1)可以获取web应用的全局初始化参数,在web.xml中配置(此配置可以写在web-app标签内部的任意子位置)
<context-param>
<param-name>driver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</context-param>
// 获取ServletContext上下文对象
ServletContext servletContext = this.getServletContext();
// 获取全局的初始化参数
String value = servletContext.getInitParameter("driver");
System.out.println(value);
context-param与init-param的区别:
context-param中设置的初始化值可以被所有的Servlet获取
init-param中设置的初始化值只能被单个Servlet获取
(2)可以获取web应用中任何资源的绝对路径
getRealPath()里面传入的参数为你要获取的资源的相对路径
相对于该web应用
(3)ServletContext的域对象
存储数据的区域对象
ServletContext域对象的作用范围:
作用于整个Web应用,也就意味着这个web应用下任何的Servlet都可以向ServletContext域中存取数据,以达到一个数据共享的效果。
相关操作:
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletOne extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
//向ServletContext域中存储key-value(键值对)形式的数据
servletContext.setAttribute("name", "Mark");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletTwo extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
//通过key来获取ServletContext域对象中存储的值
String value = (String) servletContext.getAttribute("name");
response.getWriter().write(value);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletThree extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
//通过key来移除ServletContext域对象中存储的值
servletContext.removeAttribute("name");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
12.response响应对象
12.1 设置响应的状态码
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置状态码
response.setStatus(501);
response.getWriter().write("DarkSnow...");
}
(2)设置响应头
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//添加了一个响应头
//response.addIntHeader("count", 1314520);
//response.addDateHeader("MyDate", new Date().getTime());
//修改响应头
//response.setIntHeader("", 0);
//response.setDateHeader("", 0L);
//上面的只是给你用来体验的,下面才是真正你要用的
response.addHeader("MyServer", "Tomcat");
response.setHeader("Content-Length", "19");
//总结:带add是添加,带set的是修改
}
定时页面跳转
package com.darksnow.api;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RefreshServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置刷新时间的头 里面的5代表5秒
response.setHeader("refresh", "5;url=http://www.baidu.com");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
window.onload = function(){
var time = 4;
var secondElement = document.getElementById("second");
var timer = setInterval(function(){
secondElement.innerHTML = time;
--time;
//如果时间为0了,定时器任务就应该停止
if(time == 0){
clearInterval(timer);
location.href = "http://www.baidu.com";
}
},1000);
}
</script>
</head>
<body>
恭喜您,注册成功,<span style="color:red" id="second">5</span>秒后跳转,如果不跳转点击<a href="http://www.baidu.com">这里</a>!
</body>
</html>
12.2 可以做重定向
//加上访问MyServletOne重定向到MyServletTwo
public class MyServletOne extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("one...");
//1.设置状态码为302
//response.setStatus(302);
//2.设置响应头Location(此响应头的值的格式:/项目名/资源名)
//response.setHeader("Location", "/response_demo/two");
//HttpServletResponse对象中内部封装了一个用于重定向的方法 项目名+资源名
response.sendRedirect("/response_demo/two");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
public class MyServletTwo extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("two...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
重定向的特点:
(1) 请求了两次
(2) 地址栏发生了变化
12.3 页面内容打印
package com.darksnow.api;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TextServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 设置response缓冲区的编码 写在第一行
* 但是我们发现设置了也还是会乱码,因为我们的系统是中文环境的,所以浏览器默认用的是GB2312编码
* 所以我们不用此方法
*/
//response.setCharacterEncoding("UTF-8");
//我们可以通过Content-Type响应头来告诉客户端我们到底使用什么编码
//response.setHeader("Content-Type", "text/html;charset=UTF-8");
//最终解决方案:Servlet给你提供了相关API(原理和设置响应头一样)
response.setContentType("text/html;charset=UTF-8");
//向页面写一个内容
//这里本质上用的是I/O流
response.getWriter().write("<h1>DarkSnow...</h1>"); //显示正常
/*
* 中文乱码原因:
* 内容其实在response缓冲区内,但是这个缓冲区的默认编码为ISO-8859-1
* 然而这编码表中没有中文,所以就乱码了。
*/
response.getWriter().write("真热啊..."); //乱码
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
12.4 getOutputStream方法案例
package com.darksnow.api;
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
* 写一张图片
*/
public class ImgServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这是一个字节流,该字节流可以向response缓冲区写入字节,再由tomcat引擎将字节的内容与其他信息封装成HTTP响应返回给浏览器
ServletOutputStream out = response.getOutputStream();
//首先得获取服务器上的图片路径 ---> 绝对路径
String realPath = this.getServletContext().getRealPath("22.png");
FileInputStream in = new FileInputStream(realPath);
int len = 0;
byte[] buffer = new byte[1024];
while((len = in.read(buffer))>0) {
out.write(buffer,0,len);
}
in.close();
//查看response缓存区的大小
System.out.println(response.getBufferSize()); //8192Byte 1KB=1024Byte
//设置response缓冲区的大小
//response.setBufferSize(10240);
/*
* 关于getOutputStream的相关注意事项
* 1.response获取的流不用手动关闭,tomcat会去关闭
* 2.getOutputStream方法与getWriter方法不能同时调用
* 3.response缓冲区默认大小为8KB,它存在自动扩容机制,无需你关心
*/
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
12.5 文件下载案例
前端
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>欢迎来到资源下载小站</h1>
<a href="/response_demo/download/apache-tomcat-8.5.82.zip">下载Tomcat源码</a>
<br>
<a href="/response_demo/download/girl.png">下载美女图片</a>
<br>
<a href="/response_demo/download/1.txt">下载文档</a>
<br>
<a href="/response_demo/download?filename=girl.png">真正的下载美女图片一</a>
<br>
<a href="/response_demo/download?filename=美女.jpeg">真正的下载美女图片二</a>
<br>
<a href="/response_demo/download?filename=1.txt">真正的下载文档</a>
</body>
</html>
后端
package com.darksnow.download;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DownloadServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取要下载的文件的名称
String filename = request.getParameter("filename");
//要下载的文件的类型 客户端通过文件的MIME类型去区分文件类型(Tomcat安装目录的conf/web.xml里面去看)
response.setContentType(this.getServletContext().getMimeType(filename));
//获取要下载的文件的绝对路径
String path = this.getServletContext().getRealPath("download/" + filename);
//解决文件名中文乱码问题,不同浏览器解决方式还不一样
//怎么去根据不同的浏览器设置不同的解决方案呢?怎么去区分浏览器,根据浏览器的内核来区分
//获取请求头中的User-Agent
String agent = request.getHeader("User-Agent");
//根据不同的浏览器进行不同的编码
String filenameEncoder = "";
if(agent.contains("AppleWebKit")){
//适用于谷歌浏览器和微软的Edge浏览器
filenameEncoder = URLEncoder.encode(filename,"UTF-8");
}else {
//适用于火狐浏览器
filenameEncoder = new String(filename.getBytes("GBK"),"ISO-8858-1");
}
//你要告诉客户端,文件不是直接取解析,而是以附件的形式打开(下载)
response.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoder);
//获得该文件的输入流
InputStream in = new FileInputStream(path);
//获得文件输出流 --- 通过response获得输出流
ServletOutputStream out = response.getOutputStream();
//边读边写
int len = 0;
byte[] buffer = new byte[1024];
while((len = in.read(buffer)) > 0) {
out.write(buffer,0,len);
}
//关闭资源
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
12.6 图片验证码案例
servlet
package com.darksnow.check;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 验证码生成程序
*/
public class CheckImgServlet extends HttpServlet {
// 集合中保存所有成语
private List<String> words = new ArrayList<String>();
@Override
public void init() throws ServletException {
// 初始化阶段,读取new_words.txt
// web工程中读取 文件,必须使用绝对磁盘路径
// new_words.txt存放在webapp目录下,此文件的内容为一行一个四字成语
String path = getServletContext().getRealPath("new_words.txt");
try {
BufferedReader reader = new BufferedReader(new FileReader(path));
String line;
while ((line = reader.readLine()) != null) {
words.add(line);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 禁止缓存
// response.setHeader("Cache-Control", "no-cache");
// response.setHeader("Pragma", "no-cache");
// response.setDateHeader("Expires", -1);
int width = 120;
int height = 30;
// 步骤一 绘制一张内存中图片
BufferedImage bufferedImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
// 步骤二 图片绘制背景颜色 ---通过绘图对象
Graphics graphics = bufferedImage.getGraphics();// 得到画图对象 --- 画笔
// 绘制任何图形之前 都必须指定一个颜色
graphics.setColor(getRandColor(200, 250));
graphics.fillRect(0, 0, width, height);
// 步骤三 绘制边框
graphics.setColor(Color.WHITE);
graphics.drawRect(0, 0, width - 1, height - 1);
// 步骤四 四个随机数字
Graphics2D graphics2d = (Graphics2D) graphics;
// 设置输出字体
graphics2d.setFont(new Font("宋体", Font.BOLD, 18));
Random random = new Random();// 生成随机数
int index = random.nextInt(words.size());
String word = words.get(index);// 获得成语
System.out.println(word);
// 定义x坐标
int x = 10;
for (int i = 0; i < word.length(); i++) {
// 随机颜色
graphics2d.setColor(new Color(20 + random.nextInt(110), 20 + random
.nextInt(110), 20 + random.nextInt(110)));
// 旋转 -30 --- 30度
int jiaodu = random.nextInt(60) - 30;
// 换算弧度
double theta = jiaodu * Math.PI / 180;
// 获得字母数字
char c = word.charAt(i);
// 将c 输出到图片
graphics2d.rotate(theta, x, 20);
graphics2d.drawString(String.valueOf(c), x, 20);
graphics2d.rotate(-theta, x, 20);
x += 30;
}
// 将验证码内容保存session
request.getSession().setAttribute("checkcode_session", word);
// 步骤五 绘制干扰线
graphics.setColor(getRandColor(160, 200));
int x1;
int x2;
int y1;
int y2;
for (int i = 0; i < 30; i++) {
x1 = random.nextInt(width);
x2 = random.nextInt(12);
y1 = random.nextInt(height);
y2 = random.nextInt(12);
graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
}
// 将上面图片输出到浏览器 ImageIO
graphics.dispose();// 释放资源
//将图片写到response.getOutputStream()中
ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
/**
* 取其某一范围的color
*
* @param fc
* int 范围参数1
* @param bc
* int 范围参数2
* @return Color
*/
private Color getRandColor(int fc, int bc) {
// 取其随机颜色
Random random = new Random();
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
}
前端页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
function checkImg(obj){
obj.src = "/response_demo/checkImg?time=" + new Date().getTime();
console.log(obj);
}
</script>
</head>
<body>
<form action="#" method="post">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
验证:<input type="text" name="check">
<!-- 这里的this(当前对象)其实就是img这个文档对象 -->
<img onclick="checkImg(this)" src="/response_demo/checkImg"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
12.7 登陆案例
需求
做一个登录功能,输入账号密码登录,如果登录成功(验证数据库),那么就在页面显示"恭喜xxx登陆成功,这是您第x次成功登录!",如果登录失败,那么就在页面上显示"抱歉您的账号或密码错误"
前端页面login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/response_demo/login" method="post">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
C3P0工具类
package com.darksnow.utils;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* C3P0的相关工具类
*
* 你可以把数据源看成是连接池
*/
public class C3P0Util {
//私有构造函数,不让其创建对象
private C3P0Util() {
}
/*
* 这里ComboPooledDataSource的构造函数传入了一个"darksnow"的字符串参数,那么就会去使用c3p0-config.xml配置文件中的
* <named-config name="darksnow">下面的配置
*
* 如果ComboPooledDataSource用的是无参构造,那么就会去使用c3p0-config.xml配置文件中的
* <default-config>下面的配置
*/
// private static ComboPooledDataSource dataSource = new ComboPooledDataSource("darksnow");
private static ComboPooledDataSource dataSource = new ComboPooledDataSource(); //设置数据源
/**
* javax.sql.DataSource 是Java提供的原生接口,各大厂商实现连接池需要实现这个接口
* 获取数据源的方法
*/
public static DataSource getDataSource() {
return dataSource;
}
/**
* 获取连接的方法
*/
public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
User实体类
package com.darksnow.bean;
/**
* 这就是JavaBean 对象实体
* 此对象的成员变量名和数据库的字段名是一一对应的,数据类型也是对应的
*
* 一个实体对象基本上对应一条数据
*/
public class User {
private int id;
private String username;
private String password;
private String name;
public User() {
}
public User(int id, String username, String password, String name) {
this.id = id;
this.username = username;
this.password = password;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + ", name=" + name + "]";
}
}
具体处理业务的Servlet
package com.darksnow.login;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Properties;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import com.darksnow.bean.User;
import com.darksnow.utils.C3P0Util;
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
@Override
public void init() throws ServletException {
try {
//加载count.properties文件得到一个字节输入流
InputStream in = LoginServlet.class.getClassLoader().getResourceAsStream("count.properties");
//创建Properties对象
Properties properties = new Properties();
//通过字节输入流将count.properties文件的内容加载到Properties对象中
properties.load(in);
//通过key从Properties对象中获取对应的值
int count = Integer.parseInt(properties.getProperty("count"));
//创建ServletContext上下文对象,并且向域中存入登陆次数
this.getServletContext().setAttribute("count", count);
} catch (IOException e) {
e.printStackTrace();
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//处理response缓冲区中文乱码问题
response.setContentType("text/html;charset=UTF-8");
//登录页面输入完账号密码,然后点击登录以后,就将账号密码数据提交给了LoginServlet来处理
//所以我们首先需要获取,前端页面提交过来的数据(账号密码)
String username = request.getParameter("username");
String password = request.getParameter("password");
//去数据库中校验账号密码是否正确
QueryRunner queryRunner = new QueryRunner(C3P0Util.getDataSource());
String sql = "select * from t_user where username=? and password=?";
try {
User user = queryRunner.query(sql, new BeanHandler<User>(User.class),username,password);
//根据SQL的返回结果来显示不同的提示信息
if(user != null) {
//User对象不为空意味着账号密码正确
//创建ServletContext上下文对象
ServletContext servletContext = this.getServletContext();
//获取ServletContext域中已经存在的次数
int count = (int) servletContext.getAttribute("count");
//登陆成功,登陆此数加一
count++;
response.getWriter().write("<h1>恭喜" + user.getName() + "登陆成功,这是您第" + count + "次成功登录!</h1>" );
//将更新后的次数写回ServletContext域中
servletContext.setAttribute("count", count);
}else {
response.getWriter().write("<h1 style='color:red'>抱歉~您的账号或密码错误~</h1>");
}
}catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void destroy() {
try {
//tomcat服务器停止会执行此方法,所以在这里将ServletContext域中存储的值写入到count.properties中
//通过key从ServletContext域中获取对应的值
String count = String.valueOf(this.getServletContext().getAttribute("count"));
//创建Properties对象
Properties properties = new Properties();
//给Properties对象设置key,value
properties.setProperty("count", count);
//将Properties对象的内容写入到count.properties文件中
properties.store(new FileOutputStream(LoginServlet.class.getClassLoader().getResource("count.properties").getPath()), count);
} catch (IOException e) {
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
c3p0-config.xml配置文件,放在src/main/java下
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///darksnow</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<named-config name="darksnow">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///darksnow</property>
<property name="user">root</property>
<property name="password">root</property>
</named-config>
</c3p0-config>
count.properties配置文件,放在src/main/java下
count=0
数据库截图
13.request请求对象
13.1 请求与响应图解
13.2 获取请求行的内容
form.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/request_demo/line" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<button>登陆</button>
</form>
</body>
</html>
servlet代码
package com.darksnow.api;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LineServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//request对象获取请求的方式
String method = request.getMethod();
System.out.println("method:" + method);
//request对象获取请求的资源相关的内容
String requestURI = request.getRequestURI();
StringBuffer requestURL = request.getRequestURL();
System.out.println("uri:" + requestURI); // /request_demo/line
System.out.println("url:" + requestURL); // http://localhost:8080/request_demo/line
//request对象获取web应用的名称,也就是你的工程名(格式:/web应用名) 在JavaWeb中常用
String contextPath = request.getContextPath();
System.out.println(contextPath);
//request对象获取地址后的参数的字符串,get请求方式就可以获取到,post会获取到null
String queryString = request.getQueryString();
System.out.println(queryString);
//request对象获取客户端的信息,获取访问者的IP地址,但是此API与请求行无关。
String ip = request.getRemoteAddr();
System.out.println(ip);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
13.3 获取请求头的内容
referer_test.html前端
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="/request_demo/header">访问HeaderServlet资源</a>
</body>
</html>
servlet代码
package com.darksnow.api;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HeaderServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//request获取指定的请求头
String header = request.getHeader("User-Agent");
System.out.println(header);
System.out.println("----------------------");
//获得所有的请求头的名称
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
String headerNameValues = request.getHeader(headerName);
System.out.println(headerName + ": " +headerNameValues);
}
System.out.println("----------------------");
/*
* 请求头referer的作用:能够让你知道此次访问的来源
* 点击referer_test.html前端页面中的a标签可以看到请求头中包含
* Referer: http://localhost:8080/request_demo/referer_test.html
*
* 这个头可以做防盗链
*/
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
13.4 防盗链实现
news.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>DarkSnow热搜新闻网~</title>
</head>
<body>
<a href="/request_demo/referer">独家新闻:震惊,某一明显竟然在酒店做出这样的事儿!</a>
</body>
</html>
servlet代码
package com.darksnow.exp;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RefererServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
//对该新闻的来源进行判断
String header = request.getHeader("Referer");
if(header != null && header.contains("http://192.168.0.62:8080")) {
response.getWriter().write("看到独家新闻是不是点进来了?其实明星也没干啥,老营销号了~");
}else {
response.getWriter().write("哼~ 盗链狗,可耻!!!");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
13.5 获取请求参数
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/request_demo/content" method="post">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
爱好:<input type="checkbox" name="hobby" value="zq">足球
<input type="checkbox" name="hobby" value="lq">篮球
<input type="checkbox" name="hobby" value="ppq">乒乓球
<br><button>登陆</button>
</form>
</body>
</html>
servlet代码
package com.darksnow.api;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ContentServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取单个表单值
String username = request.getParameter("username");
System.out.println("账号:" + username);
System.out.println("-----------------------");
//获取所有的请求参数的名称
Enumeration<String> parameterNames = request.getParameterNames();
while(parameterNames.hasMoreElements()) {
System.out.println(parameterNames.nextElement());
}
System.out.println("-----------------------");
//获取所有请求参数的名称与其对应的值
Map<String, String[]> parameterMap = request.getParameterMap();
Set<Entry<String, String[]>> entrySet = parameterMap.entrySet();
for(Entry<String, String[]> entry : entrySet) {
String key = entry.getKey(); //请求参数的名称
String[] value = entry.getValue(); //请求参数对应的值
System.out.println(key + "---" + Arrays.toString(value));
}
System.out.println("-----------------------");
//以上操作对post请求和get请求都有效
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
13.6 请求转发
图解
ServletOne代码
package com.darksnow.exp;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletOne extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 前面我们学习了ServletContext域对象,现在我告诉你,request对象其实也是一个域对象
* 所以它具备了同样的方法:
* 向域以key-value的形式存储数据
* request.setAttribute(String name,Object obj);
* 通过key从域中获取相应的value
* request.getAttribute(String name);
* 通过key将域中相应的key-value数据给移除调
* request.removeAttribute(String name);
*
* request域的作用范围只在一次请求中有效
*
* 因此,一般request域和请求转发搭配使用,先设置域,再转发。
* (本质上我们这么玩儿就是为了将一个Servlet中的数据带到另外一个Servlet中,且保持一次请求)
*/
request.setAttribute("name", "darksnow");
//转发在内部发生的,不需要写项目名,直接写资源就行了。 设置转发到哪里去!
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/two");
//调用转发方法
requestDispatcher.forward(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
ServletTwo代码
package com.darksnow.exp;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletTwo extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
String value = (String) request.getAttribute("name");
response.getWriter().write("ServletTwo执行了..." + value);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术