一、基本概念
1.1 前言
web开发:
- 静态web
- html、css
- 提供给所有人看的数据不会发生改变
- 动态web
- 提供给所有人看的数据始终会发生变化
- 技术栈:Servlet/JSP,ASP,PHP
在java中,动态web资源的开发的技术统称为javaweb
1.2 web应用程序
web应用程序:可以提供浏览器访问的程序
- a.html,b.html….多个web资源,这些web资源可以被外界访问,对外界提供服务
- URL
- 这个统一的web资源会被放在同一个文件夹下,web应用程序 —> Tomcat:服务器
- 一个人web应用有多个部分组成(静态web、动态web)
- html、css、js
- jsp、servlet
- java程序
- jar包
- 配置文件
1.3 静态web
- *.htm , *.html ,如果服务器上存在,我们就可以读取
- 静态web存在的缺点
- web页面无法动态更新
- 轮播图,点击特效:伪动态
- javascript
- VBscript
- web页面无法动态更新
1.4 动态web
页面会动态展示:“web页面展示的效果因人而异”
缺点:
- 加入服务器的动态web资源出现了错误,我们需要重新编写我们的后台程序,重新发布
- 停机维护
优点:
- 可以动态更新web页面
- 可以与数据库交互(数据持久化:注册、商品信息等)
- JDBC
二、web服务器
2.1 技术讲解
ASP:
微软:国内最早流行的就是asp
在html中嵌入了VB的脚本代码,ASP+COM
在ASP开发中,基本上一个页面都有几千行的业务代码,维护成本高
C#
IIS服务器
PHP:
- php开发速度快,功能强大,跨平台,代码很简单
- 无法承载大访问量的情况(局限性)
JSP/Servlet:
- sun公司(被Oracle收购)主推的B/S架构(B/S:浏览器和服务器;C/S:客户端和服务器)
- 基于java语言(所有的大公司或者一些开源的组件都是用java写的)
- 可以承载三高问题(高并发、高可用、高性能)带来的影响
2.2 web服务器
服务器是一种被动操作,用来处理用户的请求和给用户一些响应信息
IIS:
微软的(ASP);Windows中自带的
Tomcat:
Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为比较流行的Web 应用服务器。
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,它是最佳的选择。实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。
诀窍是,当配置正确时,Apache 为HTML页面服务,而Tomcat 实际上运行JSP 页面和Servlet。Tomcat最新版本为10.0.14。
apache目录:
bin:存放一些执行文件(*.exe),可以直接使用
include:包含很多c语言的文件(跟系统底层有关)
jre:运行环境
- lib:存放jar包(平时用的java.lang)
- java核心jar包是rt.jar
三、Tomcat
3.1 文件夹作用
bin:启动、关闭的脚本文件
conf:配置文件
lib:依赖的jar包
logs:日志文件
temp:存放一些临时文件
webapps:存放网站的文件
-ROOT
-MyWeb
WEB-INF
- classes:java程序 - lib:web应用所依赖的jar包 - web.xml:网站配置文件
index.html:默认的首页
static
- css - js - img
work:工作目录
启动、关闭Tomacat:
进入bin目录,双击 startup.bat 启动 Tomcat,shutdown.bat 关闭Tomcat
访问 127.0.0.1:8080 默认端口为 8080
核心配置文件(conf/server.xml)
例如配置主机的名字:
Engine 组件是 Servlet 处理器的一个实例,即 Servlet 引擎
name 是引擎的逻辑名称,在日志和错误消息中会碰到
defaultHost 是指定默认主机,分配主机来执行用户请求
Host 组件位于 Engine 容器中用于接收请求并进行相应处理的虚拟主机。通过该容器可以运行 Servlet 或者 JSP 来处理请求。
每个Host容器都是一个虚拟主机,对应于不同的域名
在 Engine 中定义的多个虚拟主机的Host主机名称中至少要有一个跟 defaultHost 定义的主机名称同名
当我们浏览器访问一个域名(a.com)时,首先会查找本地的host文件来解析域名,若是解析到了127.0.0.1(本地主机),就会查找本地的web服务,找到tomcat之后,tomcat读取并搜索server.xml,找到127.0.0.1对应的虚拟主机Host;
<Engine name="Catalina" defaultHost="localhost"> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> #这样能正常访问tomcat页面 <Engine name="Catalina" defaultHost="tp.io"> <Host name="tp.io" appBase="webapps" unpackWARs="true" autoDeploy="true"> #正常访问(host文件已经将 tp.io 解析到 127.0.0.1)
- 假如没有对应的虚拟主机Host,就会将请求发往默认的虚拟主机(Engine default)
<Engine name="Catalina" defaultHost="localhost"> <Host name="tp.io" appBase="webapps" unpackWARs="true" autoDeploy="true"> #正常访问(host文件已经将 tp.io 解析到 127.0.0.1) <Engine name="Catalina" defaultHost="tp.io"> <Host name="a.com" appBase="webapps" unpackWARs="true" autoDeploy="true"> #无法访问 (host文件已经将 tp.io 解析到 127.0.0.1)
上面四个例子可以发现,Engine的defaultHost需要直接定义为主机的名字(没有映射关系)
而Host的name可以是映射关系(host文件)中的主机名字
最好Host的name 和 defaultHost 至少有一对名字保持一致
- 假如既没有对应的虚拟主机Host,默认的虚拟主机又不存在,就前往下一步
若是没解析到,就会去访问DNS解析,最后结束
默认端口号:
- tomcat:8080
- mysql:3306
- http:80
- https:443
四、Servlet
4.1 简介
- servlet是sun公司开发动态web的一门技术
- sun在这些API中提供一个接口叫做:Servlet,如果你想开发一个Servlet小程序,只需要完成两个小步骤:
- 编写一个类,实现Servlet接口
- 把开发好的java类部署到web服务器中
把实现了Servlet接口的java程序叫做:Servlet
4.2 HelloSevlet
Servlet接口sun公司有两个默认的实现类:HttpServlet、GenericServlet
1、关于Maven父子工程的理解:
在父项目中:
<modules>
<module>servlet-01</module>
</modules>
在子项目中:
<parent>
<artifactId>Maven01</artifactId>
<groupId>com.my.maven01</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
父项目中的jar包,子项目可以直接使用
2、编写一个Servlet程序
- 编写一个普通类
- 实现Servlet接口,这里我们直接继承HttpServlet
查看HttpServlet源码:
public abstract class HttpServlet extends GenericServlet implements Serializable{
}
//HttpServlet 又继承了 GenericServlet
查看GenericServlet源码:
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
}
//GenericServlet 又继承了 Servlet
编写的程序:
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ServletOutputStream outputStream = resp.getOutputStream(); //两个方法都可以
PrintWriter writer = resp.getWriter(); //响应流
writer.print("hello sevlet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
3、编写Servlet的映射
为什么:我们写的是java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以需要在web服务中注册我们写的servlet
还需要给浏览器一个访问的路径(web.xml):
<!-- 注册servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.my.servlet.HelloServlet</servlet-class>
</servlet>
<!-- servlet的请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
4、配置tomcat
注意:配置项目发布的路径就可以了(我这里设置为s1)
5、启动测试
访问http://localhost:8080/s1/hello
五、Servlet原理
5.1 图解
5.2 Mapping 问题
1、一个Servlet可以指定一个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
2、一个Servlet可以指定多个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
3、一个Servlet可以指定通用路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
4、一个Servlet可以指定后缀和前缀(前面不能加请求路径,例如:/*.do 或者 /hello/*.do
)
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.siii0</url-pattern>
</servlet-mapping>
这样访问也是符合的:/hello/123.siii0
只要以 .siii0 结尾就行
5、默认请求路径(少用)
<!-- 不会进入根目录的index页面,直接进入我们写的页面,index页面被覆盖了 -->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
5.2.1 优先级问题
指定了固有的固有的映射路径优先级最高,没有找到就走默认的处理请求
因此就可以定义自己的404页面
public class Error extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.print("<h1>404\n没有找到该页面哦</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.my.servlet.Error</servlet-class>
</servlet>
<!-- servlet的请求路径 设置为默认路径 访问未注册的servlet就会返回该页面 404
当然初始页面也是该页面 -->
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
最终效果:
http://localhost:8080/s1/
http://localhost:8080/s1/hello
http://localhost:8080/s1/hellod11xsf
5.3 ServletContext
web容器启动的时候,它为每个web程序都创建一个ServletContext对象,它代表了当前的web应用
5.3.1 共享数据
setAttribute() 方法设置变量,getAttribute() 获取变量
一个Servlet中保存的数据,可以被另外一个Servlet拿到
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = "siii0";
context.setAttribute("username",username); //在ServletContext中设置一个键值对
resp.getWriter().print("hello");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = (String) context.getAttribute("username");
//设置编码,防止中文乱码
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
resp.getWriter().print("名字"+username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<!-- web.xml 设置映射关系 -->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.my.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getname</servlet-name>
<servlet-class>com.my.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getname</servlet-name>
<url-pattern>/getname</url-pattern>
</servlet-mapping>
这样就能实现不同的Servlet之间的访问:
(未设置ServletContext对象中的值时)
(设置了ServletContext对象中的值后)
5.3.2 获得初始化参数
web.xml 中设置初始化参数,利用getInitParameter() 方法读取
该页面映射到了 /gp
<!-- 设置初始化参数 -->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
5.3.3 请求转发
利用 getRequestDispatcher() 方法获取 RequestDispatcher 对象
利用该对象的 foward() 方法转发请求
该页面映射到了 /sd4
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
//获取目标请求 转发的对象
//RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");
//requestDispatcher.forward(req,resp); //转发请求
context.getRequestDispatcher("/gp").forward(req,resp); //和上面一样
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
可以看到 /sd4
和 /gp
显示的输出是一样的,但是请求转发不同于重定向:
请求转发:A向B发出请求,B然后向C发出请求,最后A从B得到C的响应
重定向:A向B发出请求,B让A向C发出请求,最后A直接得到C的响应
5.3.4 读取资源文件
利用getResourceAsStream() 方法将文件转换为文件流
利用 Properties 对象的 getProperty() 方法获取文件流中的参数
Properties
- 在java目录下新建properties
- 在resources目录下新建properties
发现:都被打包到了同一路径下:classes,我们俗称这个路径为:classpath
思路:需要一个文件流
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
//将资源文件转换为流,获取一个流对象
InputStream is = context.getResourceAsStream("/WEB-INF/classes/b.properties");
//导入流对象后就可以读取其中的数据
Properties properties = new Properties();
properties.load(is);
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);
}
}
#b.properties
username=root
password=123456
5.4 HttpServletResponse
web服务器接收到客户端的http请求的时候,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的HttpServletResponse对象
- 如果要获取客户端请求过来的参数,找HttpServletRequest
- 如果要向客户端响应一些信息,找HttpServletResponse
5.4.1 简单分类
- 负责向客户端发送数据的方法
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
- 负责向浏览器发送响应头的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentType(String var1);
//等等
- 响应状态码(常量)
int SC_CONTINUE = 100;
int SC_OK = 200;
int SC_USE_PROXY = 305;
int SC_NOT_FOUND = 404;
int SC_INTERNAL_SERVER_ERROR = 500;
//等等的所有状态码
5.4.2 常见应用(下载本地文件)
- 向浏览器输出信息
- 下载文件
1、获取下载文件的路径
2、下载文件的文件名
3、让浏览器支持下载我们需要的东西
4、获取下载文件的输入流
5、创建缓冲区
6、获取OutputStream对象
7、将FileOutputStream流写入到buffer缓冲区
8、使用OutputStream将缓冲区的数据输出到客户端
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取下载文件的路径
String realpath="D:\\IDEA-pros\\project\\Maven01\\response-downloadfile\\src\\main\\resources\\1.jpg";
String realpath2=this.getServletContext().getContextPath();
String realpath3=this.getServletContext().getRealPath("1.jpg");
System.out.println(realpath2);
System.out.println(realpath3);
// 2、下载文件的文件名(windows路径 \)
String filename=realpath.substring(realpath.lastIndexOf("\\")+1);
// 3、让浏览器支持下载我们需要的东西
//编码防止中文文件名可能出现乱码
resp.addHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(filename,"utf-8"));
// 4、获取下载文件的输入流
FileInputStream in = new FileInputStream(realpath);
// 5、创建缓冲区
int len;
byte[] buffer = new byte[1024];
// 6、获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
// 7、将FileInputStream流写入到buffer缓冲区
// 8、使用OutputStream将缓冲区的数据输出到客户端
while((len=in.read(buffer))>0){
out.write(len);
}
out.close();
in.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
这里是将文件路径写死了,还有其他方法
期间遇到一个问题:出现了0字节下载
发现问题出在写入缓冲区操作的while判断上
我一开始写的是 !=0
改为 >0 就正常了
这是输出的内容,之所以不能用getRealPath
就是这个原因:他获取的是tomcat的web项目的路径
//另外一种获取文件名的方式
//因为图片被存储在了web项目的classes目录中,所以可以用这种方法
String realpath = this.getClass().getClassLoader().getResource("1.jpg").getPath();
//这里的路径是java给的路径,所以是 / ,还需要改
String filename=realpath.substring(realpath.lastIndexOf("/")+1);
5.4.3 验证码功能
- 前端实现(js)
- 后端实现,需要用到java的图片类,生成一个图片
1、让浏览器三秒刷新一次(自动刷新验证码)
2、在内存中创建一个图片(刚开始是空的)
3、得到图片(创建画笔类设置图片背景颜色,字体颜色,最后给图片写数据)
4、设置响应头、缓存(让验证码图片正常工作)
5、把图片写给浏览器
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//让浏览器三秒刷新一次
resp.setHeader("refresh","3");
//在内存中创建一个图片
BufferedImage bufferedImage = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
//得到图片
Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics(); //画笔
//设置图片的背景颜色
graphics.setColor(Color.white);
graphics.fillRect(0,0,80,20);
//给图片写数据
graphics.setColor(Color.blue);
graphics.setFont(new Font(null,Font.BOLD,20)); //字体格式,大小
graphics.drawString(RandomNum(),0,20); //字体坐标
//告诉浏览器,这个请求用图片的方式打开
resp.setContentType("image/jpeg");
//网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//把图片写给浏览器
ImageIO.write(bufferedImage,"jpg",resp.getOutputStream());
}
//生成随机七位字符串
private String RandomNum(){
Random random = new Random();
String num=random.nextInt(9999999)+"";//七位数
//String是不可更改的常量,StringBuffer是字符串的变量
StringBuffer sb = new StringBuffer();
//保证是七位数
for(int i=0;i<7-num.length();i++){
sb.append("0");
}
num = sb.toString()+num;
return num;
}
5.4.4 实现重定向
B一个web资源收到客户端A的请求后,B会通知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.sendRedirect("/resp/image");
/**
* 相当于这两个操作:
* resp.setHeader("Location","/resp/image");
* resp.setStatus(302);
*/
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
请问请求转发和重定向的区别?
相同点:
- 页面都会跳转
不同点:
- 请求转发 url不会变化
- 重定向 url会改变
顺便结合重定向,写一个简易的登陆成功页面:
登录表单
<%-- 页面编码,否则中文出现乱码 --%>
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<body>
<h2>Hello World!</h2>
<%-- 这里提交的路径需要是项目的路径 --%>
<%-- ${pageContext.request.contextPath}代表当前项目
写法类似于 this.getServletContext().getContextPath(); --%>
<form action="${pageContext.request.contextPath}/login" method="get">
用户名: <input type="text" name="username">
密码: <input type="text" name="password">
<input type="submit">
</form>
</body>
</html>
登陆成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>success</h1>
</body>
</html>
请求处理
public class RequestTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理请求
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username+":"+password);
resp.sendRedirect("/resp/success.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
重定向的时候一定要注意 路径问题 ,否则会报404
<form action="${pageContext.request.contextPath}/login" method="get">
上面就是在jsp中获取当前项目路径的方法
5.5 HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求的所有信息会被封装到HttpServletRequest中,通过HttpServletRequest的方法,获取到客户端的所有信息
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
String username = req.getParameter("username");//防止请求的数据出现中文乱码
String password = req.getParameter("password");//防止响应的数据乱码
String[] hobbies = req.getParameterValues("hobby");
System.out.println("=================");
System.out.println("欢迎!"+username+"!\n你的密码为:"+password+"\n你的爱好为:");
System.out.println(Arrays.toString(hobbies));
this.getServletContext().getRequestDispatcher("/success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录成功</title>
</head>
<body>
<h1>登陆成功</h1>
<h2>你的用户名为:${pageContext.request.getParameter("username")}<br>
密码为:${pageContext.request.getParameter("password")}
</h2>
</body>
</html>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<div style="text-align: center">
<h1>登录</h1>
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
爱好:
<input type="checkbox" name="hobby" value="唱">唱
<input type="checkbox" name="hobby" value="跳">跳
<input type="checkbox" name="hobby" value="rap">rap
<br>
<input type="submit" name="提交" value="提交">
</form>
</div>
</body>
</html>
注意重定向和请求转发的细节(区别上面讲了):
- 重定向:需要包含项目根目录的路径
this.getServletContext().getRequestDispatcher("/success.jsp").forward(req,resp);
- 请求转发:不需要包含项目根目录
resp.sendRedirect("/resp/image");
关于jsp,就需要一些前端的知识了
六、Cookie、Session
6.1 会话
会话:用户打开了一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话
有状态会话:
一个同学来过教室,下次再来教室,我们会知道这个同学,曾经来过,称之为有状态会话
一个网站,怎样证明你来过?
客户端 服务端
1、服务端给客户端一个信件,客户端下次访问带上这个信件就可以了(cookie)
2、服务器登记你来过了,下次你来的时候我匹配你(session)
6.2 保存会话的两种技术
cookie
- 客户端技术(响应、请求)
session
- 服务器技术(利用这个技术,可以保存用户的会话信息,我们可以把信息或数据放在session中)
常见场景:网站登录后再次访问,不用登陆可以直接访问
6.3 cookie
public class CookieDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//服务器,告诉你你来的时间,把这个时间封装为一个信件,下次来带上,我就知道你来了
//解决中文乱码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
//Cookie 服务端从客户端获取
Cookie[] cookies = req.getCookies(); //数组,说明cookie可能有多个
//判断cookie是否存在
if(cookies!=null){
out.print("你上次访问的时间是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
//获取cookie的名字
if("lastLoginTime".equals(cookie.getName())){
//获取cookie中的值
long l = Long.parseLong(cookie.getValue());
Date date = new Date(l);
out.print(date.toLocaleString());
}
}
}
else {
System.out.println("这是你第一次访问本站");
}
//写在循环外,每次都能更新
resp.addCookie(new Cookie("lastLoginTime", System.currentTimeMillis()+""));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
这里由于第一次请求中cookie也有东西,所以else中的内容就无法触发了
第二次访问:
并且我们需要携带的cookie保存在本地:
cookie:一般保存在本地的用户目录下 appdata
一个网站cookie是否存在上限?
- 一个cookie只能保存一个信息
- 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie
- cookie大小有限制4kb
- 300个cookie浏览器上限
删除cookie:
- 不设置有效期,关闭浏览器,自动失效
- 设置有效期为0
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + ""); //键值对需要一致
cookie.setMaxAge(0); //设置cookie有效期为0,是cookie直接失效
resp.addCookie(cookie);
键值对一致,才能把原先的cookie替换
编码解码:
为了解决出现乱码的情况
URLEncoder.encode("饼干")
URLDecoder.decode(cookie.getValue())
6.4 session
什么是session:
- 服务器会给每个用户(浏览器),创建一个session对象
- 一个session独占一个浏览器,只要浏览器没有关闭,session就一直存在
- 用户登陆之后,可以访问整个网站的链接
session和cookie的区别:
- Cookie是把用户的数据写到用户的浏览器,浏览器保存(可以保存多个)
- Session把用户的数据写到用户独占的Session中,服务器端保存(保存重要的信息,减少服务器资源的浪费)
- Session对象由服务器创建
使用场景:
- 保存一个用户的信息
- 购物车信息
- 在整个网站中经常使用的数据,将它保存在Session中
public class SessionDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//获取session
HttpSession session = req.getSession();
String sessionId = session.getId();
//存入session
session.setAttribute("name",new Person("siii0",1));
if(session.isNew()){
resp.getWriter().write("session是新创建的,ID:"+sessionId);
}
else{
resp.getWriter().write("session已经创建了,ID:"+sessionId);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
//注销session
HttpSession session = req.getSession();
session.removeAttribute("name");
//手动注销
session.invalidate();
会话自动过期:web.xml配置
<!-- 设置session的默认失效时间 -->
<session-config>
<!-- session 失效时间单位为分钟 -->
<session-timeout>1</session-timeout>
</session-config>
- 本文链接:http://siii0.github.io/Java-Web/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。