Tomcat & Servlet
Web相关概念回顾
软件架构
- C/S:客户端/服务器端
- B/S:浏览器/服务器端
资源分类
-
静态资源:所有用户访问后,得到的结果都是一样的,静态资源可以被浏览器直接解析,如:html、css、js
-
动态资源:每个用户访问相同资源后,得到的结果可能不一样。动态资源被访问后,需要先转换为静态资源,再返回给浏览器。
如:servlet/jsp、php、asp…
网络通信三要素
-
IP:计算机在网络上的唯一标识。
-
端口:应用程序在计算机中的唯一标识。0~65536
-
传输协议
tcp:可靠传输协议
udp:不可靠
web服务器软件
服务器:安装了服务器软件的计算机
服务器软件:接收用户的请求,处理请求,做出响应
web服务器软件:接收用用户的请求,处理请求,做出响应
- 在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目
- web容器
常见的web服务器软件
- weblogic:oracle公司,大型JavaEE服务器,支持JavaEE规范,收费。
- webSphere:IBM公司,大型JavaEE服务器,支持JavaEE规范,收费。
- JBOSS:JBOSS公司,大型JavaEE服务器,支持JavaEE规范,收费。
- Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范,免费。
JavaEE:Java语言在企业级开发中使用的技术规范和综合,一共规定了13项大的规范。
Tomcat
Tomcat配置
-
下载:https://tomcat.apache.org/download-80.cgi
-
安装:解压压缩包即可,注意:安装目录建议不要有中文
-
卸载:删除目录即可
-
启动:配置jdk环境变量,配置tomcat环境变量。然后cmd命令行直接startup启动tomcat服务器
可能遇到的问题:
黑窗口一闪而过:
- 原因:没有正确配置JAVA_HOME环境变量
- 解决方案:重新配置JAVA_HOME
启动报错:
-
暴力:找到占用的端口号,并且找到对应的进程,杀死该进程
netstat -ano
-
温柔:修改tomcat的端口号
在配置文件conf/server.xml中修改
<Connect prot=“xxxx”
-
关闭
- 正常关闭:bin/shutdown.bat或者ctrl+c
- 强制关闭:点击启动窗口的X
-
目录结构
- bin:可执行文件
- conf:配置文件
- lib:依赖jar包
- logs:日志文件
- temp:临时文件
- webapps:存放web项目
- work:存放运行时数据
部署项目的方式
-
直接将项目放到webapps目录下即可
/hello:项目的访问路径–>虚拟目录
简化部署:将项目打成一个war包,再将war包放置到webapps目录下
- war包会自动解压缩,删除项目时,只需要删除项目的war包即可,项目会自动删除。
-
配置conf/server.xml文件
在<Host>标签体中配置
<Context docBase=“E:\hello” path="/hehe" />
- docBase:项目存放的路径
- path:虚拟目录
问题:不安全
-
**推荐,**在conf/Catalina/localhost创建任意名称的xml文件。在文件中编写
<Context docBase=“E:\hello” />
虚拟目录:xml文件的名称
静态项目和动态项目
目录结构
Java动态项目的目录结构:
- 项目的根目录
- WEB-INF目录
- web.xml:web项目的核心文件
- classes目录:放置字节码文件的目录
- lib目录:放依赖jar包
- WEB-INF目录
将Tomcat集成到IDEA中,并且创建JavaEE的项目,部署项目。
Servlet入门
概念
概念:运行在服务器端的小程序
- Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
- 以后我们自定义一个类,实现Servlet接口,复写方法
快速入门
-
创建JavaEE的项目
-
定义一个类,实现Servlet接口
public class ServletDemo1 implements Servlet
-
实现接口中的抽象方法
-
配置Servlet
<!-- 配置Servlet -->
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>cn.myz.web.servlet.Demo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
执行原理
- 当服务器接收到客户端浏览器的请求后,会解析这个请求的url路径,获取访问的Servlet的资源路径
- 查找web.xml文件,是否有对应的<url-pattern>标签体内容,与请求资源路径一致。
- 如果有,则根据servlet-name找到对应的<servlet-class>的全类名。
- tomcat会将该类的字节码文件加载进内存,并创建其对象
- 调用其方法
Servlet生命周期
创建:执行init方法,只执行一次
-
Servlet什么时候被创建?
默认情况下,第一次被访问时,Servlet被创建。但也可以去配置指定Servlet的创建时机。在servlet标签下配置
- 第一次被访问时,创建:<load-on-startup>值为负数
- 在服务器启动时,创建:<load-on-startup>值为0或正整数
-
Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
多个用户同时访问时,可能存在线程安全问题。
解决方法:尽量不要在Servlet里定义成员变量。即使定义了成员变量也不要对其修改值。
提供服务:执行service方法,执行多次
- 每次访问Servlet时,Service方法都会被调用一次
销毁:执行destroy方法,只执行一次
- Servlet被销毁时执行。服务器关闭时,Servlet被销毁。
- 注意:只有服务器正常关闭时,才会执行destroy方法。
Servlet3.0
优点
- 支持注解配置。可以不需要web.xml
步骤
-
创建JavaEE项目,选择Servlet的版本3.0以上即可,可以不创建web.xml
-
定义一个类,实现Servlet接口
-
复写方法
-
在类上使用注解@WebServlet注解配置
@WebServlet(“资源路径”)
java.lang.String[] value() default {};//代表urlPatterns()属性的配置
java.lang.String[] urlPatterns() default {};//相当于<url-pattern>
int loadOnStartup() default -1;//相当于<load-on-startup>
javax.servlet.annotation.WebInitParam[] initParams() default {};
boolean asyncSupported() default false;
java.lang.String smallIcon() default "";
java.lang.String largeIcon() default "";
java.lang.String description() default "";
java.lang.String displayName() default "";
IDEA与tomcat的相关配置
IDEA会为每一个tomcat部署的项目单独建立一个配置文件。
- 配置文件路径可以通过查看控制台logCATALINA_BASE获得:Using CATALINA_BASE: “C:\Users\hp.IntelliJIdea2019.2\system\tomcat\Tomcat_8_5_70_20210827_servlet”
工作空间项目和tomcat部署的web项目
- tomcat真正访问的是“tomcat部署的web项目”,“tomcat部署的web项目”对应着“工作空间项目”的web目录下的所有资源
- WEB-INF目录下的资源不能被浏览器直接访问。
- 断点调试
Servlet详解
Servlet体系结构
Servlet–接口
|
GenericServlet – 抽象类
|
HttpServlet --抽象类
GenericServlet
- 将Servlet接口中其它方法做了默认空实现,只将service()方法作为抽象
- 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
*HttpServlet
对http协议的一种封装,简化操作。使用步骤:
- 定义类继承HttpServlet
- 复写doGet/doPost方法
推荐使用继承HttpServlet复写doget/dopost方法
Servlet相关配置
urlpartten:Servlet访问路径
- 一个Servlet可以定义多个访问路径:@WebServlet({“d1”,“dd1”,“ddd1”})
- 路径定义规则:
- /xxx
- /xxx/xxx:多层路径,目录结构
- *.后缀名
HTTP
概念
概念:Hyper Text Transfer Protocol 超文本传输协议
传输协议:定义了客户端和服务器端通信时发送数据的格式。
特点:
- 基于TCP/IP的高级协议
- HTTP默认端口号:80
- 基于请求/响应模型:一次请求对应一次响应
- Http是一种无状态的协议:每次请求之间相互独立,不能交互数据
历史版本:
- 1.0:每一次请求响应都会建立新的连接
- 1.1:复用连接
请求消息数据格式
请求行
-
请求方式 请求url 请求协议/版本
-
GET /login.html HTTP/1.1
-
请求方式
http协议有7种请求方式,常用的有2种
GET:
- 请求参数在请求行中,在url后。
- 请求的url长度是有限制的。
- 不安全
POST:
- 请求参数在请求体中。
- 请求的url长度没有限制
- 安全
请求头
-
请求头名称:请求头值
-
常见的请求头:
User-Agent:浏览器告诉服务器,访问服务器使用的浏览器版本
- 可以在服务器端获得不同浏览器的浏览器品牌和版本,返回给它们不同的代码,以解决不同的浏览器的兼容问题。
Referer:http://localhost/login.html
-
告诉服务器,当前请求从哪里来
-
作用
1.防盗链
2.做统计工作
请求空行
- 空行,就是用于分隔POST请求的请求头和请求体的。
请求体(正文)
- 封装POST请求消息的请求参数的。
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cache-Control: max-age=0
Connection: keep-alive
Host: localhost
If-Modified-Since: Sat, 28 Aug 2021 03:00:59 GMT
If-None-Match: W/“264-1630119659545”
sec-ch-ua: “Chromium”;v=“92”, " Not A;Brand";v=“99”, “Microsoft Edge”;v=“92”
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.78
Request
- tomcat服务器会根据请求url中的资源路径,创建对应的ServletDemo对象
- tomcat服务器,会创建request和response对象,request对象中封装请求消息数据。
- tomcat将request和response两个对象传递给service方法,并调用service方法。
- 程序员可以通过request对象获取请求消息数据,通过response对象设置响应消息数据
- 服务器在给浏览器做出响应之前,会从response对象中拿程序员设置的响应消息数据。
Request对象和response对象的原理
- request和response对象是由服务器创建,我们来使用它们。
- request对象是来获取请求信息,response对象是来设置响应信息
继承体系结构
request对象继承体系结构
ServletRequest --接口
|继承
HttpServletRequest --接口
|实现
org.apache.catalina.connector.RequestFacade类
Request功能
获取请求消息数据
获取请求行数据
-
GET /day14/demo1?name=zhangsan HTTP/1.1
-
方法
-
获取请求方式:GET
String getMethod()
-
(*)获取虚拟目录:/day14
String getContextPath()
-
获取Servlet路径:/demo1
String getServletPath()
-
获取get方式请求参数:name=zhangsan
String getQueryString()
-
(*)获取请求URI:
String getRequestURI() /day14/demo1
StringBuffer getRequestURL() http://localhost/day14/demo1
URL:统一资源定位符,http://localhost/day14/demo1
URI:统一资源标识符,/day14/demo1
-
获取协议及版本:HTTP/1.1
String getProtocol()
-
获取客户机的IP地址:
String getRemoteAddr()
-
获取请求头数据
- 方法
- (*)String getHeader(String name):通过请求头的名称获取请求头的值
- Enumeration<String> getHeaderNames():获取所有的请求头名称
获取请求体数据
-
请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
-
步骤
-
获取流对象
BufferedReader getReader():获取字符输入流,只能操作字符数据
ServletInputStream getInputStream():获取字节输入流,可以操作所有类型的数据
- 在文件上传知识点后详解
-
再从流对象中拿数据
-
其它功能
获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数
-
String getParameter(String name):根据参数名称获取参数值 username=zs&password=123
-
String[] getParemeterValues(String name):根据参数名称获取参数值的数组 hobby=xx&hobby=game
-
Enumeration<String> getParameterName():获取所有请求的参数名称
-
Map<String,String[]> getParameterMap():获取所有参数的map集合
-
中文乱码问题:
-
get方式:tomcat8已经将get方式乱码entity解决了
-
post方式:会乱码
解决方法:在获取参数前,设置request的编码request.setCharacterEncoding(“utf-8”);
package request; 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.BufferedReader; import java.io.IOException; import java.util.Enumeration; import java.util.Map; import java.util.Set; /** * @author: 小码农 * @create: 2021-08-28 21:36 **/ @WebServlet("/RequestDemo6") public class RequestDemo6 extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // post 获取请求参数 // 根据参数名获取参数值 String username = req.getParameter("username"); /*System.out.println("post"); System.out.println(username);*/ // 根据参数名称获取参数值的数组 /*String[] hobbies = req.getParameterValues("hobby"); for (String hobby:hobbies){ System.out.println(hobby); }*/ // 获取所有请求的参数名称 /*Enumeration<String> parameterNames = req.getParameterNames(); while (parameterNames.hasMoreElements()){ String name = parameterNames.nextElement(); System.out.println(name); String parameter = req.getParameter(name); System.out.println(parameter); System.out.println("---------------"); }*/ // 获取所有参数的map集合 Map<String, String[]> parameterMap = req.getParameterMap(); // 遍历 Set<String> strings = parameterMap.keySet(); for (String name:strings){ // 根据键获取值 String[] strings1 = parameterMap.get(name); System.out.println(name); for (String value:strings1){ System.out.println(value); } System.out.println("----------------"); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // get 获取请求参数 // 根据参数名获取参数值 /*String username = req.getParameter("username"); System.out.println("get"); System.out.println(username);*/ this.doPost(req,resp); } }
-
请求转发:一种在服务器内部的资源跳转方式
- 步骤:
- 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
- 使用RequestDispatcher对象来进行转发:forward(ServletRequest request,ServletResponse response)
- 特点
- 浏览器地址栏路径不发生变化
- 转发只能转发到当前服务器内部资源中。
- 转发是一次请求
package request;
import javax.servlet.RequestDispatcher;
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;
/**
* @author: 小码农
* @create: 2021-08-28 21:36
**/
@WebServlet("/RequestDemo8")
public class RequestDemo8 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo8888被访问了...");
// 存储数据到request域中
req.setAttribute("msg","hello");
// 转发到demo9资源
req.getRequestDispatcher("/RequestDemo9").forward(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
package request;
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;
/**
* @author: 小码农
* @create: 2021-08-28 21:36
**/
@WebServlet("/RequestDemo9")
public class RequestDemo9 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取数据
Object msg = req.getAttribute("msg");
System.out.println(msg);
System.out.println("demo9999被访问了...");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
共享数据
域对象:一个有作用范围的对象,可以在范围内共享对象
request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据。
方法:
- setAttribute(String name,Object obj):存储数据
- Object getAttribute(String name):通过键获取值
- void removeAttribute(String name):通过键移除键值对
获取ServletContext
- ServletContext getServletContext()
案例:用户登录
需求:
-
编写login.html登录页面
username&password 两个输入框
-
使用Druid数据库连接池技术,操作mysql,day14数据库中的user表
-
使用jdbcTemplate技术封装JDBC
-
登录成功跳转到SuccessServlet展示:登陆成功!用户名,欢迎您
-
登陆失败挑战到FailServlet展示:登录失败,用户名或密码错误
开发步骤
-
创建项目,导入html页面,配置文件,导入jar包
driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/db2 username=root password=123456 # 初识化连接 initialSize=5 # 最大连接数 maxActive=10 # 超时时间 maxWait=3000
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hM3fZiAk-1630236204261)(image/image-20210829163357548.png)]
-
创建数据库环境
// 创建LOGIN数据库 CREATE DATABASE LOGIN; USE LOGIN; // 创建USER表 CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) UNIQUE NOT NULL, password VARCHAR(32) NOT NULL );
-
创建com.myz.domain,创建User实体类
package com.myz.domain; /** * @author: 小码农 * @create: 2021-08-29 16:35 * 用户实体类 **/ public class User { private int id; private String username; private String password; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
-
创建包com.myz.dao,创建类UserDao,提供login方法
创建JDBC工厂类
package com.myz.utils;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;import java.io.IOException;import java.io.InputStream;import java.sql.Connection;import java.sql.SQLException;import java.util.Properties;/** * @author: 小码农 * @create: 2021-08-29 16:40 * JDBC工具类,使用Durid连接池 **/public class JDBCUtils { private static DataSource ds; static { // 1.加载配置文件 Properties pro = new Properties(); // 2.使用ClassLoader加载配置文件,获取字节输入流 InputStream resourceAsStream = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"); try { pro.load(resourceAsStream); // 3.初始化连接池对象 ds = DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /* * 获取连接池对象 * */ public static DataSource getDataSource(){ return ds; } /* * 获取连接Connection对象 * */ public static Connection getConnection() throws SQLException { return ds.getConnection(); }}
UserDao类
package com.myz.dao; import com.alibaba.druid.util.JdbcUtils; import com.myz.domain.User; import com.myz.utils.JDBCUtils; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; /** * @author: 小码农 * @create: 2021-08-29 16:37 * 操作数据库中user表的类 **/ public class UserDao { //声明JDBCTemplate对象共用 private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource()); /* * 登录方法 * loginUser只有用户名密码 * user包含用户全部数据 * * */ public User login(User loginUser){ try { // 1.编写sql String sql = "select * from user where username=? and password=?"; // 2.调用query方法 User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), loginUser.getUsername(), loginUser.getPassword()); return user; }catch (DataAccessException e){ e.printStackTrace(); return null; } } }
-
编写com.myz.servlet.LoginServlet类
-
login.html中的form表单的action路径写法
- 虚拟目录+Servlet的资源路径
-
BeanUtils工具类,简化数据封装
- 用于封装JavaBean的
-
JavaBean:标准的Java类
要求
-
类必须被public修饰
-
必须提供空参的构造器
-
成员变量必须使用private修饰
-
提供公共setter和getter方法
功能:封装数据
-
-
概念:
成员变量
属性:setter和getter方法截取后的产物
例如:getUsername() --> Username ——>username
-
方法:
- setProperty(Object obj,String name,String value):把value值按照obj对象有关于name的set方法,对obj赋值
- getProperty(Object obj,String name):根据name调用obj对象的getname方法,返回value值。
- populate(Object obj,Map map):封装JavaBean,它会把map集合的键值对信息,封装到对应的JavaBean对象中。