概述
Javaweb的核心就是围绕servlet
Servlet就是一个接口, 定义了java类 被浏览器访问到(tomcat识别)的接口
将来就是自己写一个类 ,实现servlet接口 ,重写方法
执行过程
当服务器接收到客户端浏览器的请求后,会解析请求的url路径,获取访问的servlet的资源路径
查找web.xml文件是否有对应的<url-pattern>标签体内容与资源路径一致
如果有,则在找到<servlet-class>对应的全类名
tomcat会将字节码文件加载到内容 创建对象(反射) 调用 service方法
快速入门
- 创建javaee项目
- 定义一个类,这个类必须实现servlet接口
- 实现接口中的抽象方法
- 配置servlet
public class ServletDemo1 implements Servlet {
//初始化方法 初始化当前servlet
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init");
}
// 获取当前servlet的配置信息
@Override
public ServletConfig getServletConfig() {
System.out.println("getServletConfig");
return null;
}
// 访问服务方法 每次访问servlet都会执行一次这个方法
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service");
}
//获取当前servlet信息
@Override
public String getServletInfo() {
System.out.println("getServletInfo");
return null;
}
// 销毁方法
@Override
public void destroy() {
System.out.println("destroy");
}
}
web.xml
<!-- 配置servlet-->
<servlet>
<servlet-name>servletDemo1</servlet-name>
<!-- 当前servlet的全类名-->
<servlet-class>servlet.ServletDemo1</servlet-class>
</servlet>
<!-- servlet 映射-->
<servlet-mapping>
<!-- 通过这个名字 找到 servlet标签-->
<servlet-name>servletDemo1</servlet-name>
<!-- 当前 servlet实现类的访问路径-->
<url-pattern>/servlet1</url-pattern>
</servlet-mapping>
生命周期
init 被创建
service 被访问
每次访问servlet都会调用一次service方法
destroy 被销毁
servlet被销毁时,服务器正常关闭时执行destroy方法,但是服务器如果不是正常关闭不走这个方法
getServletConfig
返回servlet配置对象
getServletInfo
获取servlet的一些信息: 版本,作者等
init
默认情况下servlet第一次被访问时被创建
可以指定配置servlet的创建时机
init只执行一次,说明servlet在内存中只存在一个对象(servlet时单例的)
多用用户同时访问时,可能存在线程安全问题
解决方案:尽量不要在servlet中定义成员变量 ,即使定义的成员变量,也不要对其修改值
<!-- 配置 init初始话时间 正数
负数 默认 第一次访问servlet的时候 执行init方法
正数 启动服务的之后 执行init方法
-->
<load-on-startup>1</load-on-startup>
Servlet的体系结构
- Servlet -- 接口
- GenericServlet -- 抽象类
- HttpServlet -- 抽象类
- GenericServlet :将Servlet接口中的方法做了默认的实现,只将service()方法作为抽象,将来定义Servlet时 直接继承 GenericServlet类即可
- HttpServlet:对http协议的一种封装,简化操作
- 定义类继承HttpServlet类
- 重写doGet doPost方法
@WebServlet("/servletDemo4")
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("我是用户发送的get请求");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("我是用户发送的post请求");
}
}
Http
•HyperText Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间--数据传输的规则
•数据传输的规则指的是请求数据和响应数据需要按照指定的格式进行传输。
TCP协议和HTTP协议
基于TCP协议: 面向连接,安全
TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全。
基于请求-响应模型的:一次请求对应一次响应
请求和响应是一一对应关系
HTTP协议是无状态协议:对于事物处理没有记忆能力。每次请求-响应都是独立的
无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。这种特性有优点也有缺点
缺点:多次请求间不能共享数据
优点:速度快
请求之间无法共享数据会引发的问题,如:
淘宝,加入购物车和去购物车结算是两次请求
HTTP协议的无状态特性,加入购物车请求响应结束后,并未记录加入购物车是何商品
Java提出了使用会话技术(Cookie、Session)来解决这个问题
Request 请求
请求行: HTTP请求中的第一行数据,请求行包含三块内容,分别是 GET[请求方式] /[请求地址] HTTP/1.1[HTTP协议及版本]
请求方式有七种,最常用的是GET和POST
常见的HTTP请求头:
获取请求体 公共方法
// 获取参数
String username = request.getParameter("username");
String password = request.getParameter("password");
// String like = request.getParameter("like");
//如果是多选 那么就选择这个方法
String[] likes = request.getParameterValues("like");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(likes));
System.out.println("---------");
// 获取所有请求参数的名称
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()){
String s = parameterNames.nextElement();
System.out.println(s);
}
System.out.println("--------------");
// 返回所有参数的集合
Map<String, String[]> parameterMap = request.getParameterMap();
Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
for (Map.Entry<String, String[]> entry:entries
) {
System.out.print(entry.getKey()+":");
System.out.print(Arrays.toString(entry.getValue()));
System.out.println();
}
}
Request 乱码问题
POST请求解决方案
分析出现中文乱码的原因:
POST的请求参数是通过request的getReader()来获取流中的数据
TOMCAT在获取流的时候采用的编码是ISO-8859-1
ISO-8859-1编码是不支持中文的,所以会出现乱码
解决方案:
页面设置的编码格式为UTF-8
把TOMCAT在获取流数据之前的编码设置为UTF-8
通过request.setCharacterEncoding("UTF-8")设置编码,UTF-8也可以写成小写
//在获取请求参数之前 修改编码格式
request.setCharacterEncoding("utf-8");
出现连接过大异常
修改ConnectionUtils
static DataSource dataSource;
static {
try {
Properties properties = new Properties();
// FileInputStream resourceAsStream = new FileInputStream("druid.properties");
InputStream resourceAsStream = ConnectionUtil.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(resourceAsStream);
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 获取连接对象
* @return 连接对象
*/
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (Exception e) {
throw new RuntimeException(e);
}
return connection;
}
请求转发
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 在Servlet1中接收到 浏览器发送的请求
System.out.println("我是servlet1 我接受到了请求");
System.out.println("但是我解决不了 需要转发给servlet2");
//请求 http://localhost:8080/javaweb2/ServletDemo1?name=tom
// 传入资源2的路径
// 不需要添加虚拟路径的 , 因为是在服务器本生资源中进行跳转
// 然后将当前的请求和响应对象传递过去
request.getRequestDispatcher("/ServletDemo2").forward(request,response);
}
概念
请求转发(forward):一种在服务器内部的资源跳转方式
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A处理完请求后将请求发给资源B
(3)资源B处理完后将结果响应给浏览器
(4)请求从资源A到资源B的过程就叫==请求转发==
请求转发资源间共享数据
主要解决的问题是把请求从/req1转发到/req2的时候,如何传递数据给/req2。
需要使用request对象提供的三个方法:
存储数据到request域 void setAttribute(String name,Object o);
根据key获取值 Object getAttribute(String name);
根据key删除该键值对 void removeAttribute(String name);
请求转发的特点
•浏览器地址栏路径不发生变化
•只能转发到当前服务器的内部资源
•不能从一个服务器通过转发访问另一台服务器
•一次请求,可以在转发资源间使用request共享数据
•虽然后台从/req1转发到/req2,但是这个==只有一次请求==
响应 response
响应消息格式
响应行
组成 HTTP/1.1 200 ok 协议版本 /响应码 码表述
响应头
格式:头名称:值
常见的响应头:
content-type: 服务器告诉客户端 当前的响应的数据格式以及编码格式
content-disposition:服务器告诉客户端 ,以什么形式打开当前的响应体
1.in-line:默认值,在当前页面打开
2.attachment : 以附件的形式打开
响应体 :真实的传输的数据
响应码
功能:告诉浏览器或者客户端一个当前的请求的响应状态
响应码 都是3位数
1xx 服务器接收客户端消息 但是没有接收完成,等待一段时间之后发送1xx ,询问客户端 还有没有数据 (不常见)
2xx 成功 代表:200
3xx 重定向 代表:302 (重定向) 304(访问缓存)
4xx 客户端错误。代表404(请求路径没有对应的资源) 405(没有对应的请求方法 )
5xx 服务器端错误 代表500(服务器内部出现异常)
出现4xx 就去查看请求路径或者配置servlet的路径 ,转发重定向路径有没有问题
如果出现500 就看控制台提示的报错信息
操作response
//操作响应
// 响应行
//方法: setStatus(int i) 设置响应状态码
// response.setStatus(500);
// 设置响应头
// 方法: setHeader()设置响应头
// response.setHeader("content-disposition","attachment");
// 响应体
/*
响应体
方法:字符输出流 getWriter 字节输出流 getOutputStream
使用输出流将数据输出到浏览器客户端上
*/
response.getWriter().write("你好世界");
重定向
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的 路径
(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B
(4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫重定向
System.out.println("A接收到了请求");
String name = req.getParameter("name");
System.out.println(name);
System.out.println("重定向到B");
// 在request 请求域对象中 设置一个内容
req.setAttribute("abc","hello");
//http://localhost:8080/javaweb2/ServletDemoA?name=tom
//设置状态码为302
// resp.setStatus(302);
//设置响应头中的location 为 新的请求地址
// 请求地址是需要添加虚拟路径
// resp.setHeader("location","/javaweb2/ServletDemoB");
// 重定向的方式2
resp.sendRedirect("/javaweb2/ServletDemoB");
重定向的特点
浏览器地址栏路径发送变化当进行重定向访问的时候,由于是由浏览器发送的两次请求,所以地址会发生变化
可以重定向到任何位置的资源(服务内容、外部均可) 因为第一次响应结果中包含了浏览器下次要跳转的路径,所以这个路径是可以任意位置资源。
两次请求,不能在多个资源使用request共享数据 因为浏览器发送了两次请求,是两个不同的request对象,就无法通过request对象进行共享数据
重定向和转发的区别
浏览器使用:需要加虚拟目录(项目访问路径)
服务端使用:不需要加虚拟目录 对于转发来说,因为是在服务端进行的,所以不需要加虚拟目录
对于重定向来说,路径最终是由浏览器来发送请求,就需要添加虚拟目录。
设置响应的编码格式
*/
/*
setCharacterEncoding 要保证和浏览器的编码格式一致
*/
// response.setCharacterEncoding("gbk");
//通过设置请求头来解决这个问题 数据格式;编码格式
response.setHeader("content-type","text/html;charset=utf-8");
response.getWriter().write("你好世界");
ServletContext
ServletContext代表整个web应用
可以和程序的容器(服务器)(servlet)来进行通讯功能 :
获取MIME类型 (互联网中文件的类型) text/html
域对象 : 共享数据
获取文件的真实路径(服务器路径)
// 获取 servletContext的对象
//方式1
ServletContext servletContext = req.getServletContext();
System.out.println(servletContext);
//方式2
/*
servletContext 在多个Servlet中获取到的是一个对象
*/
// ServletContext servletContext2 = getServletContext();
// context的功能
// 功能1 获取文件的额mime类型
//格式:大类型/小类型 例如 text/html image/jpg
String fileName = "a.java";
String mimeType = servletContext.getMimeType(fileName);
System.out.println(mimeType);
// 功能2 域对象
servletContext.setAttribute("name","张三");
// 转发到servletContext2中
resp.sendRedirect("/javaweb3/ServletContext2");
//功能3 获取服务器的真实路径
//druid.properties
// /project/Project/out/artifacts/javaweb3_war_exploded/WEB-INF/classes/druid.properties
//E:\sofwin\2312二阶段\project\Project\out\artifacts\javaweb3_war_exploded\
//获取当前项目下 web文件夹下的路径
String realPath = servletContext.getRealPath("index.jsp");
System.out.println(realPath);
//WEB-INF目录下的资源访问
String realPath2= servletContext.getRealPath("/WEB-INF/");
// WEB-INF下 classes 的路径
String realPath3= servletContext.getRealPath("/WEB-INF/classes/");
文件上传
1.将form表单中的enctype属性修改为multipart/form-data
2.servlet需要添加@MultipartConfig注解
3.获取Part对象 将post请求的数据封装成part对象
4.通过part. getSubmittedFileName获取文件名字
5.通过part. write输出到指定目录
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>$Title$</title>
</head>
<body>
<form action="/javaweb3/UpdateServlet" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit">
</form>
</body>
</html>
Java代码
/**
* 1.将form表单中的enctype属性修改为multipart/form-data
* 2.servlet需要添加@MultipartConfig注解
* 3.获取Part对象 将post请求的数据封装成part对象
* 4.通过part.getSubmittedFileName获取文件名字
* 5.通过part. write输出到指定目录
*/
//3.获取Part对象 将post请求的数据封装成part对象
Part part = request.getPart("file");
// 4.通过part.getSubmittedFileName获取文件名字
String fileName = part.getSubmittedFileName();
//5.通过part. write输出到指定目录
//获取真实路径
String realPath = getServletContext().getRealPath(fileName);
part.write(realPath);
文件下载
1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
2. 定义Servlet
1. 获取文件名称
2. 使用字节输入流加载文件进内存
3. 指定response的响应头: content-disposition:attachment;filename=xxx
4. 将数据写出到response输出流
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/javaweb3/DownLoadServlet?filename=dog.jpg">点击下载</a>
</body>
</html>
// 1. 获取文件名称
String filename = req.getParameter("filename");
// 2. 使用字节输入流加载文件进内存
//获取 servletContext对象
ServletContext servletContext = getServletContext();
String realPath = servletContext.getRealPath(filename);
FileInputStream fileInputStream = new FileInputStream(realPath);
// 3. 指定response的响应头: content-disposition:attachment;filename=xxx
resp.setHeader("content-disposition","attachment;filename="+filename);
ServletOutputStream outputStream = resp.getOutputStream();
// 4. 将数据写出到response输出流
int len=0;
byte [] bytes = new byte[1024];
while ((len=fileInputStream.read(bytes))!=-1){
outputStream.write(bytes,0,len);
}
outputStream.close();
fileInputStream.close();