Javaweb
一.tomcat服务器
1.下载tomcat压缩包
https://tomcat.apache.org/download-90.cgi
2.tomcat文件结构
3.启动,关闭tomcat服务器
4.tomcat配置
server.xml文件可以配置启动的端口号:tomcat的默认端口号为:8080,mysql:3306 http:80,https:443
可以配置主机名称:
默认主机名为: localhost —>127.0.0.1(本地DNS域名解析)
例如如果改name=“www.123.com”,则www.123.com应映射到127.0.0.1。这样可以通过
www.123.com:8080访问到tomcat的webapps网站。
修改映射地址的文件:C:\Windows\System32\drivers\etc下的hosts文件
二.HTTP
1、什么是HTTP
(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP之上。
https(443端口):安全版的http
2、两个时代
HTTP/1.0:客户端可以与web服务器连接后,只能获得一个web资源,获取完资源就断开连接
HTTP/1.1:客户端可以与web服务器连接后,可以获得多个web资源,获取完资源不会断开资源。
3、http请求
客户端发请求(Request)给服务器
搜索百度的请求头部分信息:
Request URL: https://www.baidu.com/ 请求地址
Request Method: GET //请求方式get/post
Status Code:200 OK //状态码
Remote(远程) Address:14.215.177.38:443//远程ip地址
Accept:text/html //支持的数据类型
Accept-Encoding:gzip, deflate, br //支持的编码格式
Accept-Language:zh-CN,zh;q=0.9//语言
Cache-Control:max-age=0//缓存控制
Connection:keep-alive //是否长连接
请求行:
请求行中的请求方式:Get,Post,HEAD,DELETE,PUT,TRACT…
get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
post:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效。
4、http响应
服务器给客户端作出响应(Response)
搜索百度的响应头部分信息:
Cache-Control:private 缓存控制
Connection:Keep-Alive 连接
Content-Encoding:gzip 编码
Content-Type:text/html 类型
1)、响应体
Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding:支持哪种编码格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language:告诉浏览器,它的语言环境Cache-Control:缓存控制
Connection:告诉浏览器,请求完成是断开还是保持连接
HOST:主机....
Refresh:告诉客户端,多久刷新一次;Location:让网页重新定位;
Location: 让网页重新定位
2)、响应状态码
200: 请求响应成功
3xx: 请求重定向
4xx: 如找不到资源(404)
5xx: 如服务器代码错误(500) 网关错误(502)
三.Maven
我为什么要学习这个技术?1.在Javaweb开发中,需要使用大量的jar包,我们手动去导入;2.如何能够让一个东西自动帮我导入和配置这个jar包。由此,Maven项目架构管理工具诞生了!
Maven记住约定大于配置(项目目录结构按照规范来)
1.下载
下载完成后,解压即可;
2.Maven环境变量配置
在我们的系统环境变量中配置如下配置:
M2_HOME:maven目录下的bin目录
MAVEN_HOME:maven的目录
在系统的path中配置%MAVEN_HOME%\bin
测试环境变量配置成功:
3.配置阿里云镜像
作用:加速我们的下载(比如自动导包的速度)
在Maven配置文件D:\Environment\apache-maven-3.8.1\conf\settings.xml中加入以下代码:
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
4.创建本地仓库
作用:存放jar包
<localRepository>D:\Environment\apache-maven-3.8.1\maven-repo</localRepository>
5.IDEA中使用Maven
1.新建Maven工程项目
2.选择我们自己的Maven(3.8.1),同时选择配置文件及本地仓库路径
3.等待下载完依赖包到本地仓库中显示构建成功,并在本地仓库中看多了很多jar包。
6.pom.xml文件
pom.xml是Maven的核心配置文件
Maven由于约定大于配置,如果不按照约定在规定文件夹内创建对象的文件,会导致无法导出或则无法生效(比如我们自己写的配置文件如果乱放的话),解决方法:
7.web.xml版本修改
IDEA中Maven的web应用项目web.xml版本配置保持与tomcat的一致。(为了让环境达到最优)
原来:
改为:
四.IDEA配置tomcat服务器
1.tomcat基本配置:
2.解决警告问题:
3.添加虚拟路径映射
启动tomcat打开的地址为http://localhost:8080/liqingfeng,既当前web项目的根路径。
五.Servlet
1.概念
Servlet就是sun公司开发动态web的一门技术
Sun在这些APi中提供一个接口叫做:Servlet,如果你想开发一个Servlet程序,只需要完成两个小步骤:
- 1.编写一个类,实现Servlet接口 把开
- 2.将写好的java类部署到web服务器中。
2.Servlet原理
执行原理:
Servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会:
1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
2. 查找web.xml文件,是否有对应的标签体内容。
3. 如果有,则在找到对应的全类名
4. tomcat会将字节码文件加载进内存,并且创建其对象
5. 调用其方法
3.Mapping映射
在web.xml中需要将我们写的java类映射,如下,当请求localhost:8080/hello
就会在服务器中找到com.kuang.servlet.HelloServlet这个类去执行其doGet或则doPost方法。
<!--注册Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
*:通配符。如下表示当请求localhost:8080/hello
(*为空时) 或localhost:8080/hello/......
(*为有值时)下的所有url路径就会在服务器中找到com.kuang.servlet.HelloServlet这个类去执行其doGet或则doPost方法。
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
4.ServletContext
web服务器在启动的时候,它会为每个web应用程序都创建一个对应的ServletContext对象,它代表了当前的web应用;
getRealPath方法: 获取文件真实工作路径
System.out.println(
this.getServletContext().getRealPath("/WEB-INF/classes/db.properties"));
//D:\Environment\apache-tomcat-9.0.48\webapps\digitalreview\WEB-INF\classes\db.properties
**getMimeType方法:**获取MIME类型
MIME类型:在互联网通信过程中定义的一种文件数据类型
格式: 大类型/小类型 (如:text/html,image/jpeg)
1)、共享数据
在一个servlet的servletContext 对象中添加一个属性,在另一个servlet的servletContext 获取该属性的值
public class Servletcontext1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext(); //获取当前web应用的ServletContext对象,它表示当前web应用
servletContext.setAttribute("username","王老五"); //给servletContext添加一个新属性值,其属性为username,值为王老五
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
public class Servletcontext2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext(); //获取当前web应用的ServletContext对象,它表示当前web应用
Object username = servletContext.getAttribute("username"); //获取存放在servletContext中的属性(username)的值
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
resp.getWriter().print("username:" + username); //打印在页面上
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
web.xml配置该类映射
<servlet>
<servlet-name>setservletcontext</servlet-name>
<servlet-class>com.liqingfeng.Servletcontext1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>setservletcontext</servlet-name>
<url-pattern>/setdata</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getservletcontext</servlet-name>
<servlet-class>com.liqingfeng.Servletcontext2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getservletcontext</servlet-name>
<url-pattern>/getdata</url-pattern>
</servlet-mapping>
2).请求转发
当请求http://localhost:8080/liqingfeng/rd
时,会执行该类,然后会转发到http://localhost:8080/liqingfeng/getdata
路径去执行那个路径所映射的类,但浏览器url地址栏一直显示http://localhost:8080/liqingfeng/rd
。
public class Requestdispatcher extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.println("进入了ServletDemo04");
RequestDispatcher requestDispatcher = context.getRequestDispatcher("/getdata"); //请求转发的路径
requestDispatcher.forward(req,resp); //调用forward实现请求转发;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
web.xml配置该类映射
<servlet>
<servlet-name>requestdispatcher</servlet-name>
<servlet-class>com.liqingfeng.Requestdispatcher</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>requestdispatcher</servlet-name>
<url-pattern>/rd</url-pattern>
</servlet-mapping>
3).资源文件读取
user.properties资源文件放在resources文件夹,在启动tomcat时会在target下的java-01-maven(当前web项目根路径)WEB-INF/classes下生成user.properties文件。
public class Readresource extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/user.properties"); //绝对路径,第一个/是"http://localhost:8080/liqingfeng/"
System.out.println(is);
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.getWriter().print(user+":"+pwd);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
web.xml配置该类映射
<servlet>
<servlet-name>readresource</servlet-name>
<servlet-class>com.liqingfeng.Readresource</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>readresource</servlet-name>
<url-pattern>/read</url-pattern>
</servlet-mapping>
5.Request
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器, HTTP请求中的所有信息会被封装到HttpServletRequest,通过HttpServletRequest的方法,获得客户端的所有信息。
在request请求中存放的数据,生存期随着请求消失而消失,多个Servlet不能共享,除非这个请求一直在这几个Servlet中来回。
在ServletContext中存放的数据,生存期随着web应用关闭而消失,多个Servlet可以共享。
req.getParameter()和req.getAttribute()方法的区别:
getParameter() :该方法是用于客户端传递过来的参数,它的返回值类型永远是字符串类型这个赋值动作是有客户端完成的。
getAttribute():该方法是专用在请求传到服务器端后,在去使用其进行存取一些附加数据。它的返回类型永远是Object.
问题:
既然parameter和attribute都是传递参数,为什么不直接使用parameter呢?
原因:
1)从上面分析可以找到getParameter获取的是客户端发送的参数,而且在服务器端不能通过setParameter(key, value)来添加参数,因为没有这个函数,所以如果需要在服务器端进行跳转,并需要向下个页面发送新的参数时,则没法实现。但是attribute可以,可以通过setAttribute(),将值放入到request对象,然后在其他页面使用getAttribute获取对应的值,这样就达到一次请求可以在多个页面共享一些对象信息
2)parameter返回值是字符串,意味着不能传递其他的对象,如List,但是attribute则可以存放任意类型的Java对象
getContextPath(): 获取当前web项目的虚拟路径(即tomcat配置的web项目访问的根路径)
public class Request extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username"); //获取浏览器请求过来的参数值
String[] hoppys = req.getParameterValues("hoppys"); //获取浏览器请求过来的参数值数组
System.out.println(username);
for (String hoppy : hoppys) {
System.out.println(hoppy);
}
req.getRequestDispatcher("/login.jsp").forward(req,resp);//请求转发,/表示当前web项目的根路径
request.setAttribute("list","对象"); //还可以往本次请求中加数据,该请求到了其他地方可以取这个属性值
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>request</servlet-name>
<servlet-class>com.liqingfeng.Request</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>request</servlet-name>
<url-pattern>/req</url-pattern>
</servlet-mapping>
当发送请求http://localhost:8080/liqingfeng/req?username=老李&hoppys=game&hoppys=java
时,工作台打印:
同时跳转到http://localhost:8080/liqingfeng/login.jsp
页面,但由于是请求转发,所以浏览器url地址栏不会变。
请求转发:req.getRequestDispatcher(String path).forward(req,resp)
1. 浏览器地址栏路径不发生变化
2. 只能转发到当前服务器内部资源中。(path就不能写当前服务器资源外的路径)
3. 转发是一次请求(通过set/getgetAttribute在请求中加入信息给转发的资源共享数据)
6.Response
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse;
- 如果要获取客户端请求过来的参数:找HttpServletRequest
- 如果要给客户端响应一些信息:找HttpServletResponse
2)下载文件
下载:从服务器下载资源到客户端用户
上传:从客户端用户上传数据到服务器存储着
public class Response extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 要获取下载文件的路径
String realPath = "C:\\Users\\秋天的思念\\Desktop\\Java\\Java web\\javaweb\\java-01-maven\\src\\main\\resources\\img\\img1.jpg";
// 2. 下载的文件名是啥?
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
System.out.println(realPath.lastIndexOf("\\"));//81
// 3. 设置想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件名URLEncoder.encode编码,否则有可能乱码
resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
//设置响应头ContentType,告诉浏览器本次响应体数据格式以及编码格式
//resp.setHeader("ContentType","text/html;charset=utf-8");
resp.setContentType("text/html;charset=utf-8");
// 4. 获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
// 5. 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
// 6. 获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
// 7. 将输入流的内容按照1024字节一次循环读取到buffer中,然后响应输出流输出到浏览器页面
while ((len=in.read(buffer))>0){
out.write(buffer,0,len); //写到了响应体中
}
in.close();
out.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
3)重定向
1. 地址栏发生变化
2. 重定向可以访问其他站点(服务器)的资源
3. 重定向是两次请求。不能使用request对象来共享数据
//重定向
public class SendRedirect extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("走到了SendRedirect类的doGet方法");
resp.sendRedirect("/liqingfeng/sendRedirect.jsp");//重定向到http://localhost:8080/liqingfeng/sendRedirect.jsp
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>sendredirect</servlet-name>
<servlet-class>com.liqingfeng.SendRedirect</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sendredirect</servlet-name>
<url-pattern>/sd</url-pattern>
</servlet-mapping>
在浏览器url地址栏输入http://localhost:8080/liqingfeng/sd
,会走到SendRedirect类的doGet方法,然后重定向到http://localhost:8080/liqingfeng/sendRedirect.jsp
路径写法
1. 路径分类
1. 相对路径:通过相对路径不可以确定唯一资源
* 如:./index.html
* 不以/开头,以.开头路径
* 规则:找到当前资源和目标资源之间的相对位置关系
* ./:当前目录
* ../:后退一级目录
2. 绝对路径:通过绝对路径可以确定唯一资源
* 如:http://localhost/day15/responseDemo2 /day15/responseDemo2
* 以/开头的路径
* 规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
* 给客户端浏览器使用:需要加虚拟目录(项目的访问路径):/day15/responseDemo2
* 建议虚拟目录(/dat15)动态获取:request.getContextPath()
* 如<a> , <form>, 重定向...
* 给服务器使用:不需要加虚拟目录:/responseDemo2
* 如请求转发的路径
六.Cookie、Session
会话:用户打开一个浏览器,访问web资源,最后关闭浏览器,这个过程可以称之为会话。
有状态会话:一个用户来过某个网站,下次再来该网站,还能识别该用户,称之为有状态会话。
cookie: 服务器端给客户端一个信件,客户端下次访问服务端带上信件就可以了。
所以cookie属于客户端技术 。
session: 服务器登记你来过了,下次你来的时候我来匹配你。
所以session属于服务器技术。
1.Cookie
客户端会话技术,将数据保存到客户端浏览器;浏览器发起请求的请求头中会包含cookie。服务器响应的响应头中会包含set-cookie
cookie只在当前浏览器有效,其他浏览器中不存在该cookie。
每个cookie只能保存一个键值对(name:value),而session可以保存对象及多个键值对,所以后面都是session用的多(而且cookie作用域只在当前页面,session作用域在整个浏览器)
响应头中会有set-cookie:键值对、键值对…
请求头会有cookie:键值对、键值对…
public class Cookies extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置响应的消息体的数据格式以及编码 text/html:charset=utf-8
response.setContentType("text/html;charset=utf-8");
//1.请求头中有cookie,获取所有Cookie
Cookie[] cookies = request.getCookies();
boolean flag = false;//没有cookie为lastTime
//2.遍历cookie数组
if(cookies != null && cookies.length > 0){
for (Cookie cookie : cookies) {
//3.获取cookie的名称
String name = cookie.getName();
//4.判断名称是否是:lastTime
if("lastTime".equals(name)){
//有该Cookie,不是第一次访问
flag = true;//有lastTime的cookie
//获取Cookie的value,时间
String value = cookie.getValue();
System.out.println("解码前:"+value);
//URL解码:
value = URLDecoder.decode(value,"utf-8");
System.out.println("解码后:"+value);
response.getWriter().write("<h1>欢迎回来,您上次访问时间为:"+value+"</h1>");
//获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = sdf.format(new Date());
System.out.println("编码前:"+str_date);
//URL编码
str_date = URLEncoder.encode(str_date,"utf-8");
System.out.println("编码后:"+str_date);
cookie.setValue(str_date);
//设置cookie的存活时间
cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
response.addCookie(cookie);//添加到响应头中 cookie:键值对
break;
}
}
}
if(cookies == null || cookies.length == 0 || flag == false){//没有,第一次访问,设置Cookie的value
//获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = sdf.format(new Date());
System.out.println("编码前:"+str_date);
//URL编码
str_date = URLEncoder.encode(str_date,"utf-8");
System.out.println("编码后:"+str_date);
//创建新的cookie
Cookie cookie = new Cookie("lastTime",str_date);
//设置cookie的存活时间
cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
//添加到响应头中 set-cookie:键值对
response.addCookie(cookie);
response.getWriter().write("<h1>您好,欢迎您首次访问</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>cookie</servlet-name>
<servlet-class>com.liqingfeng.Cookies</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cookie</servlet-name>
<url-pattern>/cookie</url-pattern>
</servlet-mapping>
Cookie在浏览器中的存活时间:
1. 默认情况下,当浏览器关闭后,Cookie被销毁
2. 持久化存储:通过setMaxAge(int seconds)方法
(1). seconds为正数:指定cookie存活时间,时间到后cookie自动被销毁(将Cookie数据写到硬盘的文件中,持久化存储)
(2). seconds为负数:默认值(浏览器关闭cookie就被销毁)
(3). seconds为零:销毁该cookie
在上述情况下,若设置的cookie存活时间比较长,那么在此期间关闭浏览器,再打开浏览器,cookie仍然存在,直到存活时间到才被销毁。
cookie共享问题:
1. 假设在一个tomcat服务器中部署了多个web项目,那么这些web项目中cookie能不能共享?
* 默认情况下cookie不能共享
* setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录
* 如果要共享,则可以将path设置为"/",即当前tomcat服务器下的所有web项目都可访问共享
2. 不同的tomcat服务器间cookie共享问题?
* setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享
* setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com中cookie可以共享
2.Session
服务器端会话技术,在一次会话的多次请求间共享数据(多个servlet可以共享),将数据保存在服务器端的对象中。默认情况下关闭浏览器session被销毁,再次打开浏览器访问时session自动又被重新创建。 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在。
原理:
session依赖cookie实现。当浏览器发起请求,服务器给出的响应头中有set-cookie:JSESSIONID=xxxxxxxxx,并自动在服务器端创建该id的session对象,这样该浏览器在服务器断就有了唯一的session标识。再次访问服务器时,请求头中有cookie:JSESSIONID=xxxxxxxxx,服务器就会根据这个session的id找到是哪个浏览器的session对象。
所以,一个浏览器只有一个session(但可以有多个cookie),该浏览器只要访问服务器,保存在session中的数据就可以在servlet和jsp中共享。
Session和cookie的区别:
1. session存储数据在服务器端,Cookie在客户端
2. session没有数据大小限制、类型也无限制,Cookie有
3. session数据安全,Cookie相对于不安全
public class Sessions 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不用我们创建,服务器会给每一个用户(浏览器)创建一个Session对象
HttpSession session = req.getSession(); //获取session
session.setAttribute("user",new Person("王老吉",new Random().nextInt(10))); //往session中添加新的属性值(键值对)
String sessionId = session.getId(); //获得session的id
if(session.isNew()){ //判断session是否为新创建的
resp.getWriter().print("session创建成功,ID:"+sessionId); //第一次请求该Servlet都会执行这一行,如果本次请求Servlet中销毁了session,则下次请求仍会执行这一行
}
else{
resp.getWriter().print("session已经在服务器中存在了,ID:"+sessionId);//如果本次请求Servlet中没有销毁session,则下次请求就会执行这一行
}
//Session创建的时候做了什么事情;
// Cookie cookie = new Cookie("JSESSIONID",sessionId);
// resp.addCookie(cookie);
Person person = (Person) session.getAttribute("user"); //获取session中user属性的值
System.out.println(person);
session.removeAttribute("user"); //删除user属性和对应的值
// session.invalidate(); //手动销毁当前发起请求的浏览器session,然后服务器又会自动创建一个新的session给该浏览器,且sessionid会变
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>session</servlet-name>
<servlet-class>com.liqingfeng.Sessions</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>session</servlet-name>
<url-pattern>/session</url-pattern>
</servlet-mapping>
Session存活时间:
方法1:session.invalidate() //手动代码实现服务器端销毁该Session对象
方法2:关闭浏览器,自动销毁当前Session的id,下次访问不是同一个session
方法3:web.xml配置 //时间到服务器端自动销毁该Session对象(浏览器不关闭,时间到该session就被销毁)
<!--设置Session默认的有效时间-->
<session-config>
<!--15分钟后Session自动失效,以分钟为单位-->
<session-timeout>15</session-timeout>
</session-config>
浏览器关闭再次访问,服务器不关闭时session不是同一个问题
因为默认情况下,当getSession()后,session就被被创建。session在创建时,服务器会通过Cookie返回session 的ID给浏览器,之后服务器根据浏览器Cookie里的session的ID来分辨不同浏览器。但是,这种方法返回的cookie是保存在浏览器的内存中,浏览器关闭后内存会被清理,所以该sessionid在浏览器关闭后就失效了。再次访问服务器,服务器又会重新给浏览器一个新的sessionid和创建对应的session对象。虽然在服务器保存的session会在有效期后才会被销毁,但是浏览器的cookie里没有之前的session的ID,服务器就不能判断出当前用户是否是原先的那个用户(即使之前那个seesion对象还在)。
所以,设置session的有效时间只是服务器端保证该session对象有效时间到了就被销毁。浏览器端关闭后若要想再次访问时session还是同一个,则需要保证sessionid为同一个,这样服务器端才能识别该浏览器。
解决方法如下(该cookie最大存活时间应<=服务器端设施的有效时间,否则服务器端有效时间到会自动销毁当前id的session对象,这样浏览器的sessionid就匹配不了被销毁的那个session对象)
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60*60); //该cookie最大存活时间=
response.addCookie(c);
客户端不关闭,服务器关闭后,两次获取的session也不是同一个。但是要确保数据不丢失,tomcat自动完成以下工作,所以浏览器端不用担心session数据丢失。
* session的钝化:
* 在服务器正常关闭之前,将session对象系列化到硬盘上
* session的活化:
* 在服务器启动后,将session文件转化为内存中的session对象即可。
七.JSP
1.原理
Java Server Pages : Java服务器端页面,也和Servlet一样,用于动态Web技术!
最大的特点:写JSP就像在写HTML。
区别:HTML只给用户提供静态的数据,JSP页面中可以嵌入JAVA代码,为用户提供动态数据。
找到该路径:
C:\Users\秋天的思念\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\Unnamed_java-01-maven\work\Catalina\localhost\liqingfeng\org\apache\jsp
发现jsp转变成了Java程序!
所以,浏览器向服务器发送请求,不管访问什么资源,其实本质都是访问Servlet!JSP最终也会被转换成为一个Java类!JSP 本质上就是一个Servlet。
login.jsp和login_jsp.java对比可知,其实jsp就是简化了java代码书写。在JSP页面中,只要是 JAVA代码就会原封不动的输出;如果是HTML代码,就会被转换为:out.write("<html>\r\n");
这样的格式,输出到前端页面。
JSP九大内置对象,这些对象我们可以在JSP中直接访问使用
变量名 真实类型 作用
* pageContext PageContext 当前页面共享数据,还可以获取其他八个内置对象
* request HttpServletRequest 一次请求访问的多个资源(转发)共享数据
* session HttpSession 一次会话的servlet、jsp之间共享数据
* application ServletContext 服务器的资源都可以共享数据
* response HttpServletResponse 响应对象
* page Object 当前页面(Servlet)的对象 this
* out JspWriter 输出数据到页面上
* config ServletConfig Servlet的配置对象
* exception Throwable 异常对象
2.JSP语法
JSP脚本:
1.<%= java代码%>(用来将程序输出(out.print(java代码)),输出到客户端,在jsp .java的service方法中)
<%= new java.util.Date()%>
<%= i%>
2.<% java代码%>(用来放java代码,在jsp .java的service方法中)
<%
int sum = 0;
for (int i = 1; i <=100 ; i++) {
sum+=i;
}
out.println("<h1>Sum="+sum+"</h1>");
%>
2.<%! java代码%>(用来声明改类的成员(变量或方法),在jsp .java的类中)
<%!
static {
System.out.println("Loading Servlet!");
}
private int globalVar = 0;
public void kuang(){
System.out.println("进入了方法Kuang!");
}
%>
JSP指令:
用于配置JSP页面,导入资源文件
<%@ 指令名称 属性名1=属性值1 属性名2=属性值2 … %>
指令名称:
page: 配置JSP页面
include : 包含其他页面,导入页面的资源文件
taglib: 导入资源
<%@page args.... %>
<%@include file=""%>
<%--@include 会将两个页面合二为一--%>
<%@include file="common/header.jsp"%>
<h1>网页主体</h1>
<%@include file="common/footer.jsp"%>
JSP标签:
<jsp: …> </jsp:…>
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name" value="kuangshen"></jsp:param>
<jsp:param name="age" value="12"></jsp:param>
</jsp:forward>
3.JSTL(JSP标准标签库)
在pom.xml导入两个依赖包
<!-- JSTL表达式的依赖 -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- standard标签库 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
分为格式化标签,SQL标签,XML 标签,核心标签 (掌握部分),JSTL 函数
详解可参考https://www.runoob.com/jsp/jsp-jstl.html
以<c:forEach>遍历为例:
<%--导入核心标签库才能使用--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
ArrayList<String> people = new ArrayList<>();
people.add(0,"张三");
people.add(1,"李四");
people.add(2,"王五");
people.add(3,"赵六");
people.add(4,"田六");
request.setAttribute("list",people);//在本次请求存放数据(list:people),随着请求消失数据也消失,这样节约资源
%>
<%--
var, 临时变量
items, 要遍历的对象
begin, 哪里开始
end, 到哪里
step, 步长
--%>
<c:forEach var="people" items="${list}">
<c:out value="${people}"/> <br>
</c:forEach>
<hr>
<c:forEach var="people" items="${list}" begin="1" end="3" step="1" >
<c:out value="${people}"/> <br>
</c:forEach>
4.EL表达式
格式:${表达式}
作用:替换和简化jsp页面中java代码的编写
jsp默认支持el表达式的。如果要忽略el表达式1.设置jsp中的page指令:isELIgnored=“true” 忽略当前jsp页面中所有的el表达式;2.\${表达式}
:忽略当前这个el表达式
使用:(具有2个功能)
1. 运算:
1. 算数运算符: + - * /(div) %(mod) 如${3/4},浏览器访问该jsp将显示0
2. 比较运算符: > < >= <= == !=
3. 逻辑运算符: &&(and) ||(or) !(not)
4. 空运算符: empty
* 功能:用于判断字符串、集合、数组对象是否为null或者长度是否为0
* ${empty list}:判断字符串、集合、数组对象是否为null或者长度为0
* ${not empty str}:表示判断字符串、集合、数组对象是否不为null 并且 长度>0
2. 获取值
1. el表达式只能从域对象中获取值
2. 语法:
1. ${域名称.键名}:从指定域中获取指定键的值
* 域名称:(域的大小排名pageContext<request<session<application)
1. pageScope --> pageContext
2. requestScope --> request
3. sessionScope --> session
4. applicationScope --> application(ServletContext)
* 举例:在request域中存储了name=张三(request.setAttribute(name","张三"))
* 获取:${requestScope.name}
2. ${键名}:表示依次从最小的域中开始查找是否有该键对应的值,直到找到为止。
3. 获取对象、List集合、Map集合的值
1. 对象:${域名称.键名.属性名}
*属性:类中set/get方法去掉set和get,首字母小写后的字符串
* ${域名称.键名.属性名}本质上是调用该键名对应对象的get方法,如{user.name}:调用user对应的对象(假设也为user)的user.getName方法返回的name值
2. List集合:${域名称.键名[索引]}
3. Map集合:
* ${域名称.键名.key名称}
* ${域名称.键名["key名称"]}
3. 隐式对象:
* el表达式中有11个隐式对象
* pageContext隐式对象:(可以获取jsp其他八个内置对象)
* ${pageContext.request.contextPath}:动态获取虚拟目录,实质调用pageContext.getRequest.getContextPath。
八.JavaBean
JavaBean即实体类,一般用来和数据库的字段做映射 ORM
JavaBean有特定的写法:必须要有一个无参构造,属性必须私有化,必须有对应的get/set方法;
ORM :对象关系映射
数据库表——一个类
表中一个字段——类的一个属性
表中一行数据——类的一个实例化对象
九.MVC三层架构
MVC( Model view Controller):即模型、视图、控制器架构
以前的架构:
用户直接访问控制器,控制器就可以直接操作数据库。
MVC三层架构:各层只做自己的事,分工明确
Model
业务层:业务逻辑(Service)
数据持久层:CRUD (Dao - 数据持久化到数据库)
View
展示数据(jsp)
提供链接发起Servlet请求 (a,form,img…)
Controller
接收用户的请求,获取数据:(req:请求参数、Session信息….)
交给业务层处理对应的代码
控制视图的跳转
十.Filter过滤器
过滤器 :用来过滤网站的数据。
可以用来处理中文乱码,登录验证……
过滤器执行流程:
1.执行过滤器
2.执行放行后的资源(jsp、servlet)
3.回来执行过滤器放行代码下边的代码
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//是由请求触发的filter,Request的处理放这里
servletRequest.setCharacterEncoding("utf-8");
//放行
filterChain.doFilter(servletRequest,servletResponse);
//响应后走到这里,Response的处理放这里
servletResponse.setContentType("text/html;charset=UTF-8");
}
过滤器生命周期方法:
1.init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源;2.doFilter:每一次请求被拦截资源时,会执行。执行多次;3. destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源。
拦截方式配置资源被访问的方式:
* 注解配置:
* 设置dispatcherTypes属性
1. REQUEST:默认值。浏览器请求时触发filter @WebFilter(dispatcherTypes = DispatcherType.REQUEST)
2. FORWARD:服务器请求转发时触发filter
3. INCLUDE:包含访问资源时触发filter
4. ERROR:错误跳转资源时触发filter
5. ASYNC:异步访问资源时触发filter
* web.xml配置
* 设置<dispatcher></dispatcher>标签即可
过滤器链(配置多个过滤器)
执行顺序:
如果有两个过滤器:执行顺序为过滤器1和过滤器21. 过滤器1;2. 过滤器2;3. 资源执行;4. 过滤器2;5. 过滤器1
过滤器执行先后顺序问题:
1.都采用注解配置:按照类名的字符串比较规则比较,值小的先执行。如: AFilter 和 BFilter,AFilter就会先执行。2. 都采用web.xml配置: 谁定义在上边,谁先执行
自己的过滤器必须实现Filter(javax.servlet.Filter)接口,在doFilter方法中写需要过滤的代码,切记加上chain.doFilter(request,response);
过滤器使用的request,response放行后到其他jsp(servlet)时的request,response是同一个对象,所以在filter里对request,response进行d的操作,是直接影响放行后在jsp(servlet)的request,response对象。
//过滤器实现字符编码
public class Filters implements Filter {
@Override
//初始化:web服务器启动,就以及初始化了,随时等待过滤对象出现!
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter初始化");
}
@Override
/*
1. doFilter方法,在过滤特定请求的时候都会执行
2. 必须要让过滤器放行
chain.doFilter(request,response);
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("CharacterEncodingFilter执行前....");
chain.doFilter(request,response); //让我们的请求继续走,如果不写,程序到这里就被拦截停止!
System.out.println("CharacterEncodingFilter执行后....");
}
@Override
//销毁:web服务器关闭的时候,过滤器会销毁
public void destroy() {
System.out.println("Filter销毁");
}
}
web.xml配置过滤器映射
只要是 http://localhost:8080/liqingfeng/session的任何请求,会先经过这个过滤器。
<filter>
<filter-name>Filters</filter-name>
<filter-class>com.liqingfeng.Filters</filter-class>
</filter>
<filter-mapping>
<filter-name>Filters</filter-name>
<url-pattern>/session/*</url-pattern>
</filter-mapping>