零、互联网通信流程
(一)、设计技术【偏过时】
控制浏览器行为技术:HTML、CSS、JavaScript
控制硬盘上数据库行为技术:MySql数据库服务器管理使用(SQL重点),JDBC规范
控制服务端Java行为技术:Http服务器,Servlet,JSP
互联网通信流程开发规则:MVC
(二)、实质
两台计算机通过网络实现文件共享行为
(三)、角色划分
- 客户端计算机:用于发送请求,索要资源文件的计算机
- 服务端计算机:用于接受请求,提供对应的资源文件计算机
(四)、互联网通信模型
1. C/S通信模型:
C:client software:客户端软件
(1)客户端软件专门安装在客户端计算机上
(2)帮助客户端计算机向指定服务端计算机发送请求,索要资源文件
(3)帮助客户端计算机将服务端计算机发送回来 二进制数据 解析为 文字、数字、图片、视频、命令、
S:server software:服务器软件
(1)服务器软件专门安装在服务端计算机上
(2)服务器软件用于接收来自于特定的客户端软件发送请求
(3)服务器软件在接收到请求之后自动的在服务端计算机上定位被访问的资源文件
(4)服务器软件自动的将定位的文件内容解析为 二进制数据 通过网络发送回发起请求的客户端软件上
使用场景
个人娱乐:微信,电商,视频网站;企业办公领域应用少
优缺点
- 优点:
- 安全性较高
- 有效降低服务端计算机工作压力
- 缺点
- 增加客户获得服务的成本
- 更新较为繁琐
2. B/S通信模型
B:browser,浏览器
(1)浏览器,安装在客户端计算机软件
(2)可以向任意服务器发送请求,索要资源文件
(3)可以将服务器返回的 二进制数据 解释为 文字、数字、图片、视频、命令
S:server software 服务器软件
(1)服务器软件专门安装在服务端计算机上
(2)可以接受任何浏览器发送请求
(3)自动的在服务端计算机上定位被访问的资源文件
(4)自动的将定位的资源文件内容以二进制形式发送会发送请求浏览器上
使用场景
既适用于个人,更适用于企业
优缺点
- 优点
- 不会增加用户获得服务的成本
- 几乎不需要更新浏览器
- 缺点
- 几乎无法有效对服务端计算机资源文件进行保护
- 服务端计算机工作压力极大(高并发)
(五)、共享资源文件
1. 定义:
- 可以通过网络进行传输的文件,都被称为共享资源文件
- 所有的文件内容都可以通过网络传输,所有文件都是共享资源文件
2. Http服务器下对共享资源文件分类
(1)静态资源文件
(2)动态资源文件
3. 静态资源文件
(1)如果文件内容是固定的,就被称为静态资源文件
(2)如果文件存放的是命令,这些命令只能在浏览器编译与执行,也被称为静态资源文件(.html、.css、.js)
4. 动态资源文件
- 文件存放命令,并且命令不能在浏览器编译与执行,只能在服务端计算机编译执行,该文件被称为动态资源文件(.class)
5. 静态资源文件与动态资源文件调用的区别
静态资源文件被索要时,Http服务器直接通过输出流将静态文件内容或者命令以二进制形式推送给发起请求的浏览器
动态资源文件被索要时,Http服务器需要创建当前class文件的实例对象,通过实例对象调用对应的方法处理用户请求,通过输出流将运行结果以二进制形式推送给发起请求的浏览器
(六)、开发人员担任的工作
1. 客户端计算机上(浏览器)
控制浏览器请求行为(三要素)
- 控制浏览器发送的请求地址
- 控制浏览器发送请求的方式
- 控制浏览器发送请求携带的参数
控制浏览器接收结果行为
- 控制浏览器采用对应编译器将接收二进制数据解析为文字、视频、图片、命令
- 控制浏览器将解析内容或者命令进行执行与展示
- 控制用户与浏览器之间交流 js ----> Jquery
2. 服务端计算机
Http服务器上保存静态资源文件和动态资源文件
一、web.xml一点配置
- idea生成的头文件有点过时,不妨改为如下的
关于 Mapping 的问题
- 一个Servlet可以对应一个或多个映射路径
- 一个Servlet可以指定通用映射路径(加上通配符*)
HelloServlet
/servlet/*
2.默认请求路径(会把index页面取代,少用)
HelloServlet
/servlet/*
3.自定义后缀,注意:不能在*前面加上映射路径。
HelloServlet
*.do
4.优先级问题
指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求。
二、ServletContext
简介
ServletContext是一个全局的储存信息的空间,服务器开始就存在,服务器关闭才释放。
在web容器启动的时候,服务器会为每个web程序创建对应的一个ServletContext,代表当前的web应用。
==一个web应用对应一个ServletContext。==
对象获取
this.getServletContext();
this.getServletConfig().getServletContext()
常用方法
//添加属性:
setAttribute(String name, Object obj);
//得到值:
getAttribute(String name),这个方法返回Object
//删除属性:
removeAttribute(String name)
//ServletContext中的属性的生命周期从创建开始,到服务器关闭结束。
一般应用
1、多个Servlet可以通过ServletContext对象来实现数据间的共享
(可用Session或者request的方法将其取代)
类似于Session,通过ServletContext对象我们也可以实现数据共享,但值得注意的是,Session是只能在一个客户端中共享数据,而ServletContext中的数据是在所有客户端中都可以实现数据共享的。
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello");
resp.setContentType("text/html;utf-8");
//获得当前的ServletContext
//ServletContext可以实现数据共享
ServletContext context = this.getServletContext();
String username = "text";
//在web容器启动的时候,服务器会为每个web程序创建对应的一个ServletContext。代表当前的web应用。一个web应用对应一个ServletContext
//将数据保存在本程序对应的ServletContext中,键值对的形式保存
context.setAttribute("username",username);
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
ServletContext context = this.getServletContext();
//获取资源
String username = (String) context.getAttribute("username");
//在页面输出
resp.getWriter().println(""+"名字"+username+"
");
}
2、0.获取全局初始化参数
//获取初始化参数
public abstract String getInitParameter(String s)
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
ServletContext context = this.getServletContext();
//获得初始化参数
resp.getWriter().println(context.getInitParameter("texturl"));
}
texturl
jdbc:mysql://localhost:3306/mybatis
2、1.获取当前Servlet的配置参数(局部)
ServletDemo
com.vigil.servlet.ServletDemo
encoding
utf-8
String encoding = this.getServletConfig().getInitParameter("encoding");
3、请求转发
(一般用request实现)
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
//请求路径
RequestDispatcher requestDispatcher = context.getRequestDispatcher("/Demo0");
//调用forward实现请求转发
requestDispatcher.forward(req,resp);
}
这种转发效果与通过request对象的getRequestDispatcher("/url").forward(req,resp)方法一样
//效果一样的转发方式
request.getRequestDispatcher("/url").forward(req, resp);
//
this.getServletContext().getRequestDispatcher("/url").forward(req, resp)
4、读取资源文件
(尽量用类加载和反射实现这一目的)
如何确定资源地址(getResourceAsStream(url))是一个重点
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这种方法的默认读取路径就是Web应用的根目录
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
// 创建属性对象
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.getWriter().println(user + ":" + pwd);
}
username=root
password=123456
ServletContext的应用总结
涉及到不同用户共享数据,而这些数据量不大,同时又不希望写入数据库中,我们就可以考虑使用ServletContext实现。
例如:1. 网站计数器 2. 网站的在线用户显示
注意:因为存在ServletContext中的数据在服务器中会长时间,这样就会占用很多内存,因此在使用ServletContext时,建议不要往里面添加过大的数据!
三、HttpServletRequest和HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HTTPServletRequest对象,代表响应的HttpServletResponse
(一)、HttpServletResponse
1. 简单分类
(1)负责像浏览器发送数据的方法
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
(2)负责向浏览器发送响应头的方法
void setCharacterEncoding(String var1);
void setContentLength(int 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);
(3)响应状态码
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;
2. 常见应用
(1)向浏览器输出消息
(2)下载文件
//关于字符串url
1、要获取下载的文件路径
2、下载的文件名
3、设置想办法让浏览器能够支持下载我们需要的东西
4、获取下载的文件输入流
//以下为Java io学习
5、创建缓冲区
6、获取OutputStream对象
7、将FileOutputStream流写入到buffer缓冲区
8、使用OutputStream将缓冲区中的数据输入到客户端
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、要获取下载的文件路径(java io 基础,这里写死了一个地址,仅作练习)
String realPath = "D:\\javadownload\\ideaworkplace\\javaweb-02-servlet\\response01\\src\\main\\resources\\1.jpg";
System.out.println("下载文件的路径:" + realPath);
// 2、下载的文件名
String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);
// 3、设置(响应头)想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件名URLEncoder.encode编码
resp.setHeader("Content-Disposition","attachment;filename"+ URLEncoder.encode(fileName,"UTF-8"));
// 4、获取下载的文件输入流(将文件变成流)
FileInputStream in = new FileInputStream(realPath);
// 5、创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
// 6、获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
// 7、将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输入到客户端
while((len = in.read(buffer))>0) {
out.write(buffer,0,len);
}
// 8、关闭流
in.close();
out.close();
}
(3)验证码功能
前端实现:jsp
后端实现:需要使用Java的图片,产生一个图片
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器5s自动刷新一次
resp.setHeader("refresh","5");
//在内存中创建图片
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(getNum(),0,20);
//告诉浏览器,这个请求用图片的方式打开
resp.setContentType("image/jpeg");
//网站存在缓存,设置不让浏览器缓存
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//把图片写给浏览器
ImageIO.write(image,"jpg",resp.getOutputStream());
}
//生成随机数
private String getNum() {
Random r = new Random();
String num = r.nextInt(999999) + "";
//确保生成的随机数长度为6
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 6 - num.length(); i++) {
sb.append(""+r.nextInt(10));
}
return sb.toString() + num;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
(4)==实现重定向==![重要]
B一个web资源,收到客户端A请求后,B会通知A客户端去访问另一个web资源C,这个过程叫做重定向。
重定向的时候一定要注意路径问题,否则会有404错误
常见:用户登录
void sendRedirect(String var1) throws IOException;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
r01是本程序的projectName
resp.setHeader("Location","r01/image");
resp.setStatus(302);
*/
resp.sendRedirect("r01/image");//重定向
}
面试题:请你聊聊重定向和转发的区别(手动搜索)
相同点:页面都会实现转换
不同点:请求转发的时候,url不会产生变化,重定向时候,url地址栏会发生变化
(二)、HTTPServletRequest
HTTPServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HTTPServletRequest,通过这个HTTPServletRequest的方法,获得客户端的所以信息。
String getAuthType();
Cookie[] getCookies();
long getDateHeader(String var1);
String getHeader(String var1);
Enumeration getHeaders(String var1);
Enumeration getHeaderNames();
int getIntHeader(String var1);
String getMethod();
String getPathInfo();
String getPathTranslated();
String getContextPath();
String getQueryString();
String getRemoteUser();
Principal getUserPrincipal();
String getRequestedSessionId();
String getRequestURI();
StringBuffer getRequestURL();
String getServletPath();
HttpSession getSession(boolean var1);
HttpSession getSession();
==获取参数,请求转发==(下面为一个测试例子)
//LoginServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("================================");
System.out.println(username+":"+password);
System.out.println(Arrays.toString(hobbys));
System.out.println("================================");
//通过请求转发
//这里的 / 代表当前web应用
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);//处理Post请求时,转到doGet方法中
}
<%--index页面--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Login
登录
<%--以post方式提交表单到login页面--%>
<%--登录成功的页面--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
success
登陆成功
(三)、request.setAttribute()和request.getSession().setAttribute()的区别
request.setAttribute():在request范围一次传参数,速度快,缺点是参数只能取一次。
request.getSession().setAttribute():取得当前HttpSession,在HttpSession范围保存参数,就一直在,随时可以取来用。缺点是比较占用资源。
session.setAttribute()和session.getAttribute()配对使用,作用域是整个会话期间,在所有的页面都使用这些数据的时候使用。
request.setAttribute()和request.getAttribute()配对使用,作用域是请求和被请求页面之间。request.setAttribute()是只在此action的下一个forward需要使用的时候使用;request.getAttribute()表示从request范围取得设置的属性,必须要先setAttribute设置属性,才能通过getAttribute来取得,设置与取得的为Object对象类型。其实表单控件中的Object的 name与value是存放在一个哈希表中的,所以在这里给出Object的name会到哈希表中找出对应它的value。setAttribute()的参数是String和Object。
request.getParameter()表示接收参数,参数为页面提交的参数。包括:表单提交的参数、URL重写(就是xxx?id=1中的id)传的参数等,因此这个并没有设置参数的方法(没有setParameter()),而且接收参数返回的不是Object,而是String类型。
request.getSession().setAttribute("adminid",adminForm.getAdminid())把adminForm.getAdminid())返回值保存到session当前会话中,名称为adminid,只要当前会话不过期, 任何地方都可以访问到 ;request.setAttribute("adminid",adminForm.getAdminid())把adminForm.getAdminid())的返回值保存到request当前请求中,名称为adminid,只在当前请求中访问到,其他请求中无法访问。
request.setAttribute这个的生命周期就是request级别的
request.getSession().setAttribute这个的生命周期是session级别的
- request.getParameter() 和request.getAttribute() 区别
(1)request.getParameter()取得是通过容器的实现来取得通过类似post,get等方式传入的数据,request.setAttribute()和getAttribute()只是在web容器内部流转,仅仅是请求处理阶段。
(2)request.getParameter()方法传递的数据,会从Web客户端传到Web服务器端,代表HTTP请求数据。request.getParameter()方法返回String类型的数据。
request.setAttribute()和getAttribute()方法传递的数据只会存在于Web容器内部
还有一点就是,HttpServletRequest类有setAttribute()方法,而没有setParameter()方法。
- request.getAttribute()与request.setAttribute()
request.getAttribute("nameOfObj")可得到JSP页面一表单中控件的Value。其实表单控件中的Object的 name与value是存放在一个哈希表中的,所以在这里给出Object的name会到哈希表中找出对应它的value。
而不同页面间传值使用request.setAttribute(position, nameOfObj)时,只会从a.jsp到b.jsp一次传递,之后这个request就会失去它的作用范围,再传就要再设一个 request.setAttribute()。而使用session.setAttribute()会在一个过程中始终保有这个值。
P.S:JavaScript与JSP中不能相互传值,因为JavaScript运行在客户端,而JSP运行在服务器端。若想使它们之间可以相互传递参数,可以在JSP中设置一个hidden控件,用它的value结合上面所说的用法来传递所需的数值。
- request.setAttribute()、session.setAttribute()和request.getParameter()的联系与区别(记录)
四、Cookie和Session
(一)、==会话==:
用户通过浏览器(客户端),点击一个网页的超链接等去访问多个web资源,直到关闭浏览器的过程。
一个网站如何知道一个客户端?第一次会给客户端一个cookie,在服务器端创建相应的session,下一次,客户端只要有相应的cookie和服务器端的session匹配即可。(也即是SessionID匹配)
(二)、保存会话的两个技术
==cookie== 客户端技术(响应,请求)
==session== 服务器技术,利用这个技术,可以把信息或者数据放在session上
- 如何区分不同用户——Cookie/Session机制详解
- JavaWeb学习总结(十二)——Session
Cookie
- 从请求中拿到cookie信息
- 服务器响应给客户端cookie
例子演示:保存用户本次登录的时间,并输出上一次和本次的登录(Java中获取)时间的示例
//保存用户本次登录的时间,并输出上一次和本次的登录时间
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决中文乱码
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
//从请求中获得cookie,为什么这么说,是因为客户端向服务器请求会提交客户端本地的cookie
Cookie[] cookies = req.getCookies();//这里返回数组,说明cookie可能存在多个
String nowTime = getTime();//获得当前时间
//判断cookie是否存在
if (cookies != null) {
int flag = 0;
//遍历cookie数组
for (Cookie cookie : cookies) {
if ("lastLoginTime".equals(cookie.getName())) {
out.println("你上一次登录的时间是:" + cookie.getValue() +
"现在的时间是:" + nowTime + "
");
flag = 1;
}
}
if(flag == 0)
out.println("这是你第一次访问本站
现在的时间是:" + nowTime + "
");
} else {
out.println("这是你第一次访问本站
现在的时间是:" + nowTime + "
");
}
//服务器给客户端发送cookie
Cookie cookie = new Cookie("lastLoginTime", nowTime);
//设置有效期
cookie.setMaxAge(1*60*60);
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
//获得时间
private String getTime() {
SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
Date date = new Date(System.currentTimeMillis());
return formatter.format(date);
}
//重要方法
Cookie[] cookies = req.getCookies();//获得cookie对象
cookie.getName()//获得cookie中的key
cookie.getValue()//获得cookie的value
Cookie cookie = new Cookie("lastLoginTime", newTime);//新建一个cookie对象
cookie.setMaxAge(1*60*60);//设置cookie的有效期
resp.addCookie(cookie);//响应给服务器
一个网站cookie是否存在上限?
- 一个Cookie只保存一个信息(键值对)
- 一个web站点可以给浏览器发送多个Cookie,最多存放20个Cookie
- Cookie大小限制为4kb
- 300个cookie浏览器上限
IE/Firefox/Chrome等浏览器保存Cookie的位置
删除Cookie
- 不设置有效期,关闭浏览器,自动失效
- 设置有效期时间为0
(间接删除,新建同名的cookie,此同名不是变量名同名,是指cookie.getName()返回的名是一致的,也即是cookie初始化对象传进去的第一个参数)
//手动删除cookie
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决中文乱码
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
//服务器给客户端发送cookie,名字和要被删去的cookie一致
Cookie cookie = new Cookie("lastLoginTime","text");
//设置有效期为0,马上过期
cookie.setMaxAge(0);
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
3.浏览器(Edge)中删除
中文传输乱码问题
Cookie处理中文时编码问题:cookie里面保存的数据类型为ASCII码,并不能直接保存中文。相应的报错为:Control character in cookie value or attribute.
利用工具类对中文编码保存,解码使用 (URLEncode)
//重要方法
URLEncoder.encode("测试","UTF-8")//编码
URLDecoder.decode(cookie.getValue(),"UTF-8")//解码
//中文传输的乱码问题
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决中文乱码
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
//从请求中获得cookie,为什么这么说,是因为客户端向服务器请求会提交客户端本地的cookie
Cookie[] cookies = req.getCookies();//这里返回数组,说明cookie可能存在多个
if(cookies != null) {
for (Cookie cookie : cookies) {
if("name".equals(cookie.getName())) {
out.println(URLDecoder.decode(cookie.getValue(),"UTF-8"));
System.out.println(cookie.getValue());
}
}
}
resp.addCookie(new Cookie("name", URLEncoder.encode("测试","UTF-8")));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
Session
推荐再看:JavaWeb学习总结(十二)——Session
-
什么是Session
- 服务器会给每一个用户(浏览器)创建一个Session对象
- 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在
- 用户登录之后,这个网站用户都可以访问,例如保存用户的信息、购物车的信息
-
Session和Cookie的区别
- Cookie是把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)
- Session把用户的数据写到用户独占Session中,服务器端保存(保存重要的信息,减少服务器端资源的浪费)
- Session对象由服务创建
-
使用场景:
- 保存一个登录用户的信息
- 购物车信息
- 在整个网站中经常使用的数据
Session方法使用
//SessionDemo01
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
//得到Session
HttpSession session = req.getSession();
//给Session中存东西
session.setAttribute("Person", new Person(9,"测试"));
//获得Session的ID
String id = session.getId();
//判断Session是不是新创建的
if (session.isNew()) {
resp.getWriter().println("Session创建成功,ID为:" + id);
} else {
resp.getWriter().println("Session已经在服务器中存在,ID为:" + id);
}
}
//SessionDemo02
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
//得到Session
HttpSession session = req.getSession();
Person person = (Person) session.getAttribute("Person");
if(person != null) {
resp.getWriter().println(person.toString());
System.out.println(person.toString());
} else {
resp.getWriter().println("no person");
}
}
==注销Session==
- 手动注销
//手动注销session
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
session.removeAttribute("Person");
session.invalidate();//注销session
}
- 在web.xml中配置自动过期
10
五、==JSP==
(一)、什么是JSP
==Java Servlet Pages==:
Java服务器端页面,和Servlet一样用于实现动态页面的技术
最大的特点:写JSP就像写HTML,区别是
1、HTML只给用户提供静态的数据
2、JSP页面中可以嵌入Java代码,为用户提供动态数据
(二)、JSP原理
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet
JSP本质是一个Servlet,他最终会被转化为一个Java类
1. JSP中三个基本方法
public void _jspInit() {}//初始化
public void _jspDestroy() {}//销毁
//服务,有两个传入的对象!!request和response
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) {}
2. JSP的九个内置对象
是_jspService(req,resp){}方法独有的(八个常用的,最后两个对象略略略)
//jsp的十个内置对象(八个常用的,最后两个对象略略略)
request //请求
response //响应
final javax.servlet.jsp.PageContext pageContext; //页面上下文
javax.servlet.http.HttpSession session = null; //session
final javax.servlet.ServletContext application; //applicationContext
final javax.servlet.ServletConfig config; //配置
javax.servlet.jsp.JspWriter out = null; //输出
final java.lang.Object page = this; //当前页
exception 对象
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
3. 页面输出前的对象设置
response.setContentType("text/html;charset=UTF-8");//页面输出配置
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
4. 以上的对象都可以在JSP直接使用
在JSP页面中,只要是Java代码就会原封不动的输出,但是如果是HTML代码就会被转成类似下面这种在输出到前端
out.write("\r\n");
out.write("\r\n");
(三)、JSP基础语法
maven需要的依赖
要写JSP首先要配好环境,下面是pom.xml所缺少的代码
javax.servlet
javax.servlet-api
4.0.1
javax.servlet.jsp
javax.servlet.jsp-api
2.3.3
jstl
jstl
1.2
taglibs
standard
1.1.2
1. JSP表达式
<%--JSP表达式
作用:用于将程序的输出到客户端
<%= 变量或者表达式%>
--%>
<%= 1+2 %>
2. jsp脚本片段
<%--jsp脚本片段--%>
<%
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
out.println("sum="+sum+"
");
%>
<%--JSP脚本片段的实现
重点理解就是说,这些Java代码和HTML代码的转化,都是用out.print方法输出到前端
--%>
<%--在Java代码嵌入HTML元素--%>
<%
for (int i = 0; i < 5; i++) {
%>
Hello <%=i %>
<%
}
%>
3. JSP声明
- 会被编译到JSP生成的Java类中!其他的就会生成到_jspService方法中
<%--JSP声明,是属于类的,与前面的区别在于多了一个 !--%>
<%!
private int num = 0;
public void show() {
System.out.println("run the show method" + num);
}
static {
System.out.println("loading servlet");
}
%>
<% %>
<%= %>
<%! %>
关于注释
- JSP的注释不会显示在客户端,而HTML注释会显示在客户端
(四)、JSP指令
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page errorPage="error/error500.jsp" %>
<%@ page isErrorPage="true" %>
==两个包含的区别==
(jsp中静态包含和动态包含的区别与联系)
因为包含的位置不是完全相同的,对于变量的两者规范不一,jsp:include包含的话,多个页面之间存在的变量名可以一样且多次出现,但是<%@include%>包含的话只允许变量在页面中与被包含页面只出现一次。
<%--@include会将两个页面合二为一,是在_jspService方法中初始化--%>
<%@include file="index.jsp" %>
<%--JSP标签
jsp:include:拼接页面,但他不会将其他的页面直接在_jspService方法中初始化--%>
web.xml中关于错误页面的部署
404
/error/error404.jsp
500
/error/error500.jsp
(五)、JSP九大内置对象
- request 存东西
- response
- pageContext 存东西
- session 存东西
- application (applicationContext)存东西
- config
- out
- page
- exception
<%--内置对象--%>
<%
pageContext.setAttribute("name0","测试0");//保存的数据只在一个页面中有效
request.setAttribute("name1","测试1");//保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name2","测试2");//保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name3","测试3");//保存的数据只在服务器中有效,从打开服务器到关闭服务器
%>
<%--通过pageContext取出我们保存的值--%>
<%
//通过寻找的方式
//从底层找起,pageContext-->request-->session-->application
String name0 = (String) pageContext.findAttribute("name0");
String name1 = (String) pageContext.findAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");
%>
<%--使用EL表达式输出 ${} --%>
取出的值有:
${name0}
${name1}
${name2}
${name3}
${name4}
四大作用域的大小
- pageContext
- request
- session
- application
<%-- pageContext-->request-->session-->application --%>
<%
pageContext.setAttribute("name0","测试0");//保存的数据只在一个页面中有效
request.setAttribute("name1","测试1");//保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name2","测试2");//保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name3","测试3");//保存的数据只在服务器中有效,从打开服务器到关闭服务器
%>
(六)、JSP标签、JSTL标签、EL表达式
再说一遍,maven中,关于JSTL表达式要导入的包
jstl
jstl
1.2
taglibs
standard
1.1.2
El表达式 ${ }
- 获得数据
- 执行运算
- 获得web开发中常用的对象
- 调用Java方法
JSP标签
<%-- 转发 --%>
JSP 标准标签库(JSTL)
JSP 标准标签库(JSTL)
JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义许多标签,可供我们使用,标签的功能和Java代码一样
-
核心标签(掌握)
- 格式化标签
- SQL标签
- XML标签
JSTL标签库使用方法
- 引入对应的taglib
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
使用其中的方法
c:if
c:choose
你的成绩为优秀
你的成绩为良好
你的成绩为一般
你的成绩为合格
你的成绩为不合格
c:forEach
<%
ArrayList people = new ArrayList();
people.add(0,"李四");
people.add(1,"张三");
people.add(2,"王五");
people.add(3,"田七");
people.add(4,"吴孟达");
request.setAttribute("peopleliset",people);
%>
<%--var,每一次遍历出来的对象
item,遍历的对象数组
begin,开始的位置
end,结束的位置(包含的)
step步长
--%>
<%
people people = new people();
people.setAddress("西安");
people.setAge(21);
people.setId(1);
people.setName("李三");
out.print(people.getName());
out.print(people.getAge());
out.print(people.getId());
out.print(people.getAddress());
%>
<%=people.getName()%>
<%=people.getId()%>
<%=people.getAge()%>
<%=people.getAddress()%>
<%--jsp使用--%>
姓名:
年龄:
ID:
地址:
六、JavaBean
实体类:一般和数据库中的表结构一一对应。
JavaBean有特定的写法:
JavaBean
- 必须要有一个无参构造
- 属性必须私有化
- 必须有对应的get/set方法
JavaBean作用
一般用于和数据库的字段做映射 ORM:对象关系映射
- 表 --> 类
- 字段 --> 属性
- 行记录 --> 对象
public class people {
private int id;
private int age;
private String name;
private String address;
public people() {
}
public people(int id, int age, String name, String address) {
this.id = id;
this.age = age;
this.name = name;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "people{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
<%
people people = new people();
people.setAddress("西安");
people.setAge(21);
people.setId(1);
people.setName("李三");
out.print(people.getName());
out.print(people.getAge());
out.print(people.getId());
out.print(people.getAddress());
%>
<%=people.getName()%>
<%=people.getId()%>
<%=people.getAge()%>
<%=people.getAddress()%>
<%----%>
姓名:
年龄:
ID:
地址:
了解MVC
七、Filter和Listener
编写Filter(重要)
1. 导包
2. 实现接口中的所有方法
public class CharacterEncodingFilter implements Filter {
//初始化,web服务器启动时就初始化了,随时等待过滤
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("run the CharacterEncodingFilter");
}
//chain:链
/*
1.过滤中的所有的代码,在过滤特定请求的时候都会执行
2.必须要让过滤器继续执行
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
System.out.println("CharacterEncodingFilter doFiter run before");
chain.doFilter(request,response);//让我们的请求继续走,如果不写,程序到此被拦截结束。
System.out.println("CharacterEncodingFilter doFiter run after");
}
//销毁,web服务器关闭的时候,过滤器会销毁
public void destroy() {
System.out.println("destroy the CharacterEncodingFilter");
}
}
重点注意:
值得注意的不可缺少的一行代码
chain.doFilter(request,response);//让我们的请求继续走,如果不写,程序到此被拦截结束。
3.在web.xml中配置Filter
CharacterEncodingFilter
com.vigil.filter.CharacterEncodingFilter
CharacterEncodingFilter
/textFilter/*
编写Listener
1. 导包,实现监听器的接口
//原理:统计网站在线人数:统计session
public class OnlineCountListener implements HttpSessionListener {
//创建session监听
//一旦创建Session就会触发一次这个事件
public void sessionCreated(HttpSessionEvent se) {
ServletContext ctx = se.getSession().getServletContext();
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
if(onlineCount == null) {
onlineCount = new Integer(1);
} else {
onlineCount++;
}
ctx.setAttribute("OnlineCount",onlineCount);
}
//销毁session监听
//一旦销毁Session就会触发一次这个事件
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext ctx = se.getSession().getServletContext();
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
if(onlineCount == null) {
onlineCount = new Integer(0);
} else {
onlineCount--;
}
ctx.setAttribute("OnlineCount",onlineCount);
}
}
2. web.xml配置监听器
com.vigil.listener.OnlineCountListener
3. 看情况使用!
过滤器和监听器常见使用于:
(监听器:GUI编程中常用)
实现小项目:
用户登录之后才可以进入主页,用户注销后就不能进入主页。
1. 用户登录后,向Session中放入用户 的数据
登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
login
登录
提交登录的Servlet
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获得前端数据
String username = (String) req.getParameter("username");
if("admin".equals(username)) { //登陆成功
req.getSession().setAttribute("USER_NAME",username);
System.out.println(username + "=username");
System.out.println("getUSER_NAME=" + req.getAttribute("USER_NAME"));
System.out.println("getUSER_NAME=" + req.getSession().getAttribute("USER_NAME"));
resp.sendRedirect("/sys/succeed.jsp");
} else { //登陆失败
resp.sendRedirect("/errorname.jsp");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
登录失败的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
error
错误
无权限
登录成功的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%--<%--%> 页面实现过滤器功能的jsp脚本片段
<%-- Object user_name = request.getSession().getAttribute("USER_NAME");--%>
<%-- if(user_name == null) {--%>
<%-- response.sendRedirect("/login.jsp");--%>
<%-- }--%>
<%--%>--%>
主页
登出请求的Servlet处理(将目标Session的某个元素删去,而是撤销Session)
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object user_name = req.getSession().getAttribute("USER_NAME");
System.out.println("getUSER_NAME=" + user_name);
System.out.println("getUSER_NAME=" + req.getAttribute("USER_NAME"));
if(user_name != null) {
req.getSession().removeAttribute("USER_NAME");
resp.sendRedirect("/login.jsp");
} else {
resp.sendRedirect("/errorname.jsp");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
2. 进入页面的时候判断用户是否已经登录,在过滤器实现
登录页面过滤器
public class SysFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//ServletRequest --> HttpServletRequest
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if(req.getSession().getAttribute("USER_NAME") == null) {
resp.sendRedirect("/login.jsp");
}
chain.doFilter(req,resp);
}
public void destroy() {
}
}
SysFilter
com.vigil.filter.SysFilter
SysFilter
/sys/*
八、JavaWeb_JDBC
什么是JDBC:Java连接数据库
需要jar包支持
- java.sql
- javax.sql
- mysql-connector-java
环境搭配
实验数据库
create table users
(
id int auto_increment
primary key,
name varchar(40) null,
password varchar(40) null,
email varchar(60) null,
birthday date null
)
charset = utf8;
- 导入数据库依赖
mysql
mysql-connector-java
5.1.47
JDBC固定使用步骤
使用Statement
(3,4,5可以换成预编译格式)
- 加载驱动
- 连接数据库,(代表数据库)
- 创建向数据库发送SQL语句的对象Statement :CRUD
- 编写SQL语句(根据业务需求)
- 执行SQL语句
- 关闭连接,释放资源
public class TextJdbc {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//配置信息
String utl = "jdbc:mysql://127.0.0.1:3306/jdbc?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
String username = "root";
String password = "20201123";
//1、加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2、数据库,代表数据库
Connection connection = DriverManager.getConnection(utl, username, password);
//3、向数据库发送SQL的对象Statement:CRUD
Statement statement = connection.createStatement();
//4、编写SQL
String sql = "select * from users";
//5、执行查询SQL,返回一个ResultSet结果集
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()) {
System.out.println("id="+resultSet.getObject("id"));
System.out.println("name="+resultSet.getObject("name"));
System.out.println("password="+resultSet.getObject("password"));
System.out.println("email="+resultSet.getObject("email"));
System.out.println("birthday="+resultSet.getObject("birthday"));
}
//关闭连接,释放资源,!!先开的后关
resultSet.close();
statement.close();
connection.close();
}
}
预编译模式
- 加载驱动
- 连接数据库,(代表数据库)
- 编写SQL(根据业务需求)
- 预编译
- 执行SQL语句
- 关闭连接,释放资源
public class TextJdbc2 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//配置信息
String utl = "jdbc:mysql://127.0.0.1:3306/jdbc?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
String username = "root";
String password = "20201123";
//1、加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2、数据库,代表数据库
Connection connection = DriverManager.getConnection(utl, username, password);
//3、编写SQL
String sql = "insert into users (id, name, password, email, birthday) VALUE (?,?,?,?,?)";
//4、预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,5);//给第一个占位符?,赋值5
preparedStatement.setString(2,"textyu");//给第一个占位符?,赋值textyu
preparedStatement.setString(3,"123456");//给第一个占位符?,赋值123456
preparedStatement.setString(4,"[email protected]");//给第一个占位符?,赋值[email protected]
preparedStatement.setString(5,"2020-11-11");//给第一个占位符?,赋值2021-1-26
//5、执行sql
int i = preparedStatement.executeUpdate();
if(i > 0) {
System.out.println("插入成功");
}
//6、关闭连接,释放资源,!!先开的后关
preparedStatement.close();
connection.close();
}
}
StateMent对象
- jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。
- statement对象的executeUpdate方法,用于向数据库中发送增,删,改,查的sql语句,executeUpdate执行完成后,会返回一个整数(即增删改查语句导致了数据库几行数据发生了变化)
- statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象
preparedStatement对象
- 继承了statement对象,使用代表数据库对象的connection的preparedstatement方法获得该对象,但是必须要传入一个sql语句,该sql语句中存在 ? 作为占位符,而该对象提供了set方法将数据一一对应的放到占位符的位置
- statement是无法防止sql注入的,但是preparedstatement是可以防止sql注入
- 原理是:preparedstatement对象把传递进来的参数当作字符,所以会符号转义
事务
- 要么都成功,要么都失败
ACID原则:保证数据安全
- 开启事务
//关闭数据库的自动提交,自动开启事务
connection.setAutoCommit(false); //开启事务
- 事务提交
connection.commit(); //业务完毕,提交业务
- 事务回滚
//在catch语句中,可以加上回滚操作
//不加的话,但事务程序出错,系统也会帮我们进行回滚
connection.rollback();
- 关闭事务(默认都会关闭)
数据库连接池
本质 实现了接口DataSource
有两个实现的数据库连接池
DBCP
需要的jar包:commons-dbcp-1.4、commons-pool-1.6
C3P0
需要的jar包:c3p0-0.9.5.5、mchange-java-0.2.19
配置文件为xml类型
junit单元测试
依赖:
junit
junit
4.12
只有加了@Text注解的方法才可以不用main方法调用