Java Web
可以通过浏览器对Java编写的内容进行访问
Java Web基于请求响应开发的
请求request:浏览器或者客户端给服务器发送数据
响应response:服务器给客户端返回数据,读音为rɪˈspɑːns
请求和响应是成对出现的,有请求就有响应
Tomcat
下载后选择一个目录解压
以下为相关的目录结构
├───bin 存放可执行程序
├───conf 存放服务器的配置文件
├───lib 存放jar包
├───logs 存放tomcat运行时的日志
├───temp 存放运行时的临时数据
├───webapps 存放web部署的工程
└───work 工作的目录,放一些翻译的源码
启动服务器:执行bin目录下的startup.bat
测试是否启动成功:打开浏览器,打开地址localhost:8080
进行测试
关闭服务器:
- 点击命令行的关闭按钮
- 再打开的命令行,按
CTRL + C
- 执行bin目录下的
shutdown.bat
修改端口号:
修改以下标签中的port,范围1-65535
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
修改完之后,重启tomcat才会生效
将内容部署到服务器
访问:localhost:端口号/文件夹名称
方式1
在tomcat的webapps中新建一个目录,将文件拷贝进去即可
方式2
在\conf\Catalina\localhost
下新建一个xml配置文件,名称随意
<Context path="/映射名称" docBase="本地路径" />
例如<Context path="/book" docBase="C:/Users/singx/OneDrive/Java/IDEA/JavaWeb/Book" />
访问:localhost:端口号/映射名称
使用IP地址+端口号和直接输入本地路径进行访问的不同
输入本地路径进行访问时,使用的协议是file://
协议,表示告诉浏览器直接读取后边的路径,一切都在本地执行,不走网络
如果使用http://ip+端口号
访问
如果仅输入http://localhost:端口号
,代表此时访问的是Tomcat下的webapps
文件夹中的ROOT
文件
----webapps下的文件夹---
├───Book
├───docs
├───examples
├───host-manager
├───manager
└───ROOT
idea也可以绑定tomcat
文件 | 设置 | 构建、执行、部署 | 应用程序服务器
idea创建动态的web工程
在创建的模块或者项目上右击,选择添加框架(framework 读音ˈfrāmˌwərk
)支持,勾选web应用程序
,勾选创建web.xml
此时创建好的动态的web工程为
src
目录存放自己编写的Java
代码web
目录放置web
工程的资源文件,比如html css javascript
文件web
目录下的WEB-INF
是一个受服务器保护的目录,浏览器无法直接访问此目录的内容web.xml
文件是保存着项目配置的描述文件- 可以新建一个
lib
文件,里边存放jar
包
如果没有配置文件web.xml
,可以选择 文件-项目结构-facet-当前的web模块
,点击+
号
可以编辑tomcat配置,之后点击运行按钮可以正常运行tomcat实例,可以修改为热部署,当资源修改时,自动更新,不需要再次重启服务器进行更新资源
Servlet
小服务程序
是Java EE
规范之一,规范就是接口,也是Java Web
的三大组件之一
servlet是运行在服务器上的一个Java小程序,主要的作用是:接收客户端发来的请求,并响应数据给客户端
实现servlet
-
首先需要将
tomcat\lib
下的servlet-api.jar
导入到项目中 -
import javax.servlet.*;
-
新建一个类,使其实现
Servlet
接口-
public class ServletTest implements Servlet{ }
-
-
主要是实现其中的
service()
方法,处理请求,响应数据-
@Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("Service Test类被使用了"); }
-
-
去
web.xml
配置文件中配置Servlet
程序的访问地址,一个文件中可以有多个<servlet>、<servlet-mapping>
标签,因为一个页面中可能有不同的需求-
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 属于上下文参数,输入整个工程,可以有多组--> <context-param> <param-name>paramname</param-name> <param-value>paramvalue</param-value> </context-param> <!-- servlet标签给Tomcat配置servlet程序--> <servlet> <!-- 为servlet取一个别名,一般为类名,还需要指定映射地址--> <servlet-name>HttpServletTest</servlet-name> <!-- servlet程序的全类名,包含包名--> <servlet-class>HttpServletTest</servlet-class> <!-- 初始化参数,为键(name)值(value)对的形式,可以有多组--> <init-param> <!-- 参数名--> <param-name>param</param-name> <!-- 参数值--> <param-value>key-value</param-value> </init-param> <!-- 比方说MySQL的用户名和地址可以写到这里--> <init-param> <!-- 参数名--> <param-name>name2</param-name> <!-- 参数值--> <param-value>value2</param-value> </init-param> </servlet> <!-- 指定映射地址--> <servlet-mapping> <!-- 告诉服务器,这个映射地址要给哪个servlet程序使用,一般和前边的别名相同--> <servlet-name>HttpServletTest</servlet-name> <!-- 配置访问的地址--> <!-- / 斜杠代表```http://ip:端口号/工程路径``` 工程路径为运行/调试配置中的url--> <!-- /test代表```http://ip:端口号/工程路径/test--> <url-pattern>/test</url-pattern> </servlet-mapping> <!-- servlet标签可以有多个--> <servlet> <servlet-name>servlet</servlet-name> <servlet-class>ServletTest</servlet-class> </servlet> <servlet-mapping> <servlet-name>servlet</servlet-name> <url-pattern>/tests</url-pattern> </servlet-mapping> </web-app>
-
pattern,中文为图案、模式、格局,读音为
ˈpadərn
-
-
可以尝试着运行tomcat,默认打开工程路径,在上例中,此时如果打开自行指定的
<url-pattern>/test</url-pattern>
中的地址,程序会自动执行指定的类中重写的service()
方法开始运行
打开自行指定的<url-pattern>/test</url-pattern>
中的地址后,自动执行了servlet
方法,在控制台最后一行可以看到
如果自行指定的<url-pattern></url-pattern>
标签中不加/
,运行时会报错,提示无效地址
执行过程
Servlet的生命周期
只有进入到http://ip:端口号/工程路径/自行制定的地址
后才会触发servlet
的各个方法,第1、2步仅在第一次进入到http://ip:端口号/工程路径/自行制定的地址
创建servlet
对象时才会调用
执行顺序为:
Servlet
实现类的构造器,仅第一次访问才会调用init()
初始化方法,仅第一次访问才会调用service()
方法,每次访问都会调用destroy()
销毁方法,只有在程序停止时才会执行
destroy,中文为摧毁、销毁、破坏,读音为dɪˈstrɔɪ
处理get请求和post请求
在servlet
的实现类中,有一个service
方法需要手动实现,该方法有两个参数,一个是请求参数request
(ServletRequest
类型)和响应参数response
,
在请求参数类型中,它的子接口HttpServletRequest
接口中有一个String getMethod()
的方法可以获取到它的请求的方法(对应着HTML表单中的method
属性)
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
System.out.println("第三被执行的service方法,请求方式为" + request.getMethod());
}
在实际开发中,如果对使用get或者post方法进行处理时,可能要进行很多的操作,可以将相关的操作都放在一个方法中,可以直接调用相关的方法进行处理
继承子类实现Servlet
在实际的开发中,很少直接实现Servlet
接口,通常都是继承其子类
可以继承HttpServlet
类,根据需要重写相关的方法,比方说doPost()
,doGet()
方法
idea可以快速生成servlet的配置文件
GenericServlet
为抽象类,实现了Servlet
接口,但仍需重写service()
方法,而HttpServlet
类继承于GenericServlet
,所以重写了service()
方法,在此基础上,又添加了许多方法,大部分的方法都是负责抛出异常的,比方说doGet()
和doPost()
这两个方法只会抛出异常,所以要有一个自己写的类继承自HttpServlet
类,而自己的类可以根据需要重写相关的方法(如果用得到的话,必须要重写,否则无法正常的使用)
ServletConfig类
Servlet
程序的配置信息类
param
中文为参数
由Tomcat负责创建,默认是第一次访问时创建
功能:
- 可以获取
Servlet
的别名,即<servlet-name></servlet-name>
的值 - 获取初始化参数
init-param
- 获取
ServletContext
对象,context中文为语境、景况
在手动实现的Servlet
接口中有一个public void init(ServletConfig config)
方法,同样的,在HttpServlet
类的父类中也有这个方法(需要重写),在这个形式参数中,有一个ServletConfig
,可以通过该参数进行相关的配置信息获取等操作
获取别名
public void init(ServletConfig config) throws ServletException {
System.out.println("别名为:" + config.getServletName());
}
获取初始化值
public void init(ServletConfig config) throws ServletException {
System.out.println("初始化参数为param的值为:" + config.getInitParameter("param"));
System.out.println("初始化参数为name的值为:" + config.getInitParameter("name2"));
System.out.println("初始化参数为不存在的值为:" + config.getInitParameter("不存在"));
}
通过getInitParameter(String name)
方法,获取<init-param>
标签中的<param-name>
和<param-value>
,方法的参数name
和<param-name>
中的值相对应,返回<param-value>
的值,如果没有相关的键值对,则返回null
<init-param>
<!-- 参数名-->
<param-name>param</param-name>
<!-- 参数值-->
<param-value>key-value</param-value>
</init-param>
获取ServletContext
对象:getServletContext()
可能出现的错误
在HttpServlet
类中的其方法中,也可以使用servletConfig
的(前提是自己的实现类中没有重写init()
方法)但需要通过getServletConfig()
方法进行获取
因为在GenericServlet
类中,定义了 ServletConfig
,以下是相关的方法和属性
private transient ServletConfig config;
public ServletConfig getServletConfig() {
return this.config;
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
由此可见,再执行init(ServletConfig config)
方法时,会将获取到的ServletConfig config
给第一行的private transient ServletConfig config;
中的config
,而getServletConfig()
方法返回的是this.config
,即第一行中的config
,该属性在没有执行在GenericServlet
类中的init(ServletConfig config)
方法前是空的,如果子类中重写了init()
方法 而不去处理config
属性,此时的config
会一直为空,解决办法:
- 子类中重写
getServletConfig()
方法 - 调用父类的
init(ServletConfig config)
方法 在子类的,该操作无法实现,因为init(ServletConfig config)
方法中给config
属性赋值config
的权限修饰符为private
- 仅重写
init()
方法的空参重载的方法,因为在init(ServletConfig config)
方法中,调用了空参的init()
方法
如果在自定义类中重写了init()
方法,那么在其他方法中调用getServletConfig()
方法时,会抛出空指针异常的错误
public class HttpServletTest extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post请求");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//因为重写了init方法,此时调用getServletConfig()方法会报空指针异常
System.out.println("get请求000");
System.out.println("初始化参数为param的值为:" + getServletConfig().getInitParameter("param"));
System.out.println("初始化参数为name的值为:" + getServletConfig().getInitParameter("name2"));
System.out.println("初始化参数为不存在的值为:" + getServletConfig().getInitParameter("不存在"));
}
//重写了init方法
public void init(ServletConfig config) throws ServletException {
}
}
为避免此问题,可以在重写的方法中,加上super.init(config);
,默认还是调用父类的
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
ServletContext类
- 是一个接口
- 一个web工程只有一个
ServletContext
对象实例 - 是一个域对象
域对象:可以和map一样存、取、删除数据
作用:
- 获取web.xml中的
context-param
- 获取当前的工程路径
- 获取工程部署后在磁盘的绝对路径
- 像map一样存取数据
只能得到
<context-param>
<param-name>键</param-name>
<param-value>值</param-value>
</context-param>
中的内容
工程重启后自动销毁,数据也消失,可以在整个工程中存取数据
public void init() throws ServletException {
// 获取web.xml中的<context-param>
System.out.println("获取web.xml中的<context-param>" + getServletContext().getInitParameter("paramname"));
System.out.println("获取web.xml中的<context-param>的不存在的值" + getServletContext().getInitParameter("not find"));
// 获取当前的工程路径
System.out.println("当前的工程路径" + getServletContext().getContextPath());
// 获取工程部署后在磁盘的绝对路径
// "/"代表http://ip:port/工程名/
// 为运行时idea拷贝的路径
System.out.println("当前在磁盘的绝对路径" + getServletContext().getRealPath("/"));
// 像map一样存取数据
System.out.println("key的值为:" + getServletContext().getAttribute("key"));
// 存
getServletContext().setAttribute("key", "value");
// 取
System.out.println("key的值为:" + getServletContext().getAttribute("key"));
System.out.println("key的值为:" + getServletContext().getAttribute("key"));
}
}
请求
分为get请求和post请求
常用的请求头:
Accept
:客户端可以接收的数据类型Accept-Language
:客户端可以接收的语言类型User-Agent
:浏览器的信息Host
:请求时服务器的IP和端口号
get请求
请求行:
- 请求的资源路径:+?+请求参数
请求头
- key:value组成,不同的键值对表示不同的含义
post请求
分为:
- 请求行
- 请求头
- 请求体
分类
Get请求:
form
标签的method="get"
a
标签link
标签引入css
srcipt
标签引入js
文件img
标签插入图片iframe
引入html
页面- 浏览器地址栏输入地址敲回车
post请求:
form
标签的method="post"
响应的HTTP协议的格式
- 响应行
- 响应的协议和版本号 HTTP1.1
- 响应状态码 200
- 响应状态描述符 ok
- 响应头
- 键值对形式,不同的响应头有不同的含义
- 响应体
- 回传给客户端的数据
常见的响应状态码
状态码 | 含义 |
---|---|
200 | 表示请求成功 |
302 | 表示请求重定向 |
404 | 表示请求服务器已经收到了,但是所要的数据不存在,也就是请求地址错误 |
500 | 表示服务器已经收到请求了,但服务器内部错误(代码错误) |
HttpServletRequest类
只要有请求进入Tomcat服务器,Tomcat就会把请求过来的HTTP协议信息解析好封装在Request
对象中,然后传递到service()
方法中以供使用,可以通过HttpServletRequest
对象获取所有的请求信息
方法 | 含义 |
---|---|
getRequestURI() | 获取请求资源的路径 |
getRequestURL() | 获取请求资源的绝对路径 |
getRemoteHost() | 获取客户端的IP地址,中文为远程、远端、偏僻,读音为rəˈmōt |
getHeader() | 获取请求头 |
getParameter(key) | 获取请求的参数,例如表单里边的提交的内容可以查到,parameter,读音为pəˈræmɪtər ,中文为参数 |
getParameterValues() | 获取请求的参数,在有多个值的时候使用 |
getMethod() | 获取请求的方法,Get或者POST |
setAttrbute(key, value) | 设置域数据,其中,value类型是Object |
getAttribute(key) | 获取域数据 |
getRequestDispatcher("path") | 获取请求转发对象,dispatcher 读音dɪˈspætʃər ,中文 调度员,dispatch,中文为派遣、调度、发送 |
setCharacterEncoding("字符集编码") | 设置字符集编码,解决post提交中文乱码问题character 读音为ˈkærəktər ,一定要在获取请求之前调用!否则不生效 |
getScheme() | 获取协议,比如HTTP协议,返回字符串,读音skiːm ,中文计划/方案 |
getServerName() | 获取服务器IP地址 |
getServerPort() | 获取服务器端口号 |
getContextPath() | 获取工程路径 |
URI为设置的部署路径
+ <url-pattern>
中指定的地址
URL为http://IP:port/部署路径/<url-pattern>
URI = /web2/api
URL = http://localhost:8080/web2/api
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("URI = " + req.getRequestURI());
System.out.println("URL = " + req.getRequestURL());
// 如果使用local host进行访问得到的是0:0:0:0:0:0:0:1,即IPV6的IP地址
// 如果使用的是127.0.0.1进行访问,得到的是127.0.0.1
// 如果使用的是本机的IP地址,得到的是本机的IP地址
System.out.println("客户端的IP地址 = " + req.getRemoteHost());
System.out.println("请求头中的浏览器标识为:" + req.getHeader("user-agent"));
System.out.println("请求方式:" + req.getMethod());
}
获取get请求的内容
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String ip = req.getRemoteHost();
String equipment = req.getHeader("user-agent");
String username = req.getParameter("username");
String password = req.getParameter("password");
equipment = equipment.substring(equipment.indexOf("(") + 1, equipment.indexOf(";"));
System.out.println("---------------");
System.out.println("ip = " + ip);
System.out.println("equipment = " + equipment);
System.out.println("username = " + username);
System.out.println("password = " + password);
System.out.println("---------------");
}
相应的HTML代码
<form action="/web2/api" method="get">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<button type="submit">提交</button>
</form>
控制台输出的信息
---------------
ip = 0:0:0:0:0:0:0:1
equipment = Linux
username = 123
password = 123
---------------
如果表单中的相应值没有请求,则取出来的相应的值为null
如果请求的数据一个键有多个值时(例如表单使用checkbox复选框)可以使用getParameterValues(key)
,此时返回值是String[]
获取Post请求的内容
基本代码和Get请求一致
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//出现了中文乱码的情况,需要设置一下字符集编码,一定要在获取请求之前调用!
req.setCharacterEncoding("UTF-8");
String ip = req.getRemoteHost();
String equipment = req.getHeader("user-agent");
String username = req.getParameter("username");
String[] password = req.getParameterValues("password");
equipment = equipment.substring(equipment.indexOf("(") + 1, equipment.indexOf(";"));
System.out.println("----Post请求-----");
System.out.println("ip = " + ip);
System.out.println("equipment = " + equipment);
System.out.println("username = " + username);
System.out.println("password = ");
for (String s : password) {
System.out.println(s);
}
System.out.println("---------------");
}
请求转发
服务器收到请求后,服务器收到请求后,从一个资源跳转到另一个资源的操作叫做请求转发
在RequestDispatcher类中有一个转发的方法,为forward(request, response)
,中文为向前、发送,读音为ˈfôrwərd
可以进行的操作:
- 获取传递进来的参数
- 根据传递进来的参数,进行处理,在域对象中进行插入一个值做一个标记
- 获取转发的对象
- 进行转发
- 处理转发后的内容
默认进行访问的页面
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>测试</title>
</head>
<body>
<form action="/web2/requests" method="get">
用户名:<input type="text" name="username"><br>
<button type="submit">提交</button>
</form>
</body>
</html>
进行请求和转发的类
package package1;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class requests extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 象征意义的看一下传递进来的参数
System.out.println("用户名" + req.getParameter("username"));
// 做一个标记,代表已通过
req.setAttribute("flag", "success");
// 获取请求转发对象,参数中填写要转发进入的地址,要以"/"开头
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/forward");
// 进行转发
requestDispatcher.forward(req, resp);
}
}
转发后进行处理的类
package package1;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class forward extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 象征性的再次取出数据
System.out.println("转发后获取的用户名为:" + req.getParameter("username"));
// 看一下转发前的标记的内容
System.out.println("flag = " + req.getAttribute("flag"));
System.out.println("转发结束");
}
}
特点:
- 转发后的地址不变
- 只进行一次请求
- 共享域中的数据,即设置的键值对共享
- 可以转发到
WEB-INF
目录下 - 所设置的转发的路径中可以带有文件名(
.html
) - 不可以跳转到外部地址,例如
http://www.google.com
base标签
在使用请求转发的跳转过程中,如果这个页面中有如果有相对路径进行跳转到其他页面的操作时,若直接用相对路径跳转会出现问题。原因是请求转发后,转发后浏览器的地址不变,而相对路径就是针对当前浏览器的地址进行转发的,为解决这个问题,引入了<base href="为基准的地址">
标签,该标签可以尝试写在<head></head>
中
在base标签中,最后的具体文件名可以省略
比如<base href="http://localhost:8080/aaa/bbb/ccc.html">
等价于
<base href="http://localhost:8080/aaa/bbb/"
,最后的/
不可以省略,即bbb/
不能写为bbb
,省略了最后的/
,bbb
不会被认为是路径,而是被当作资源文件进行处理的
Web中/的含义
在web中是一个绝对路径,在服务器和浏览器中有不同的含义
被浏览器解析:
<a href = "/">斜杠</a>
- 这里的
/
代表http://ip:port
被服务器解析:
/
代表http://ip:port/工程名/
HttpServletResponse类
每次请求完毕,Tomcat都会创建一个response对象传递给servlet使用,如果设置返回给客户端的信息,可以对HttpServletResponse
对象进行设置
两个输出流
字节流:getOutputStream()
- 常用于下载,传递二进制数据
字符流:getWriter()
-
常用于回传字符串
-
返回
PrintWriter
-
PrintWriter
类中,有类似于System.out.printXXX()
的方法,这些方法作用效果是在浏览器网页中输出相关的内容 -
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().println("1234"); }
-
两个流只能同时用一个
直接输出中文会产生乱码,默认的字符集编码为ISO-8859-1
若将resp.setCharacterEncoding("UTF-8");
设置为UTF-8
后,中文还是会乱码,这是因为浏览器不知道需要解析的字符编码,还需要设置浏览器的响应头为UTF-8
,resp.setHeader("Content-Type", "text/html;charset=UTF-8");
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("resp.getCharacterEncoding() = " + resp.getCharacterEncoding());
resp.setCharacterEncoding("UTF-8");
resp.setHeader("Content-Type", "text/html;charset=UTF-8");
resp.getWriter().println("测试中文");
}
其中text/html
代表HTML格式,charset代表字符集
也可以使用resp.setContentType("text/html;charset=UTF-8");
直接设置,此时将服务器和客户端以及响应头都设置为了UTF-8
一定要在获取流之前调用才有效
请求重定向
方式1
客户端给服务器发送一个请求,服务器重定向一个地址让客户端访问
过程:
-
设置响应状态码为302
-
resp.setStatus(302);
-
-
设置响应头的
Location
为重定向后的地址-
resp.setHeader("Location", "重定向后的地址");
-
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("resp.getCharacterEncoding() = " + resp.getCharacterEncoding());
resp.setContentType("text/html;charset=UTF-8");
resp.setStatus(302);
resp.setHeader("Location", "地址");
}
上图就是设置响应状态码为302,响应头中的Location
为https://www.google.com
特点:
- 浏览器地址栏的地址有变化
- 是两次请求(第一次给服务器发送请求,第二次为服务器给出的响应
- 不共享
Request
域中的数据,即使用request.setAttribute(key, value)
的值不共享 - 无法访问
WEB-INF
文件夹下的东西 - 可以重定向至外部地址,例如
Google
等
方式2
直接使用resp.sendRedirect("地址")
redirect中文为重定向、重新使用、读音为ˌriːdəˈrekt
direct中文为直接的
Java EE三层框架
结构
- service为接口包
- implement实现接口的包
- utils为工具类的包
- test为测试的包
- 先创建相应地数据库
- 编写相应的
JavaBean
类
可以尝试进行解决
如果出现所有的类找不到了,可以尝试清除缓存
Debug
- 步过:将代码往下执行一行
- 步入:可以进入自定义的方法体内(第三方包属于自定义的范畴)
- 步出:跳出当前所在的方法
- 强制步入:对于所有的方法,无论是内置的还是自己写的都可以进入
- 运行到光标处:无论代码在哪里,只要光标之前没有断点,代码会直接运行到光标所在的行,相当于临时断点
- 恢复程序:执行到下一个断点处停止,如果没有下一个断点,则执行完程序
变量视图可以查看当前方法内所有有效的变量
JSP
全称为Java server pages
,即为Java服务器页面
代替Servlet
程序回传html
页面的数据,Servlet
回传数据比较麻烦,开发和维护成本都极高
例如
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class Res extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("<!DOCTYPE html>");
writer.write("<html lang=\"en\">");
writer.write("<head>");
writer.write("<meta charset=\"UTF-8\">");
writer.write("<title>Title</title>");
writer.write("</head>");
writer.write("<body>");
writer.write("<p>");
writer.write("testtttt");
writer.write("</p>");
writer.write("<button type = button>按钮</button>");
writer.write("</body>");
writer.write("</html>");
writer.println("中文中文");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>Res</servlet-name>
<servlet-class>Res</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Res</servlet-name>
<url-pattern>/res</url-pattern>
</servlet-mapping>
</web-app>
响应后的效果为
由此可见整个页面的相关代码在设置时特别繁琐
新建一个jsp
右键-新建-JSP/JSPX
jsp和HTML一样,都是存放在web目录底下,访问方式也和HTML一样
jsp本质上是一个servlet程序,第一次在浏览器打开jsp文件时,tomcat会自动将jsp文件翻译成相关的类,并将其编译为字节码文件,这个文件间接的继承了HttpServlet
类,也就是说,是一个servlet
程序
路径在
打开源文件,可以发现,生成的内容大致与自行写的writer.write("内容");
差不多
jsp的配置指令
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
可以修改页面中的一些重要的属性或者行为
属性 | 含义 |
---|---|
language | 表示jsp所翻译的语言,暂时只支持Java,也就是打开相关页面后,会翻译成相关的编程语言 |
contentType | 表示jsp的返回的数据类型,可以设置页面的字符集编码,相当于翻译文件后的response.setContentType("text/html;charset=UTF-8"); |
pageEncoding | 表示当前页面的字符集编码 |
import | 与Java中的作用相同,用于导包、导入类,例如<%@ page import="java.util.Scanner" %> |
autoFlush | 是给out 输出流使用的,flush ,中文为冲洗,读音为fləSH ,设置当out缓冲区满了之后,是否自动刷新缓冲区,默认为true |
buffer | 是给out 输出流使用的,设置缓冲区的大小,默认为8kb |
errorPage | 设置当前jsp页面出错后自动跳转到的路径(对于普通用户来说,可能会看不懂这是什么错误,可以指定到一个错误页面),即当前页面抛出异常时跳转去的页面,例如可能遇到下面jsp缓冲区溢出的错误后,直接跳转到设置的路径 |
isErrorPage | 设置当前页面是否是错误页面,默认为false,如果是true可以获取错误信息,会有相关的对象进行创建 |
session | 设置当前页面是否会创建HttpSession 对象,默认为true,读音为ˈseʃn ,中文为会议 |
extends | 设置翻译出来的Java类默认继承的类 |
对于给out
输出流使用的属性:
<%@ page autoFlush="false" buffer="1kb" %>
这时候复制几千行的内容,此时大小必定超过1kb了,这时候也把自动刷新缓冲区给关闭了,此时打开浏览器,给提示错误,抛出的异常为java.io.IOException
,具体信息是JSP缓冲区溢出
HTTP状态 500 - 内部服务器错误
类型 异常报告
消息 在 [7] 行处理 [/2.jsp] 时发生异常
描述 服务器遇到一个意外的情况,阻止它完成请求。
例外情况
java.io.IOException: 在 [7] 行处理 [/2.jsp] 时发生异常
4: Date: 2021/9/27
5: Time: 21:25
6: To change this template use File | Settings | File Templates.
7: --%>
8: <%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" language="java" %>
9: <%@ page autoFlush="false" %>
10: <html>
Stacktrace:
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:493)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:383)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:331)
javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
根本原因。
java.io.IOException: 错误:JSP缓冲区溢出
org.apache.jasper.runtime.JspWriterImpl.bufferOverflow(JspWriterImpl.java:151)
org.apache.jasper.runtime.JspWriterImpl.write(JspWriterImpl.java:330)
java.base/java.io.Writer.write(Writer.java:249)
org.apache.jsp._2_jsp._jspService(_2_jsp.java:626)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:465)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:383)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:331)
javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
):注意 主要问题的全部 stack 信息可以在 server logs 里查看
Apache Tomcat/8.5.71
可以使用默认设置就可以
jsp脚本
格式:
<%! 声明脚本的格式,可以给jsp翻译出来的类定义属性和方法,甚至是静态代码块,内部类 %>
<%@ page import="java.util.HashSet" %>
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>测试页面</p>
<%--声明类的属性--%>
<%!
public int id;
public String name;
public static HashSet<Integer> hashSet = new HashSet<>();
%>
<%--声明静态代码块--%>
<%!
static {
hashSet.add(2);
hashSet.add(3);
hashSet.add(32);
hashSet.add(52);
}
%>
<%--声明方法--%>
<%!
public int function(int a, int b){
return a + b;
}
%>
<%--声明内部类--%>
<%!
class innerClass{
int text;
}
%>
</body>
</html>
此时声明HashSet
时,会自动导入java.util.HashSet
<%@ page import="java.util.HashSet" %>
此时打开翻译好的文件,会发现:
翻译好的文件中有自定义的相关属性
如果自定义的jsp脚本中有System.out.printXXX();
语句,则默认在ide的终端中输出
表达式脚本
可以在jsp页面中输出:
- 整型
- 浮点型
- 对象
- ....
格式:
<%=值/变量名%>
<%--整型--%>
<%=111%>
<br>
<%--浮点型--%>
<%=111.111%>
<br>
<%--字符串--%>
<%="字符串"%>
<br>
<%--HashSet对象--%>
<%=hashSet%>
<br>
<%--调用函数--%>
<%=function(1, 4)%>
根据以上编写的代码,浏览器输出结果为
测试页面
111
111.111
字符串
[32, 2, 3, 52]
5
相应的,在翻译的Java源码中,有以下的内容与之对应:
out.print(111);
out.write("\r\n");
out.write("<br>\r\n");
out.write('\r');
out.write('\n');
out.print(111.111);
out.write("\r\n");
out.write("<br>\r\n");
out.write('\r');
out.write('\n');
out.print("字符串");
out.write("\r\n");
out.write("<br>\r\n");
out.write('\r');
out.write('\n');
out.print(hashSet);
out.write("\r\n");
out.write("<br>\r\n");
out.write('\r');
out.write('\n');
out.print(function(1, 4));
-
所有的表达式脚本会被翻译到
_jspService()
方法中,而这个方法所带有的两个参数为-
_jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
-
这就意味着,
request
和response
中的方法都可以正常使用 -
例如,获取请求的参数值
-
<%=request.getParameter("username")%>
-
浏览器地址栏为
http://localhost:8080/ress/2.jsp?username=admin
-
则此时的结果为
admin
-
-
会被翻译为
out.print(XXX)
-
表达式脚本的末尾不能以分号结束
-
正确写法 <%=request.getParameter("username")%>
-
错误写法 <%=request.getParameter("username");%>
-
这是因为翻译后的代码为
out.print(request.getParameter("username"););
,很显然,这种格式是不对的
-
代码脚本
通常写一些语句,例如循环、判断、输出、加减乘除,可以写一个类,但不能直接写方法,需要在类中写方法
格式:
<%
普通的Java语句;
%>
<%
System.out.println("hello world");
%>
-
和表达式脚本一样,翻译后的Java文件中,也是被翻译到
_jspService()
方法中,同时也是可以使用request
和response
-
同时,如果在代码脚本中
new
的对象,也可以在表达式脚本<%=对象%>
中使用,也就说,能够被翻译到_jspService()
方法中的代码都可以写 -
也可以将代码脚本组合在一块形成一段完整的代码
-
<% for (int i = 0; i < 10; i++) { %> <% System.out.println("输出"); } %>
-
此时控制台内容为
-
输出 输出 输出 输出 输出 输出 输出 输出 输出 输出
-
-
代码脚本也可以和表达式脚本配合在一起使用
-
<% for (int i = 0; i < 10; i++) { %> <%="测试"%> <% } %>
-
此时的网页中的内容有10个测试
-
可以和前端的代码缝合在一块,形成特殊的效果
jsp中的三种注释
- HTML注释
- 被翻译成
out.write("<!--html注释-->\r\n");
- 被翻译成
- Java注释
- 直接翻译
- jsp注释,可以注释掉一切,包含HTML、Java注释和代码
- 不被翻译
jsp中9大内置对象
其中异常对象需要手动开启,开启方式:将isErrorPage
属性设置true
四大域对象
域对象是可以像map
一样存取内容的对象,四个域对象的功能一致,不同的是对数据存取的范围,使用的优先级顺序为数据存取范围的由小到大:
pageContext
:PageContextImpl
类,仅在当前jsp
页面有效request
:HttpServletRequest
类,一次请求内有效session
:HttpSession
类,一个会话范围内有效(一次会话:打开浏览器访问服务器,直到关闭浏览器,只要浏览器不关闭,则会话一直都在)application
:ServletContext
类,整个web工程内有效,只要web工程不停止,那么数据一直都在,中文为应用、申请、申请书,读音为ˌæplɪˈkeɪʃn
可能需要Java EE 6-Java EE 6
库
<%
// 给四个域设置键值
pageContext.setAttribute("key", "pageContext");
request.setAttribute("key", "request");
session.setAttribute("key", "session");
application.setAttribute("key", "application");
%>
<%=pageContext.getAttribute("key")%>
<br>
<%=request.getAttribute("key")%>
<br>
<%=session.getAttribute("key")%>
<br>
<%=application.getAttribute("key")%>
浏览器输出的内容为
pageContext
request
session
application
数据存取范围测试:
首先有两个文件,分别为3.jsp
和4.jsp
,打开两个浏览器,分别访问这两个文件
<%--3.jsp--%>
-------------------------------------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>3.jsp</h1>
<%
// 给四个域设置键值
pageContext.setAttribute("key", "pageContext");
request.setAttribute("key", "request");
session.setAttribute("key", "session");
application.setAttribute("key", "application");
%>
<%
// 设置请求的转发,转发到4.jsp,并且将request和response作为参数传递过去
request.getRequestDispatcher("/4.jsp").forward(request, response);
%>
</body>
</html>
<%--4.jsp--%>
----------------------------------------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>4.jsp</h1>
pageContext: <%=pageContext.getAttribute("key")%>
<br>
request: <%=request.getAttribute("key")%>
<br>
session:<%=session.getAttribute("key")%>
<br>
application:<%=application.getAttribute("key")%>
</body>
</html>
-
浏览器1访问3.jsp,此时内容转发到
4.jsp
,内容为:-
从
3.jsp
跳转到4.jsp
,pageContext
只能在一个页面中有效,所以转发后为null
;在请求转发的过程中传递的参数有request
,算一次请求,所以request
中的内容得以保存 -
4.jsp pageContext: null request: request session:session application:application
-
-
浏览器1访问
4.jsp
,此时内容为:-
4.jsp pageContext: null request: null session:session application:application
-
-
浏览器2访问
4.jsp
,此时内容为:-
相当于关闭浏览器后再访问
4.jsp
,所以session
此时为null
了 -
4.jsp pageContext: null request: null session:null application:application
-
-
重启服务器(重新部署)后,浏览器访问
4.jsp
,内容为:-
此时相当于web工程重启了,所以
application
也为null
了 -
4.jsp pageContext: null request: null session:null application:null
-
JSP中的out输出和response.getWriter输出区别
在jsp中有这样一段代码
<%
response.getWriter().write("response.getWriter()输出,<br>");
out.write("out.writer()输出,<br>");
response.getWriter().write("response.getWriter()输出,<br>");
%>
和
<%
out.write("out.writer()输出,<br>");
response.getWriter().write("response.getWriter()输出,<br>");
response.getWriter().write("response.getWriter()输出,<br>");
%>
浏览器访问后,两次的结果都为
response.getWriter()输出,
response.getWriter()输出,
out.writer()输出,
可见,out
的输出顺序一直都在response
的下边
原因:
out
和response
都分别有自己的缓冲区- 当jsp代码加载完毕后,会执行
out.flush
进行刷新缓冲区,此时缓冲区的内容会被加载到response
的末尾 - 然后会执行
response
的刷新操作,把全部数据写给客户端
为避免out
的问题,一般都直接使用out.write
的方式进行输出
out.write和out.printXX
out.write()
针对字符串的输出不会出现问题,如果直接输出整型会出现问题out.printXXX()
对任何类型的值都可以输出,因为会将任意的类型转换为字符串类型
JSP静态标签
分为静态包含、动态包含、请求转发标签
jsp静态包含
-
一个页面中,或许都有顶部导航栏或者底部导航栏,如果在一个页面中都加入同样的导航栏,使用传统的方式进行插入,每次去维护成千上万的页面会变的特别麻烦
-
为了提高可维护性,把所有页面中都包含的一些内容可以使用包含引用,将其引入到页面中
-
引入方式
-
<%@ include file="路径" %>
-
有以下的目录结构
│ 5.jsp
│
└───include
header.jsp
tail.jsp
5.jsp
需要引入头部文件header.jsp
和tail.jsp
尾部文件
<%--header.jsp--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
头部信息 头部信息 头部信息 头部信息
<br>
<br>
</body>
</html>
<%--tail.jsp--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<br>
尾部信息 尾部信息 尾部信息 尾部信息
</body>
</html>
<%--5.jsp--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--引入头部文件--%>
<%@ include file="include/header.jsp"%>
主体内容<br>
主体内容<br>
主体内容<br>
主体内容<br>
主体内容<br>
主体内容<br>
<%--引入尾部文件--%>
<%@ include file="include/tail.jsp"%>
</body>
</html>
页面内容为
头部信息 头部信息 头部信息 头部信息
主体内容
主体内容
主体内容
主体内容
主体内容
主体内容
尾部信息 尾部信息 尾部信息 尾部信息
对于以上文件的包含方式,翻译后的Java源码为
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("头部信息 头部信息 头部信息 头部信息\r\n");
out.write("<br>\r\n");
out.write("<br>\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
out.write("\r\n");
out.write("主体内容<br>\r\n");
out.write("主体内容<br>\r\n");
out.write("主体内容<br>\r\n");
out.write("主体内容<br>\r\n");
out.write("主体内容<br>\r\n");
out.write("主体内容<br>\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("<br>\r\n");
out.write("尾部信息 尾部信息 尾部信息 尾部信息\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
- 也就是相当于把两个文件的所有的东西都导入到了页面中
- 将多个文件中的内容直接使用
out.write("字符串")
进行写出
jsp动态包含
语法
<jsp:include page="路径"></jsp:include>
例如
<%--
Created by IntelliJ IDEA.
User: singx
Date: 2021/10/2
Time: 14:37
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--动态包含--%>
<jsp:include page="include/header.jsp"></jsp:include>
中间内容
<br>
中间内容
<br>
中间内容
<br>
<%--动态包含--%>
<jsp:include page="include/tail.jsp"></jsp:include>
</body>
</html>
翻译后的jsp代码部分为
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write('\r');
out.write('\n');
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "include/header.jsp", out, false);
out.write("\r\n");
out.write("中间内容\r\n");
out.write("<br>\r\n");
out.write("中间内容\r\n");
out.write("<br>\r\n");
out.write("中间内容\r\n");
out.write("<br>\r\n");
out.write('\r');
out.write('\n');
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "include/tail.jsp", out, false);
out.write("\r\n");
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
-
会把相关语句翻译成Java代码
-
相当于调用
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "路径", out, false);
-
此时将
request
、response
、out
都传递给了被动态包含的页面中 -
还可以传递一些参数
-
需要写在静态包含的标签之间:
-
<jsp:include page="路径"> <jsp:param key="key" value="value"/> </jsp:include>
-
之后便可以在被包含进来的页面中,调用
request.getParameter("key")
获取键值对
-
-
在实际的使用中,静态包含要比动态包含使用的多
jsp请求转发标签
之前的请求转发的实现方式为
request.getRequestDispatcher("路径").forward(request, response);
在jsp中也有专门的请求转发的标签
<jsp:forward page="路径"></jsp:forward>
-
翻译后的Java代码为
-
if (true) { _jspx_page_context.forward("/"); return; }
自定义类无法实例化异常
有概率出现此问题
解决方法:文件-项目结构-工件-删掉当前有问题的web项目/模块,再重新新建一个:
-
点击
+
,选择web应用程序:展开型-基于模块,重新添加一下删掉的模块 -
再进行编辑配置-部署-
+
号,选择工件,选择刚刚添加的工件
傻逼CSDN耽误我时间!!!
无法使用out中的方法
文件-项目结构-模块-选择无法使用out中的方法的模块-依赖-+号-库(library)-应用程序服务器库-tomcat
对于将查询到的结果放到一个表格中的解决方案
思路:先获取请求,在请求中取出数据,在服务器中进行对比查询,将查询到的结果使用response.setAttribute("key", List)
放入到键值对中,在jsp页面,通过取出键值对中的信息进行在页面输出
监听器Listener
读音ˈlɪsənər
,中文为听众,倾听者
是Java的三大组件之一,是Java EE的规范(接口)
作用:监听某种事物的变化,通过回调函数,反馈给客户端并做一些处理
EL表达式
全称:Expression Language
,中文为表达式语言,expression
中文为表达式、表达、表示,读音为ɪkˈspreʃn
作用:代替jsp页面中的表达式脚本,在jsp页面中输出,主要是输出域对象中的数据
<%--测试存在的值--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
request.setAttribute("key", "value");
%>
普通输出:<%=request.getAttribute("key")%> <br>
EL表达式输出:${key}<br>
</body>
</html>
结果为
普通输出:value
EL表达式输出:value
当普通的方式输出不存在的值时,会显示null
,而EL
表达式什么都不输出,如果输出null
的话可能会有歧义,普通表达式也可以什么都不输出
此时可以写为
<%=request.getAttribute("不存在的值") == null ? "":request.getAttribute("不存在的值") %>
EL表达式对于4个域对象的搜索顺序
当一个jsp页面中,四个域对象都有相同的key
时
<%
pageContext.setAttribute("key", "pageContext");
request.setAttribute("key", "request");
session.setAttribute("key", "session");
application.setAttribute("key", "application");
%>
el表达式输出:${key}
搜索顺序是按照四个域对象的数据存取范围由小到大进行搜索,无论语句的位置,当多个域对象有相同的key
时,默认都是使用数据存取范围最小的key
输出普通属性中的值
例如
package com.test;
import java.util.Arrays;
import java.util.HashMap;
public class Test {
private String name;
private HashMap<String, String> map;
private int age;
private int[] arr;
@Override
public String toString() {
return "Test{" +
"name='" + name + '\'' +
", map=" + map +
", age=" + age +
", arr=" + Arrays.toString(arr) +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HashMap<String, String> getMap() {
return map;
}
public void setMap(HashMap<String, String> map) {
this.map = map;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int[] getArr() {
return arr;
}
public void setArr(int[] arr) {
this.arr = arr;
}
}
jsp页面
<%@ page import="com.test.Test" %>
<%@ page import="java.util.HashMap" %><%--
Created by IntelliJ IDEA.
User: singx
Date: 2021/10/5
Time: 9:51
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
Test test = new Test();
HashMap<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
map.put("key4", "value4");
test.setAge(99);
test.setArr(new int[]{1, 2, 4, 5});
test.setName("test");
test.setMap(map);
// 将对象设置为一个参数,如果不设置为参数,则无法正常输出
pageContext.setAttribute("test1", test);
%>
<%--作为参数进行输出--%>
el表达式输出:${test1}<br>
<%--从参数中,输出单个属性值--%>
el表达式输出单个属性:${test1.map}<br>
</body>
</html>
输出结果为
el表达式输出:Test{name='test', map={key1=value1, key2=value2, key3=value3, key4=value4}, age=99, arr=[1, 2, 4, 5]}
el表达式输出单个属性:{key1=value1, key2=value2, key3=value3, key4=value4}
对于所有的属性都是通过getXXX()
方法进行获取到的,如果没有写相关的getXXX()
,会抛出异常
对于List
类型,可以通过取下标的方式进行获取数据,格式${key.List实例[下标]}
<%
Test test = new Test();
ArrayList<Double> list = new ArrayList<>();
list.add(1111.88);
list.add(222.88);
list.add(3333.88);
list.add(4444.88);
test.setAge(99);
test.setList(list);
// 将对象设置为一个参数,如果不设置为参数,则无法正常输出
pageContext.setAttribute("test1", test);
%>
<%--作为参数进行输出--%>
el表达式输出ArrayList属性:${test1.list[1]}<br>
对于Map
类型,也可以通过${key.Map实例.Map中的键}
获取key
<%
Test test = new Test();
HashMap<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
map.put("key4", "value4");
test.setMap(map);
// 将对象设置为一个参数,如果不设置为参数,则无法正常输出
pageContext.setAttribute("test1", test);
%>
<%--作为参数进行输出--%>
el表达式输出Map中的键属性:${test1.map.key1}<br>
运算
-
算术运算
empty运算
判断一个数据是否为空,如果为空输出true
为空的情况:
- 值为
null
- 值为
空串
- 值为
Object
类型数组,长度为0
List
集合,元素个数为0
Map
集合,元素个数为0
格式:${empty key}
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.HashSet" %>
<%@ page import="java.util.HashMap" %><%--
Created by IntelliJ IDEA.
User: singx
Date: 2021/10/5
Time: 11:06
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 空值
request.setAttribute("emptyNull", null);
// 长度为0的基本类型数组
request.setAttribute("emptyArray", new int[0]);
// 长度为0的Object类型数组
request.setAttribute("emptyObjectArray", new HashMap[0]);
// 空串
request.setAttribute("emptyString", "");
// 长度为0的List集合
request.setAttribute("emptyList", new ArrayList<>());
// 长度为0的Set集合
request.setAttribute("emptySet", new HashSet<>());
// 长度为0的Map集合
request.setAttribute("emptyMap", new HashMap<>());
%>
空值:${empty emptyNull}<br>
长度为0的基本类型数组:${empty emptyArray}<br>
长度为0的Object类型数组:${empty emptyObjectArray}<br>
空串:${empty emptyString}<br>
长度为0的List:${empty emptyList}<br>
长度为0的Set:${empty emptySet}<br>
长度为0的Map:${empty emptyMap}<br>
</body>
</html>
空值:true
长度为0的基本类型数组:false
长度为0的Object类型数组:true
空串:true
长度为0的List:true
长度为0的Set:true
长度为0的Map:true
.运算和[]运算
map中也可以使用[],例如
<%
HashMap<String, String> map = new HashMap<>();
map.put("1", "value");
request.setAttribute("map", map);
%>
${map["1"]}
EL中11个内置对象
scope
,读音skəʊp
,中文为范围
是EL
进行定义的,可以直接使用
针对pageScope
、requestScope
、sessionScope
、applicationScope
的使用
<%--
Created by IntelliJ IDEA.
User: singx
Date: 2021/10/5
Time: 16:47
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
pageContext.setAttribute("key", "pageContext");
request.setAttribute("key", "request");
session.setAttribute("key", "session");
application.setAttribute("key", "application");
%>
${pageScope.key}<br>
${requestScope.key}<br>
${sessionScope.key}<br>
${applicationScope.key}
</body>
</html>
解决了四个域中具有相同的键进行查询值的问题
pageContext
的使用:
-
<%-- Created by IntelliJ IDEA. User: singx Date: 2021/10/5 Time: 16:47 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <% pageContext.setAttribute("key", "pageContext"); request.setAttribute("key", "request"); session.setAttribute("key", "session"); application.setAttribute("key", "application"); %> <%--pageContext.request.scheme<=>request.getScheme()--%> 协议:${pageContext.request.scheme}<br> IP地址:${pageContext.request.serverName}<br> 服务器端口号:${pageContext.request.serverPort}<br> 工程路径:${pageContext.request.contextPath}<br> 请求方式:${pageContext.request.method}<br> 客户端IP:${pageContext.request.remoteHost}<br> 会话的ID:${pageContext.session.id}<br> </body> </html>
-
具体的写法:
pageContext.变量名.属性
,属性相当于变量名.get属性
,即写法中不用写get
param
:获取请求的参数,该参数为Map
类型:
所有单个参数:${param}<br>
<%--获取key为key1时的value--%>
参数key1:${param.key1}<br>
请求参数为?key1=value1&key2=value2
输出结果为
所有单个参数:{key1=value1, key2=value2}
参数key1:value1
paramValues
:在请求的参数中,一个key可能有多个value,所以,可以通过该变量进行获取,输出的内容格式{key = [地址}
,地址是数组的地址,可以通过paramValues.key[下标]
取出值
多个参数:${paramValues.key2[1]}<br>
请求参数为?key1=value1&key2=value2&key2=value3
结果为多个参数:value3
header
:返回键值对,头部信息
${header}
Header:{sec-fetch-mode=navigate, sec-fetch-site=none, accept-language=zh-CN,zh;q=0.9, cookie=JSESSIONID=0C81B1BB28F1A6C56F9C0BE69A7FBDAE; Idea-35907095=2c39fe81-2ef8-43ca-9524-893dc2eec9de; _ga=GA1.1.935333769.1631151725, sec-fetch-user=?1, accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9, sec-ch-ua="Chromium";v="94", "Google Chrome";v="94", ";Not A Brand";v="99", sec-ch-ua-mobile=?0, sec-ch-ua-platform="Windows", host=localhost:8080, upgrade-insecure-requests=1, connection=keep-alive, cache-control=max-age=0, accept-encoding=gzip, deflate, br, user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36, sec-fetch-dest=document}
$(header.get("user-agent"))
<%--或者-->
header["user-agent"]
结果为Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36
headerValues
:与paramValues
用法一样
cookie
:获取cookie
,cookie:${cookie}
initalParam
:获取web.xml
中<context-param></context-param>
中的键值对
JSTL标签库
全称:JSP Standard Tag Library
即JSP标准标签库
主要用来替代代码的脚本
使用:
-
导入
jstl
的jar包,下载链接 -
需要下载
Impl:taglibs-standard-impl-1.2.5
和Spec:taglibs-standard-spec-1.2.5
-
在jsp中引入
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
功能:
-
在域中保存数据
-
<c:set scope="域" var="key" value="value"/>
-
当域不填写时,默认相当于
pageContext
-
-
单个判断标签
-
没有不成立的语句,没有else
-
if标签: <c:if test="${el表达式的条件}"> <p>成立执行这里,可以写标签</p> </c:if>
-
-
多路判断标签,类似于
if...else if...else
-
其中有
choose
、when
、otherwise
-
<c:choose> <c:when test="${el表达式条件}"> <p>成立执行这里,可以写标签</p> </c:when> <c:when test="${el表达式条件}"> <p>成立执行这里,可以写标签</p> </c:when> <c:otherwise> <p>以上都不成立执行这里,可以写标签</p> </c:otherwise> </c:choose>
-
标签中不能写HTML注释,只能写JSP注释
<%--注释--%>
,否则会抛出异常<c:when test=${el表达式}></c:when>
必须直接的写在<c:choose></c:choose>
中,如果想要写在<otherwise></otherwise>
中,则必须还要套一层<c:choose></c:choose>
-
<%--错误写法--%> <c:choose> <c:when test="${el表达式条件}"> <p>成立执行这里,可以写标签</p> </c:when> <c:when test="${el表达式条件}"> <p>成立执行这里,可以写标签</p> </c:when> <c:otherwise> <c:when test="${el表达式}"> <p>以上都不成立执行这里,可以写标签</p> </c:when> </c:otherwise> </c:choose> <%--正确写法--%> <c:choose> <c:when test="${el表达式条件}"> <p>成立执行这里,可以写标签</p> </c:when> <c:when test="${el表达式条件}"> <p>成立执行这里,可以写标签</p> </c:when> <c:otherwise> <c:choose> <c:when test="${el表达式}"> <p>以上都不成立执行这里,可以写标签</p> </c:when> </c:choose> </c:otherwise> </c:choose>
-
-
<c:forEach/>
标签-
作用:遍历输出
-
对于普通的循环:输出1-10
-
<%--begin开始位置, end为结束的位置, var为变量名同时也为正在遍历的数据--%> <c:forEach begin="1" end="10" var="i"> i = ${i} <br> </c:forEach>
-
i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 i = 8 i = 9 i = 10
-
-
遍历
Object类型的数组
,对于map
类型,也可以使用这种方式进行遍历-
<% request.setAttribute("Strings", new String[]{"abc", "ced", "fgh"}); %> <%--相当于增强for循环,items为需要遍历的数组,i为一个取出其中值的变量,相当于forEach中:左侧的变量--%> <c:forEach var="i" items="${Strings}"> i = ${i}<br> </c:forEach>
-
i = abc i = ced i = fgh
-
也可以输出某个范围内的数据
-
begin
和end
也可以省略,省略begin
代表从开始位置0
开始,省略end
代表结束位置一直到末尾 -
<% request.setAttribute("Strings", new String[]{"abc", "ced", "fgh", "fgh", "fgh", "fgh", "fgh", "fgh", "fgh", "fgh"}); %> <%--相当于增强for循环,items为需要遍历的数组,i为一个取出其中值的变量,相当于forEach中:左侧的变量,下端相当于从下标4的位置一致输出到下标7的位置的元素,也可以设置步长属性--%> <c:forEach var="i" items="${Strings}" begin="4" end="7"> i = ${i}<br> </c:forEach>
-
还有步长
step
属性-
<c:forEach var="i" begin="1" end="100" step="8"> i = ${i} <br> </c:forEach>
-
i = 1 i = 9 i = 17 i = 25 i = 33 i = 41 i = 49 i = 57 i = 65 i = 73 i = 81 i = 89 i = 97
-
-
-
varStatus
属性-
<c:forEach var="i" begin="1" end="100" step="8" varStatus="status"> i = ${i} <br> status = ${status} <br> </c:forEach>
-
输出的内容
-
i = 1 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 9 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 17 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 25 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 33 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 41 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 49 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 57 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 65 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 73 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 81 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 89 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c i = 97 status = javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@56dbf70c
可见,
varStatus
是个对象,其中里边有以下的方法
-
-
文件的上传和下载
上传
需要符合:
- 要有一个
form
标签,同时,method=post
即必须是post请求 form
标签中的encType
属性的属性值必须为multipart/form-data
,multipart
中文为多部分,表示提交的数据以多段的形式拼接,然后以二进制流的形式发送给服务器form
标签中,需要使用<input type=file/>
标签用来添加上传的文件- 编写服务器端的代码,处理上传的数据,使用
servlet
进行接收
因为get
请求在地址栏中限制长度,所以要使用post
请求
在后端必须以流的形式接收,否则无法获取到里边的内容
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取一个流
ServletInputStream inputStream = req.getInputStream();
//new一个buffer数组
byte[] buffer = new byte[1024000];
//将读取的内容,放入到buffer数组中
int read = inputStream.read(buffer);
//输出里边的内容
System.out.println(new String(buffer, 0, read));
}
输出的内容格式f为
------WebKitFormBoundaryXKfrR0Kh6y0G3j21
Content-Disposition: form-data;键值
参数
------WebKitFormBoundaryXKfrR0Kh6y0G3j21
Content-Disposition: form-data;;键值
参数
------WebKitFormBoundaryXKfrR0Kh6y0G3j21--
此时需要做的是处理解析这些数据,可以使用第三方的jar
包进行处理
需要两个:commons-fileupload.jar依赖于commons-io.jar,所以两个都需要引入
导入jar包后,可以使用ServletFileUpload
类所提供的方法对上传的数据进行解析处理
方法 | 含义 |
---|---|
FileLoad.isMultipartContent(request) | 查看当前上传进来的数据是否是多段的格式,返回boolean 类型 |
parseRequest(request) | 解析上传的数据,返回List<FileItem> 类型 |
FileItem
类:表示每一个表单项
步骤:
-
首先先使用
FileLoad.isMultipartContent(request)
进行判断上传进来的数据是否是多段的格式,如果是,执行以下步骤 -
实例化一个
FileItemFactory
文件工厂类对象,FileItemFactory
是一个接口,可以使用它的一个实现类DiskFileItemFactory
进行创建 -
使用一个
FileItemFactory
文件工厂类对象,实例化一个ServletFileUpload
对象,根据API文档描述,需要提供一个工程类来指定存储方式 -
通过
ServletFileUpload
对象的parseRequest(request)
方法,获取一个List<FileItem>
fileItems,可以通过遍历fileItems
,对每个元素进行处理,因此,可以使用每个元素的isFormField()
方法判断当前的元素是普通的表单元素还是文件-
如果是普通的表单元素,可以输出其
key
和value
- 通过
getFieldName()
获取key
,即在原<input>
标签中的name
属性值 - 通过
getString("字符集编码")
获取具体的value
- 通过
-
如果是文件,则可以将这个文件写入到本地
-
通过
getFieldName()
获取key
,即在原<input>
标签中的name
属性值 -
通过
getName()
获取文件名 -
通过
write(new File("路径" + 文件名))
把文件写入到本地自定义的路径中 -
// 上传后的文件名如果是中文可能会乱码,可以提前设置一下请求的字符集编码 req.setCharacterEncoding("UTF-8");
-
-
全部代码
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 上传后的文件名如果是中文可能会乱码,可以提前设置一下请求的字符集编码
req.setCharacterEncoding("UTF-8");
// 判断当前上传的数据格式是否是多段的格式,返回布尔类型
if (FileUpload.isMultipartContent(req)) {
// 创建FileItemFactory实现类
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 创建解析上传数据的工具类
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
// 解析上传数据,得到每个表单项FileItem
List<FileItem> fileItems;
try {
fileItems = servletFileUpload.parseRequest(req);
// 通过循环遍历,判断每个fileItems中的元素是一个普通的表单项还是一个上传的文件
for (FileItem f : fileItems){
// true代表是普通表单项
if(f.isFormField()){
// 对于普通表单项,可以尝试着输出一下键和值,为了解决输出值时可能出现的乱码问题,可以先将字符集编码设置为UTF-8
System.out.println("普通表单项,其中key = " + f.getFieldName() + ", value = " + f.getString("UTF-8"));
System.out.println("f = " + f);
}else{ //false代表上传的文件
System.out.println("上传的文件,其中key = " + f.getFieldName() + ",文件名为" + f.getName());
// 写入文件
f.write(new File("D:\\Java IO\\upload\\" + f.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
resp.getWriter().println("不是多段的");
return;
}
}
下载
步骤:
- 获取下载的文件名
- 读取文件的内容
- 把下载的文件回传到客户端
- 回传前,通过响应头告诉客户端返回的数据类型
- 通过响应头,告诉客户端收到的文件用于下载使用
请求下载的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Download</title>
</head>
<body>
<h1>下载</h1>
<form action="/ress/download" method="post" target="_blank">
文件名:<input type="text" name="filename">
<br>
<button type="submit">提交</button>
</form>
</body>
</html>
处理下载的代码
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=UTF-8");
// 1. 获取下载的文件名
String filename = req.getParameter("filename");
System.out.println(filename);
if (filename == null){
resp.getWriter().println("未找到文件");
return;
}
// 2. 通过响应头告诉客户端返回的数据类型
ServletContext servletContext = getServletContext();
//获取MIME 类型,MIME是一种标准,用来表示文档、文件或字节流的性质和格式
String mimeType = servletContext.getMimeType("file/" + filename);
System.out.println("文件类型为:" + mimeType);
//设置响应头
resp.setContentType(mimeType);
// 3. 通过响应头,告诉客户端收到的文件用于下载使用,同时也指定了文件名
resp.setHeader("Content-Disposition", "attachment;fileName=" + filename);
// 4.获取文件流和输出流
//通过文件的路径获取一个输入流
InputStream resourceAsStream = servletContext.getResourceAsStream("file/" + filename);
//输出流,获取响应的输出流
ServletOutputStream outputStream = resp.getOutputStream();
// 5. 把下载的文件回传到客户端,使用第三方jar包进行操作
IOUtils.copy(resourceAsStream, outputStream);
}
对于第18行设置响应头的内容,可参考,Content-Disposition
当指定的文件名为中文时,可能会发生文件名乱码的情况,对于chrome浏览器,需要对url进行编码resp.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(filename, "UTF-8"));
对于base标签使用的一个问题
如果base
标签中,直接填写的内容为http://localhost:port/XXXX
那么,如果使用其他的IP地址进行访问时, 当前页面的地址也是localhost
,这会导致使用相对路径引入的css等资源失效
可以尝试着动态获取
<%
// 解决指定地址后,css等资源丢失的问题
// 获取到协议://服务器IP地址:端口号/工程路径+自定义的其他路径
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/user/";
request.setAttribute("basePath", basePath);
%>
<base href="<%=basePath%>">
对于相同的模块的功能用同一个servlet进行实现
可以设置一个隐藏的表单域
<input type="hidden" name="name" value="value">
在多个页面中,赋予隐藏的表单域不同的value
属性进而进行判断要实现的功能
如果这么操作,会有大量的if...else if...else
,这些都可以通过反射给优化掉
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取当前的Class
Class clazz = this.getClass();
// 获取相应的方法,之后关于用户操作的方法名必须遵循xxxUser的格式
try {
Method method = clazz.getDeclaredMethod(req.getParameter("flag"), HttpServletRequest.class, HttpServletResponse.class);
// 用当前的对象尝试调用方法
try {
method.invoke(this, req, resp);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
BeanUtils工具类
可以导入此工具的第三方类,在一个项目中,可能有许多的getXXX()
方法和new XXX()
对象,可以使用这个工具类,注入到JavaBean中,BeanUtils工具类依赖于Commons-logging和Commons-collections(版本3.2.2)
User u = new User();
try {
BeanUtils.populate(u, req.getParameterMap());
System.out.println(u);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
populate
读音ˈpɑːpjuleɪt
,中文:填充、居住
User{id=null, username='admin', password='jVbMCDZY4gxT8D', email='null'}
可以手动写一个工具类,用请求的参数中的值赋给一个对象中对应的属性
package utils;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
public class WebUtils {
/**
* 使用BeanUtils填充值
* @param req 请求,用来将其中的参数转换为map
* @param o 需要填充的对象
*/
public static void copyToBean(HttpServletRequest req, Object o){
try {
BeanUtils.populate(o, req.getParameterMap());
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
需要保证被填充的对象中,有相应的setXXX()
方法与请求的key
相同
或者,此时调用时,可以填写request.getPatameterMap()
public static void copyToBean(Map m, Object o){
try {
BeanUtils.populate(o, m);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
还可以更简洁
public static Object copyToBean(Map m, Object o){
try {
BeanUtils.populate(o, m);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return o;
}
//调用
User user = (User) WebUtils.copyToBean(req.getParameterMap(), new User());
最后的方案-使用泛型
public static <T> T copyToBean(Map m, T o){
try {
BeanUtils.populate(o, m);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return o;
}
//调用
User user = WebUtils.copyToBean(req.getParameterMap(), new User());
MVC
全称:Model
模型、View
视图、Controller
控制器
Model
模型:将业务逻辑相关的数据封装到具体的JavaBean
类中,其中不掺杂任何与数据处理相关的代码
View
视图:只负责数据和界面的显示,不接受任何与数据显示无关的代码
Controller
控制器:负责接收请求,调用业务层代码请求,派发页面,作为一个调度者,通常为servlet
,负责转发到某个页面或者重定向到某个页面,读音kənˈtroʊlər
是一种思想,将代码拆分成组件,单独开发,组合使用,主要的目的还是为了解耦合
分页
在实际的开发中,一个页面不可能直接显示全部的数据,需要使用分页,在一页中只显示部分数据
分页的模型:
-
当前页码
-
总页码
- 总的记录数=总的数据记录数/每页的数量的上取整
-
总的数据的记录条数
-
select count(1) from 表名;
-
-
每页显示的数量
-
当前页的数据
-
select 字段名 * from 表名 limit 开始位置, 数量;
-
开始位置:
当前的页码 - 1 * 每页的数量
-
Cookie
服务器通知客户端保存数据(键值对)的一种方式,客户端有了Cookie之后,每次请求都要发给服务器,每个Cookie的大小不能超过4kb
创建
- 实例化一个
Cookie
对象 - 在
response
响应中添加Cookie
对象
protected void createCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建cookie对象
Cookie cookie = new Cookie("key", "value");
// 将cookie添加到响应中
resp.addCookie(cookie);
resp.getWriter().println("cookie创建成功");
}
可以通过开发者工具查询到自动获取的cookie
在响应头中,可以看到
创建过程:
cookie一次可以创建多个,客户端可以同时收到多个
获取cookie
可以直接调用resquest.getCookies()
方法,返回Cookie[]
遍历这个数组,对这个数组中的元素使用getName()
获取key
,getValue()
获取value
protected void getCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
for(Cookie c : cookies){
resp.getWriter().println("[" + "key = " + c.getName() + ", value = " + c.getValue() + "]");
}
}
如果想要获取指定key
的cookie
,没有别的办法,只能挨个遍历,并且使用"key".equals(cookie.getName())
进行判断
在实际的开发中,通常写一个utils
类,单独写一个针对指定的key
获取cookie
package utils;
import javax.servlet.http.Cookie;
/**
* 获取cookie
*/
public class CookieUtils {
public static Cookie findCookie(String key, Cookie[] c){
if (key == null || c == null || c.length == 0){
return null;
}
for(Cookie c1 : c){
if(key.equals(c1.getName())){
return c1;
}
}
return null;
}
}
cookie的值的修改
方式1:
- 实例化一个cookie
- 在构造器中,
new cookie(需要修改的key, 新的value)
- 将新的cookie添加到
response
方式2:
- 调用
getCookie()
获取一个Cookie[]
- 遍历
Cookie[]
中的每个元素,可以调用setValue(String value)
进行修改值 - 将修改后的cookie添加到
response
protected void updateCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (req.getParameter("key") == null || "".equals(req.getParameter("key"))) {
resp.getWriter().write("key不存在<br>");
}else{
//通过key获取key,需要key存在
Cookie cookie = CookieUtils.findCookie(req.getParameter("key"), req.getCookies());
if (cookie == null){
resp.getWriter().write("key不存在<br>");
return ;
}
cookie.setValue(req.getParameter("value"));
//再次添加到response中
resp.addCookie(cookie);
resp.getWriter().write("修改成功<br>");
}
}
cookie
中不允许存储中文
cookie生命周期
控制cookie什么时候销毁/删除
可以通过每个cookie
的setMaxAge(int value)
方法设置
设置后,还需要将设置后的内容再次添加到response
中
当value
为正值,表示value
秒后过期
当value
为负值,表示不会持久存储,即浏览器退出时清除,默认值为-1
当value
为0,表示立即清除
protected void setCookieLife(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
if (req.getParameter("key") == null || "".equals(req.getParameter("key")) || req.getParameter("time") == null || "".equals(req.getParameter("time"))) {
resp.getWriter().write("key或者存活时间不存在<br>");
} else {
Cookie cookie = CookieUtils.findCookie(req.getParameter("key"), cookies);
if (cookie == null) {
resp.getWriter().write("key不存在<br>");
return;
}
//设置存活时间,前提是时间存在并且key存在
cookie.setMaxAge(Integer.parseInt(req.getParameter("time")));
resp.addCookie(cookie);
resp.getWriter().write("设置成功," + cookie.getName() + "的存活时间为" + cookie.getMaxAge() + "<br>");
}
}
设置cookie的时间后,响应标头中的内容也发生了改变
当cookie时间设置为0时,响应标头中的内容为Set-Cookie:key=value666; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT
有效路径path
可以过滤那些内容发给服务器,那些内容不发,是通过请求的地址确定的
调用每个cookie
元素的setPath(String path)
设置路径
例如
Cookie cookie = new Cookie("key", "value666");
Cookie cookie2 = new Cookie("key2", "value2");
Cookie cookie3 = new Cookie("key3", "value3");
cookie3.setValue("value9999");
cookie.setPath("/ress/cookie/a");
cookie2.setPath("/ress/cookie/b");
设置3个cookie,为第一个和第二个指定路径,此时,浏览器使用默认的地址访问
所有的cookie都可以得到,但只能使用cookie3
- 只能使用的cookie:
- 得到的响应头为:
- 此时包含不能使用的cookie1和cookie2
保存用户的cookie
功能:用户提交表单后,将用户提交的内容使用cookie
保存起来,下次再打开这个页面时,表单中的内容依然存在
<form action="/ress/cookie">
<input type="hidden" name="flag" value="saveCookie">
用户名:<input type="text" name="username" value="${cookie.username.value}">
<button type="submit">提交</button>
</form>
其中,第3行的EL
表达式中的cookie
对象是EL
表达式的11个内置对象中的一个,是一个Map<String, Cookie>
protected void saveCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
Cookie cookie = new Cookie("username", username);
cookie.setMaxAge(300);
resp.addCookie(cookie);
resp.getWriter().write("提交成功,key = " + cookie.getName() + ", value = " + cookie.getValue() + "<br>");
}
Session会话
Session
是一个接口,具体是HttpSession
是用来维护客户端和服务器之间关联的技术,每个客户但都有自己的会话,通常用来保存用户登录后的信息
创建和获取Session
获取
req.getSession();
第一次调用这个方法是创建Session
会话,之后的调用都是获取前边创建好的Session
会话对象
判断一个Session
是刚创建出来的还是直接获取的:调用session的isNew()
方法
例如:req.getSession().isNew()
方法
每个会话都已自己唯一的id值
设置/获取值
为session
设置值:
- 获取一个
session
:request.getSession()
- 调用获取到的
session
的setAttribute(String key, Object value)
为session
获取值:
- 获取一个
session
:request.getSession()
- 调用获取到的
session
的getAttribute(String key)
protected void sessionSetAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().setAttribute(req.getParameter("key"), req.getParameter("value"));
resp.getWriter().write("已将数据插入到session中,数据key = " + req.getParameter("key") + "value = " + req.getParameter("value") + "<br>");
}
protected void sessionGetAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object value = req.getSession().getAttribute(req.getParameter("key"));
resp.getWriter().write("key = " + req.getParameter("key") + ", value = " + value + "<br>");
}
Session生命周期控制
设置生命周期:
调用Session
的sessions.setMaxInactiveInterval(值);
方法
Inactive
中文为不活跃的,读音为inˈaktiv
Interval
中文为间隔,读音为ˈɪntərvl
时长以秒为单位,默认的时长为1800秒,也就是30分钟 ,该项再Tomcat服务器中默认为30分钟
获取生命周期:
调用Session
的sessions.getMaxInactiveInterval(值);
方法
超时的概念
假设设置的超时为3秒,如果获取了session后,在3秒内如果再次获取使用session
,那么此时的超时又会变为3秒,也就是说,两次请求的最大间隔时长,一旦超过时长,session就会销毁
值为正数,代表有超时时长,时长为设置的时长
值为负数,代表永不超时
设置为0无法让其立即失效,可以调用req.getSession().invalidate();
让其马上无效,可以让其作为注销的功能
invalidate中文为作废,读音为inˈvaləˌdāt
原理
- 在没有Cookie时向服务器发送请求,服务器会调用
getSession()
方法创建会话对象 - 服务器内容中会有多个
session
,此时在响应头中会返回cookie
,cookie
中只有key
永远是JESSIONID
,value
为session
的id
值 - 每次都是通过响应把新创建的
session
值返回到客户端 - 每次请求都会把
session
的id
以cookie
形式发给服务器,服务器在获取session
时,每次都是通过request.getSession()
获取session
,获取时,通过id
在内存中查找符合的session
并返回 ,因为内存中可能有许多session
,所以只能通过id
进行匹配查找 - 如果删除掉所有的
cookie
后,此时再发送请求时,会找不到之前的session
,所以只能新创建一个新的session
,也就是再添加一个key = JESSIONID
和value = id
,然后继续重复2-3-4步
和session有关的cookie,它的MaxAge
此时为-1
,所以说为-1
的MaxAge
在Chrome中,MaxAge
会显示为会话(session)
表单重复提交的问题
-
提交完表单后,服务器使用请求进行页面跳转,此时如果用户按下刷新,就会再次发送一个请求,表单又会重复的提交到服务器,此时使用重定向跳转可以解决问题
-
resp.sendRedirect(req.getContextPath() + "工程路径下的其他页面");
-
-
用户正常提交表单,但由于网络延迟等原因,用户多点了提交按钮,表单又会重复的提交到服务器
-
用户正常提交表单,但按了浏览器的后退,在后退页面又重新提交,表单又会重复的提交到服务器
验证码
为解决表单重复提交的问题,特此引入了验证码
- 用户在提交表单时,给表单随机生成一个字符串作为验证码
- 验证码要保存到
Session
域中 - 将验证码生成为图片显示在表单中
可以使用Google的:Kaptcha
使用:
-
配置
web.xml
,在jar包中,有servlet类,引入即可 -
配置好之后,直接访问配置好的servlet就可以直接访问到图片,并且验证码也会放到
session
域中 -
在这个文件中,有一些常量,
KAPTCHA_SESSION_KEY
代表存放在session
中的key,可以通过getAttrbute(KAPTCHA_SESSION_KEY)
获取value
-
获取后,可以接着进行删除,防止验证码使用第二次.
-
使用
package server; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import static com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY; public class TestServlet extends BaseServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=UTF-8"); // 获取写入到session中的验证码的value String token = (String) req.getSession().getAttribute(KAPTCHA_SESSION_KEY); // 删除掉写入到session中的验证码的value,防止验证码使用第二次 req.getSession().removeAttribute(KAPTCHA_SESSION_KEY); // 获取表单中提交的验证码 String code = req.getParameter("code"); // 和session域中的验证码进行对比 if (token != null && token.equals(code)){ resp.getWriter().write("保存到数据库中"); }else{ resp.getWriter().write("验证码不正确"); } } }
-
<%-- Created by IntelliJ IDEA. User: singx Date: 2021/10/16 Time: 16:40 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="/ress/code"> 用户名:<input type="text" name="username"> <br> 验证码: <input type="text" name="code"> <img width="100px" height="25px" src="/ress/img.jpg" alt="验证码"> <br> <button type="submit">提交</button> </form> </body> </html>
刷新验证码
点击图片刷新
为图片绑定一个单击事件即可
code.click(function () {
this.src = "/bookf/code.jpg";
})
<img width="100px" height="25px" src="/book/code.jpg" alt="验证码" id="code">
Filter过滤器
filter中文为过滤/筛选 读音为ˈfiltər
,是Java Web的三大组件之一,是一个接口,也是Java EE的一个规范,作用是拦截请求,过滤响应
应用场景:
- 权限检查
- 事务操作
- 等等...
权限管理
例如某个目录下,仅允许登录后的用户访问,此时可以用到Filter
过滤器
大概的一个流程
让一个类实现Filter
接口,Filter
接口可能有多个,需要实现的是import javax.servlet.Filter
主要是doFilter()
方法,这个方法做权限检查
//AdminFilter.java
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class AdminFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
// 做权限检查的方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 需要类型转换,servletRequest中没有获取session的方法
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpSession session = req.getSession();
// 获取value
String username = (String) session.getAttribute("username");
// 如果为空,代表没有这个参数,此时需要转发到另一个页面
if(username == null){
servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest, servletResponse);
}else{
// 放行
filterChain.doFilter(servletRequest, servletResponse);
}
}
@Override
public void destroy() {
}
}
以上第26行的代码一定要写,如果不写就不会放行!
还需要配置一个xml
,xml
的格式和之前的servlet
类似,功能不同,功能是做拦截的
<filter>
<!-- 给filter起一个名字-->
<filter-name>AdminFilter</filter-name>
<!-- 指定一个类-->
<filter-class>AdminFilter</filter-class>
</filter>
<!-- 映射地址-->
<filter-mapping>
<filter-name>AdminFilter</filter-name>
<!-- /代表工程路径,即IP:port/工程路径/,而*代表这个路径下的全部内容-->
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
生命周期
Filter
声明周期所包含的方法:
- 构造器方法
init()
初始化方法doFilter()
过滤方法destroy()
销毁方法
前两步在Web
工程启动时执行,也就是Filter
已经创建了
第三步在每次拦截到请求就会执行
第四步在停止Web
工程的时候执行
FilterConfig类
见名知义,是Filter
的配置文件类
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("filterConfig.getFilterName() = " + filterConfig.getFilterName());
System.out.println("filterConfig.getInitParameterNames() = " + filterConfig.getInitParameterNames());
System.out.println("filterConfig.getInitParameter(\"key\") = " + filterConfig.getInitParameter("key"));
}
配置文件如下
<filter>
<!-- 给filter起一个名字-->
<filter-name>AdminFilter</filter-name>
<!-- 指定一个类-->
<filter-class>AdminFilter</filter-class>
<init-param>
<param-name>key</param-name>
<param-value>value</param-value>
</init-param>
</filter>
<!-- 映射地址-->
<filter-mapping>
<filter-name>AdminFilter</filter-name>
<!-- /代表工程路径,即IP:port/工程路径/,而*代表这个路径下的全部内容-->
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
FilterChain
chain中文为链条/通路,读音为tʃeɪn
该类出现在doFilter()
方法的形式参数中
多个Filter
执行的流程
doFilter()
方法的作用
- 如果有下一个
Filter
过滤器,那就向下执行 - 如果没有,执行后边的资源
如果多个Filter
都指向了同一个需要过滤的地址,那么执行的顺序类似域上图
例子:
//Filter1.java
1public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter1前置代码");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter1后置代码");
}
//Filter2.java
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter2前置代码");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter2后置代码");
}
web.xml
配置文件
<filter>
<filter-name>Filter1</filter-name>
<filter-class>Filter1</filter-class>
</filter>
<filter-mapping>
<filter-name>Filter1</filter-name>
<url-pattern>/test/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>Filter2</filter-name>
<filter-class>Filter2</filter-class>
</filter>
<filter-mapping>
<filter-name>Filter2</filter-name>
<url-pattern>/test/*</url-pattern>
</filter-mapping>
访问需要过滤的地址后,输出的内容为:
Filter1前置代码
Filter2前置代码
Filter2后置代码
Filter1后置代码
如果将Filter2.java
的filterChain.doFilter()
方法调用去掉(第4行),那么输出内容为:
Filter1前置代码
Filter1后置代码
此时,Filter2.java
中的doFilter()
方法不会执行
-
在有多个
Filer
过滤器时,它们的执行顺序由所在的Web.xml
文件中的顺序决定的 -
多个
Filter
默认都在同一个线程中 -
多个
Filer
过滤器的请求request
都是一样的
拦截路径的匹配
有3种匹配方法:
- 精准匹配
<url-pattern>/路径/文件名</url-pattern>
- 例如
<url-pattern>/test/abc.html</url-pattern>
- 代表路径匹配为
http://IP:port/工程路径/test/abc.html
- 代表路径匹配为
- 目录匹配
<url-pattern>/路径/*</url-pattern>
- 代表路径匹配为
http://IP:port/工程路径/test/
,即这个路径下的所有文件
- 代表路径匹配为
- 后缀名匹配
<url-pattern>*.html</url-pattern>
- 代表匹配所有以
.html
结尾的文件 - 不要加
/
!!!
- 代表匹配所有以
配置错误页面
为特定的响应状态码进行配置跳转的页面,比如说抛出异常的500状态码
当抛出异常后,用户可能看不懂异常的内容,所以可以给专门的错误码配置一个页面
需要在web.xml
中设置
<error-page>
<error-code>错误码</error-code>
<location>所跳转的文件</location>
</error-page>
例如tomcat默认的404是这样的
可以自定义一个404页面,进行替换
web.xml
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
404.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>
404
</h1>
</body>
</html>
JSON
JSON全称为JavaScript Object Notation
,是一种轻量级的数据交换格式
Notation
中文为符号,读音为noʊˈteɪʃn
轻量级是与xml
进行比较的,也是客户端和服务器之间的数据交换格式
以键值对组成,每个键由引号引起来,键和值由:
进行分割,多组键和值之间用,
进行分割
JSON本身是一个对象
在Javascript中使用JSON
<script type="text/javascript">
var json = {
"integer" : 12,
"string" : "hello",
"boolean" : true,
"array" : [1, false, "world"],
"json" : {
"key" : "jsons"
}
}
window.onload = function () {
alert(json);
}
</script>
获取值:json变量名.键
json中的值可以是整型/数组/字符串/浮点型/json
json的存在方式有两种,一种是字符串的形式,另一种是对象的形式
这两种方式可以互相转换:
- json转字符串:
JSON.stringify(对象)
- 字符串转json:
JSON.parse(字符串)
在操作JSON中的数据时,需要使用JSON对象
在客户端和服务器传递数据时,使用JSON字符串
在Java中使用JSON
需要第三方jar包,可以使用Google的GSON.jar
-
JavaBean和JSON的转换
-
public static void main(String[] args) { Cat cat = new Cat("cat", 99, true); System.out.println("cat = " + cat); Gson gson = new Gson(); //JavaBean转json System.out.println("gson.toJson(cat) = " + gson.toJson(cat)); //自定义一个json字符串 String json = """ {"name":"dog","age":88,"sex":false,"map":{"key666":"value"}} """; //json转JavaBean,第一个参数为json字符串,第二个参数为需要转换的类型 Cat cat2 = gson.fromJson(json, Cat.class); System.out.println(cat2); }
-
结果: cat = Cat{name='cat', age=99, sex=true, map={key2=value2, key3=value3, key4=value4, key=value}} gson.toJson(cat) = {"name":"cat","age":99,"sex":true,"map":{"key2":"value2","key3":"value3","key4":"value4","key":"value"}} Cat{name='dog', age=88, sex=false, map={key666=value}}
-
-
List和JSON转换
-
对于list转json,使用上例的方法即可
-
对于json转list,如果还使用上例的方法,那么只能转换为
Object
类型的-
在GSON中,官方有提供的一个reflect包,这个包中有
TypeToken
类,可以新建一个类继承这个类,泛型中填写ArrayList<类型>
-
package com.test; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.util.ArrayList; public class Test2 { public static void main(String[] args) { ArrayList<Cat> list = new ArrayList<>(); list.add(new Cat("猫",88, false)); list.add(new Cat("gou",288, true)); list.add(new Cat("addf",828, false)); list.add(new Cat("dff",828, true)); list.add(new Cat("dfdf",838, false)); System.out.println("list = " + list); Gson gson = new Gson(); //list转json,结果是一个[每个元素] System.out.println("gson.toJson(list) = " + gson.toJson(list)); String json = gson.toJson(list); //将json转换成List,此种方法只能转换成Object类型的 ArrayList arrayList = gson.fromJson(json, ArrayList.class); System.out.println(arrayList); // 会抛出异常!! // Object ccc = (Cat)arrayList.get(0); // System.out.println(ccc); //这种方式创建出来的JavaBean最正常 ArrayList<Cat> arrayList2 = gson.fromJson(json, new ArrayListType().getType()); System.out.println(arrayList2); System.out.println("arrayList2.get(0).map = " + arrayList2.get(0).age); } } class ArrayListType extends TypeToken<ArrayList<Cat>>{ }
-
-
-
Map和JSON转换,和List类似
-
package com.test; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.util.ArrayList; import java.util.HashMap; public class Test2 { public static void main(String[] args) { HashMap<String, String> map = new HashMap<>(); map.put("key", "value"); map.put("key2", "value2"); map.put("key3", "value3"); map.put("key4", "value4"); Gson json = new Gson(); // map转json String jsonString = json.toJson(map); System.out.println("jsonString = " + jsonString); // json转map,此种方法和List类似,只能转换为Object类型的 System.out.println(json.fromJson(jsonString, HashMap.class)); // 和List一样的的解决方法,新建一个类,继承TypeToken HashMap<String, String> hashMap = json.fromJson(jsonString, new MapType().getType()); System.out.println(hashMap.get("key2").length()); } } class MapType extends TypeToken<HashMap<String, String>>{ }
也可以使用匿名实现类,上边第28行使用一个新类进行继承的,未免过于浪费,可以使用匿名实现类,直接将23行代码改写为
HashMap<String, String> hashMap = json.fromJson(jsonString, new TypeToken<HashMap<String, String>>(){}.getType());
-
AJAX
Asynchronous JavaScript And XML(异步JavaScript 和 XML)
Asynchronous读音eɪˈsɪŋkrənəs
中文为异步,创建交互式网页应用开发的网页开发技术,是浏览器异步发起请求,局部刷新的页面技术
局部刷新:浏览器页面没有刷新,而内容改变了,浏览器地址也没有变化,也不会舍弃页面中原有的内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript">
function ajax() {
// 创建一个XMLHttpRequest
var xmlhttprequest = new XMLHttpRequest();
//设置请求的参数,主要有 请求的方法,请求的地址,是否是异步的 默认为true
xmlhttprequest.open("GET", "http://localhost:8080/ress/ajax?flag=ajax", true);
// 将请求发给服务器
xmlhttprequest.send()
}
</script>
</head>
<body>
<button type="submit" id="ajax" onclick="ajax()">ajax</button>
</body>
</html>
public class AjaxServlet extends BaseServlet{
protected void ajax(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Ajax请求过来了");
}
}
点击按钮后,输出Ajax请求过来了
Ajax请求效果:
<html>
<head>
<title>Title</title>
<script type="text/javascript">
function ajax() {
// 创建一个XMLHttpRequest
var xmlhttprequest = new XMLHttpRequest();
//设置请求的参数,主要有 请求的方法,请求的地址,是否是异步的 默认为true
xmlhttprequest.open("GET", "http://localhost:8080/ress/ajax?flag=ajax", true);
xmlhttprequest.onreadystatechange = function () {
// 判断状态码,只有符合时才进行相关操作
// readyState
// 0: 请求未初始化
// 1: 服务器连接已建立
// 2: 请求已接收
// 3: 正在处理请求
// 4: 请求已完成且响应已就绪
if (xmlhttprequest.readyState == 4 && xmlhttprequest.status == 200){
alert(xmlhttprequest.responseText);
}
}
// 将请求发给服务器
xmlhttprequest.send()
}
</script>
</head>
<body>
<button type="button" id="ajax" onclick="ajax()">ajax</button>
</body>
</html>
把内容写入到响应中,此时输出的内容是响应的内容
protected void ajax(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Ajax请求过来了");
HashMap map = new HashMap<>();
map.put("ke1", "value1");
map.put("ke2", "value2");
map.put("ke3", "value3");
map.put("ke4", "value4");
// 将map转为json
Gson json = new Gson();
String jsonString = json.toJson(map);
// 将json写入到响应中
resp.getWriter().write(jsonString);
}
JQuery中的AJAX
使用JQuery实现
{
url: "请求地址",
data: "请求参数",
type: "请求方式",
success: function (date) {
请求成功执行的函数
alert("服务器返回的数据:" + date)
},
dataType: "返回的数据形式,通常为text xml json对象 "
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script type="text/javascript">
$(function () {
$("#button").click(function () {
$.ajax({
url: "http://localhost:8080/ress/ajax",
data: "flag=ajax",
type: "GET",
success: function (date) {
alert("服务器返回的数据:" + date)
},
dataType: "text"
}
)
})
})
</script>
</head>
<body>
<button type="button" id="button">按钮</button>
</body>
</html>
JQuery中的get/post方法
用法与前边的大致一样,差别在于第10行和13行,无需再填写type
属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script type="text/javascript">
$(function () {
$("#button").click(function () {
$.get或者post({
url: "http://localhost:8080/ress/ajax",
data: "flag=ajax",
success: function (date) {
alert("服务器返回的数据:" + date)
},
dataType: "text"
}
)
})
})
</script>
</head>
<body>
<button type="button" id="button">按钮</button>
</body>
</html>
getJSON
获取响应的内容为json的时候用的
<script type="text/javascript">
$(function () {
$("#button").click(function () {
$.getJSON({
url: "http://localhost:8080/ress/ajax",
data: "flag=ajax",
success: function (date) {
alert("key1" + date.key1)
}
}
)
})
})
</script>
将表单内容以AJAX提交
表单可以序列化的
$(form标签的选择器).serialize()
方法可以把表达序列化,序列化效果就是将表单中的内容提取出来
i8n国际化
指同一个网站支持不同的语言
实际用处不多
Q.E.D.