1. IDEA 创建 JavaWeb 项目
1.1 选择JavaEE 项目
1.2 Project tempate中选择 Web Application
1.3 一路创建
1.4 在Edit Configurations 设置tomcat, 下面为brew 安装的tomcat地址
/usr/local/Cellar/[email protected]/9.0.53/libexec
1.5 设置On update action: update resources, On frame deactivation: update resources
1.6 设置第二栏Deployment: Application text: /web_demo
1.7 启动tomcat 即可自动在浏览器上预览
2. 打包JavaWeb 项目
2.1 在本地wepapp文件夹内, 使用jar -cvf myweb.war .打包
jar -cvf myweb.war .
2.2 上传到tomcat服务器中的webapps中, 重启tomcat即可看到发布结果
scp myweb.war webserver:tomcat9.0/webapps/
./shutdown.sh
./startup.sh
3. 更改Tomcat 端口
conf中server.xml 8080更改为80端口
4. 在server.xml中添加其它目录做为tomcat项目
**在Host中间添加
<Context path="/myweb" docBase="/Users/lizicai/IdeaProjects/web_demo/src/main/webapp" />
访问ip:8080/myweb 即可
5. 设置域名访问tomcat
5.1 在server.xml中添加配置
# 在<Engine name="Catalina" defaultHost="localhost"> 下添加
<Host name="tom.lizicai.com" appbase="webapps"
unpackWARs="true" autoDeploy="true" >
<Context path="/s" docBase="web_demo" />
</Host>
name 域名
appbase 项目存放路径
unpackWARs 解压
autoDeploy 自动发布
5.2 设置dns, 或者更改本机hosts
# 添加dns
192.168.0.100 tom.lizicai.com
# 更改本机hosts
127.0.0.1 tom.lizicai.com
6. HTTP 协议的响应
- 常见状态码
200 一切OK
302/307 请求重定向,两次请求,地址栏发生变化
304 请求资源未发生变化,使用缓存
404 请求资源未找到
500 服务器错误
- HTTP 响应头
Location 请求重定向的地址,常与302,307配合使用
Server 服务器相关信息
Content-Type 响应正文的MIME类型
Content-Length 响应正文的长度
Content-Dispositio 告知客户端浏览器,以下载的方式打开响应正文
Refresh 定时刷新
Lash-Modified 服务器资源的最后修改时间
Set-Cookie 会放管理相关,非常重要
Expires:-1 服务器资源到客户端浏览器后的缓存时间
Catch-Control:no-catch 不要缓存
Servlet
- Servlet的介绍
- Servlet是运行在Java 服务器端的程序,用于接收和响应来自客户端基于HTTP协议的请求。
- 如果想实现Servlet的功能,可以通过实现javax.servlet.Servlet接口或者继承它的实现类。
- 核心方法:service(),任何客户端的请求都会经过该方法。
对应项目案例在03_JavaWeb
- Servlet 的执行流程
- 浏览器地址
- web.xml的地址/url
- /url匹配web.xml的servlet name
- servlet name 匹配servlet class
- 执行servlet calss中的service()方法
实现 GenericServlet
package com.lizicai.test;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class Demo1GenericServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Servlet 执行了");
}
}
实现HttpServlet
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 Demo2HttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("DoPost 执行了");
}
}
servlet 详细执行过程
Servlet GenericServlet HttpServlet 之间的关系
Servlet 实现方式
Servlet 实现方式
- 第一种
- 实现Servlet接口,实现所有的抽象方法。该方式支持最大程度的自定义。
- 第二种
- 继承 GenericServlet抽象类,必须重写 service方法,其他方法可选择重写。该方式让我们开发Servlet变得简单。
- 但是这种方式和HTTP协议无关。
- 第三种
- 继承 Httpser/let抽象类
- 需要重写doGet和doPost方法。该方式表示请求和响应都需要和HTTP协议相关。
Servlet 生命周期
- 出生: 请求第一次到达Servlet时,对象就创建出来, 并且初始化. 只出生一次, 放到内存中.
- 活着: 服务器提供服务的整个过程中, 该对象一直存在, 每次都执行service() 方法
- 死亡: 当前服务停止时, 或者服务器宕机时, 对象死亡.
结论: Servlet 对象只会创建一次, 销毁一次, 所以Servlet对象只有一个实例, 如果对象实例在应用中是唯一存在, 就是单例模式
Servlet 线程案例问题
-
由于Servlet 采用的单例模式, 也就是整个应用中只有一个实例, 这个实例是否是线程案例的
-
模拟2个用户同时访问HttpServlet时, doGet中打印用户名, 其中一个sleep3000
-
结论: 2个用户返回同一个用户名, 线程不安全的.
-
解决:
- 定义类成员要谨慎. 如果是共用的, 并且只会在初始化时赋值, 其他时间都是获取的话,那没问题的.
- 如果不是共用的, 或者每次使用都有可能对其赋值, 那就要考虑线程安全问题了.
- 变量可以定义到doGet doPost方法内
- 在doGet doPost 使用同步功能, 如 synchronized (this)
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 Demo4HttpServletFixed extends HttpServlet {
private String username;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = null;
username = req.getParameter("username");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
PrintWriter pw = resp.getWriter();
pw.println("welcome "+username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
synchronized (this){
username = req.getParameter("username");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
PrintWriter pw = resp.getWriter();
pw.println("welcome "+username);
}
}
}
Serlet 映射方式
- 第一种
- 具体名称的方式, 访问的资源路径必须和映射配置安全相同
- 第二种
- /开头 + 通配符的方式. 只要符合目录结构即可, 不用是考虑结尾
- 第三种
- 通配符 + 固定格式结尾的方式. 只要符合固定结尾格式即可, 不用考虑前面的路径
**优先级的问题, 越时具体优化级越高, 第一种 > 第二种 > 第三种
<!-- Servlet 映射方式1 -->
<servlet>
<servlet-name>demo5HttpServlet</servlet-name>
<servlet-class>com.lizicai.test.Demo5HttpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo5HttpServlet</servlet-name>
<url-pattern>/demo5HttpServlet</url-pattern>
</servlet-mapping>
<!-- Servlet 映射方式2 -->
<servlet>
<servlet-name>demo5HttpServlet</servlet-name>
<servlet-class>com.lizicai.test.Demo5HttpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo5HttpServlet</servlet-name>
<url-pattern>/demo/*</url-pattern>
</servlet-mapping>
<!-- Servlet 映射方式3 -->
<servlet>
<servlet-name>demo5HttpServlet</servlet-name>
<servlet-class>com.lizicai.test.Demo5HttpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo5HttpServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
- Servlet 多映射使用的场景
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 Demo6HttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int sum = 100;
String url = req.getRequestURI();
String vUrl = url.substring(url.lastIndexOf("/"));
if( "/vip".equals(vUrl)){
sum = 95;
} else if( "/vvip".equals(vUrl)){
sum = 90;
} else {
sum = 100;
}
System.out.println(url+"..."+vUrl+"您的商品价格是"+sum+"元");
}
}
Servlet 创建时机
- 第一次访问时创建
- 优势: 减少对服务器内存的浪费. 提高了服务器启动的效率
- 弊端: 如果有一些要在应用加载时就做的初始化操作, 无法完成
- 服务器加载时创建
- 优势: 提前创建好对象, 提高了首次执行的效率. 可以完成一些应用加载时要做的初始化操作
- 弊端: 对服务器内存占用较多, 影响了服务器启动的效率.
**修改Servlet 创建时机, 在
<servlet>
<servlet-name>demo6HttpServlet</servlet-name>
<servlet-class>com.lizicai.test.Demo6HttpServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>demo6HttpServlet</servlet-name>
<url-pattern>/demo/*</url-pattern>
</servlet-mapping>
默认Servlet
- 默认Servlet 是由服务器提供的一个Servlet. 它配置在Tomcat的conf目录中的web.xml中.
- 它的映射路径是
/ , 我们在发送请求时, 首先会在我们项目中的web中查看映射配置, 找到则执行. - 但是当找不到对应的Servlet路径时, 就去找默认的Servlet, 由默认Servlet处理, 一切都Servlet.
ServletConfig
- ServletConfig 是Servlet 的配置参数对象, 在Servlet的规范中, 允许为每一个Servlet都提供一个初始化的配置. 所以每个Servlet都有一个自己的ServletConfig
- 作用: 在Servlet的初始化时, 把一些配置信息传递给Servlet
- 生命周期: 和Servlet 相同.
<servlet>
<servlet-name>demo7ServletConfig</servlet-name>
<servlet-class>com.lizicai.test.Demo7ServletConfig</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>demo7ServletConfig</servlet-name>
<url-pattern>/demo7</url-pattern>
</servlet-mapping>
ServletConfig 常用方法
返回值 方法名 说明
String getInitParameter(Stringname) 根据参数名称获取参数值
Enumeration<String> getInitParameterNames() 获取所有参数名称的枚举
String getServletName() 获取Servlet名称
ServletContext getServletContext() 获取ServletContext对象
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
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.util.Enumeration;
public class Demo7ServletConfig extends HttpServlet {
private ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String enCodingValue = config.getInitParameter("encoding");
System.out.println(enCodingValue);
Enumeration<String> initParameterNames = this.config.getInitParameterNames();
while ( initParameterNames.hasMoreElements()){
String key = initParameterNames.nextElement();
String value = config.getInitParameter(key);
System.out.println(key+"...."+value);
}
String sereletName = config.getServletName();
System.out.println(sereletName);
ServletContext servletContext = config.getServletContext();
System.out.println(servletContext);
}
}
ServletContext 介绍
- ServletContext 是应用上下文对象(应用域对象). 每一个应用中只有一个SeverContext对象
- 作用: 可以配置和获得应用的全局初始化参数, 可以实现Servlet之间的数据共享.
- 生命周期: 应用一加载则创建, 应用被停止则销毁
域对象
- 域对象指的是对象有作用域. 也就是有作用范围. 域对象可以实现数据的共享. 不同的作用范围的域对象, 共享数据的能力也不一样
- 在Servlet规范中, 一共有4个域对象. ServletContext就是其中的一个, 它也是Web应用中最大的作用域, 也
- 叫application域. 它可以实现整个应用之间的数据共享.
<servlet>
<servlet-name>demo8ServletContext</servlet-name>
<servlet-class>com.lizicai.test.Demo8ServletContext</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo8ServletContext</servlet-name>
<url-pattern>/demo8ServletContext</url-pattern>
</servlet-mapping>
<context-param>
<param-name>globalEncoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<context-param>
<param-name>globalDesc</param-name>
<param-value>ServletContext cc</param-value>
</context-param>
Servlet 常用方法
返回值 方法名 说明
String getInitParameter(String.name) 根据名称获取全局配置的参数
String getContextPath() 获取虚拟路径,这是run配置的delployment的context路径
String getRealPath(String.path) 根据虚拟目录获取应用部署的磁盘绝对路径
void setAttribute(String.name,Object.object) 向应用域对象中存储数据
Object getAttribute(String.name) 通过名称获取应用域对象中的数据
void removeAttribute(String.name) 通过名称移除应用域对象中的数据
import javax.servlet.ServletContext;
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 Demo8ServletContext extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String value = servletContext.getInitParameter("globalEncoding");
System.out.println(value);
String contextPath = servletContext.getContextPath();
System.out.println(contextPath);
String realPath = servletContext.getRealPath("/");
System.out.println(realPath);
String realPath2 = servletContext.getRealPath("/b.txt");
System.out.println(realPath2);
String realPath3 = servletContext.getRealPath("/WEB-INF/c.txt");
System.out.println(realPath3);
}
}
多个Servlet 之间从ServletContext 获取数据
先访问demo8ServletContext2再访问demo8ServletContext3
<!-- 多Servlet间共享数据, 通过ServletContext, Demo8ServletContext2和 Demo8ServletContext3之间-->
<servlet>
<servlet-name>demo8ServletContext2</servlet-name>
<servlet-class>com.lizicai.test.Demo8ServletContext2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo8ServletContext2</servlet-name>
<url-pattern>/demo8ServletContext2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>demo8ServletContext3</servlet-name>
<servlet-class>com.lizicai.test.Demo8ServletContext3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo8ServletContext3</servlet-name>
<url-pattern>/demo8ServletContext3</url-pattern>
</servlet-mapping>
import javax.servlet.ServletContext;
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 Demo8ServletContext2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
servletContext.setAttribute("name", new String("Zhangsan"));
System.out.println("访问Demo8ServletContext2...写入Attribute");
}
}
import javax.servlet.ServletContext;
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 Demo8ServletContext3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String value = (String) servletContext.getAttribute("name");
System.out.println("从ServletContext 取出之前2存入name"+value);
}
}
注解开发Servlet
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/demo9Servlet")
public class Demo9Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("处理请求...");
}
}
手动创建容器
- 创建Demo10ManualServle 继承 HttpServlet
- MyRegister implements ServletContainerInitializer
- 在src/main/java文件夹下创建包META-INF.services, 创建文件javax.servlet.ServletContainerInitializer, 添加一行com.lizicai.test.MyRegister
- 在MyRegister中执行
- Demo10ManualServle demo10ManualServle = new Demo10ManualServle();
- ServletRegistration.Dynamic registration = servletContext.addServlet (“demo10ManualServle”, demo10ManualServle);
- registration.addMapping("/demo10ManualServle");
- 浏览器访问 /demo10ManualServle
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 Demo10ManualServle extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Demo10ManualServle处理请求...");
}
}
public class MyRegister implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
// 创建servlet
Demo10ManualServle demo10ManualServle = new Demo10ManualServle();
// ServletContext 对象添加Servlet, 并得到Servlet 的动态配置对象
ServletRegistration.Dynamic registration = servletContext.addServlet
("demo10ManualServle", demo10ManualServle);
registration.addMapping("/demo10ManualServle");
}
}
com.lizicai.test.MyRegister
案例, 提交学生信息->保存文件中->同时返回一个保存成功信息给浏览器
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
@WebServlet("/demo11StudentServlet")
public class Demo11StudentServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = (req.getParameter("name"));
String age = req.getParameter("age") ;
String chenji = req.getParameter("chenji") ;
File file = new File("/Users/lizicai/workplace/test/b.txt");
BufferedWriter bw = new BufferedWriter(new FileWriter(file,true));
String sStr = name+","+age+","+chenji;
System.out.println(sStr);
bw.write(sStr);
bw.close();
PrintWriter pw2 = resp.getWriter();
pw2.println("save success");
pw2.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>学生信息保存页</title>
</head>
<body>
<form action="/stu/demo11StudentServlet" method="get" autocomplete="on">
<label for="name">学生姓名:</label>
<input type="text" name="name" id="name">
<br>
<label for="age">学生年龄:</label>
<input type="text" name="age" id="age">
<br>
<label for="chenji">学生成绩:</label>
<input type="text" name="chenji" id="chenji">
<br>
<button type="submit">保存信息</button>
</form>
</body>
</html>