JavaWeb2
1. web服务器软件:Tomcat
1.1 概述
- 服务器:安装了服务器软件的计算机
- 服务器软件:接受用户的请求,处理请求,做出响应
- web服务器软件:接受用户的请求,处理请求,做出响应
- 在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目
- 常见的Java相关的web服务器软件
- webLogic:Oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的
- webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的
- JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的
- Tomcat:Apache基金阻阻止,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范。开源的,免费的。
- JavaEE:Java语言在企业级开发中使用的技术规范的总和,一共规定了13项大的规范
1.2 Tomcat安装、卸载、启动
- 下载:https://tomcat.apache.org/
- 安装:解压压缩包(安装的目录不要有空格和中文)
- 卸载:删除目录
-
启动:
-
bin/starp.bat,双击运行即可
-
访问:在浏览器输入 http://localhost:8080 回车访问自己
http://别人的ip:8080 回车访问别人
-
可能遇到的问题:
-
运行后的黑窗口一闪而过:
- 原因:没有正确配置JAVA_HOME环境变量
- 解决方案:正确配置JAVA_HOME环境变量
-
启动报错:
-
暴力解决方案:找到占用8080对应的进程,杀死该进程
- cmd:
netstat -ano
- 找到8080对应的PID(进程ID)、
- 任务管理器找到该进程并结束
- cmd:
-
温柔解决方案:修改自身的端口号(不建议)
-
打开conf/server.xml文件
-
修改默认端口号
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
-
-
-
-
一般会将Tomcat的默认端口号修改为80。80端口号是http协议的默认端口号。
- 好处:访问时不用输入端口号
-
-
关闭
- 正常关闭:
- 执行bin/shutdown.bat批处理文件
- 或者在启动窗口按Ctrl+C
- 强制关闭:(不推荐)
- 点击启动窗口的×
- 正常关闭:
-
配置
-
部署项目的方式:
- 直接将项目放在webapps文件夹下
- 访问时输入:
localhost/hello/hello.html
即可访问 /hello
:项目的访问路径--->虚拟目录 一般和资源名称相同hello.html
:资源名称- 简化:将项目文件夹打包成hello.war文件,将其放在webapps文件夹下,会自动解压缩
- 访问时输入:
- 配置conf/server.xml文件(不推荐,很不安全)
- 在<Host>标签体中配置
<Context docBase="D:\hello" path="/hehe"/>
- 其中docbase:项目存放的路径 path:虚拟路径
- 在<Host>标签体中配置
- 在conf\Catalina\localhost下创建任意名称的xml文件。在文件中编写
<Context docBase="D:\hello" />
- 虚拟目录即xml文件的名称
- 直接将项目放在webapps文件夹下
-
静态项目和动态项目区别
-
目录结构不同
-
java动态项目的目录结构:
-
项目的根目录
- WEB-INF目录:
- web.xml:web项目的核心配置文件
- classes目录:放置字节码文件的目录
- lib目录:放置依赖的jar包
- WEB-INF目录:
-
-
-
-
1.3 Tomcat与IDEA集成&创建项目
将Tomcat继承到IDEA中,并且创建JavaEE的项目,部署项目(本人使用IDEA 2021 3.3 版本)
IDEA集成Tomcat:
- Run--->Edit Configurations
- 左上角加号--->Tomcat Sever--->Local
- 选择JRE--->OK
创建JavaEE项目
- new Module--->Java Module--->Next-->起模块名为myTomcat--->Finish--->ok
- 右键创建的Module--->add Framework Support
- 选择 JavaEE Version为Java EE 7
- 勾选web Application,JavaEE 7版本默认为3.1版本,勾选Create web.xml--->ok
- 一般默认会在Tomcat的Deployment添加该项目,若没有则执行下列步骤
- Run--->Edit Configurations--->Tomcat Sever--->Deployment
- 左上角加号--->Artifact--->myTomcat:war exploded--->Application context:默认为/(即虚拟目录)
- OK
2. Servlet入门
2.1 概述
Servlet即 server applet,运行在服务器端的小程序
- servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
- 将来我们自定义一个类,实现Servlet接口,复写方法
2.2 快速入门
-
创建JavaEE项目
-
定义一个类,实现Servlet接口
- 关于手写类实现Servlet不会导包的问题解决办法:
- File--->Project Structure--->myTomcat-->Dependencies
- 左上角加号--->Library--->Application Server Libraries--->Tomcat 8.5.81
- Add Selected--->Apply--->OK
- 关于手写类实现Servlet不会导包的问题解决办法:
-
实现接口中的抽象方法
public class ServletDemo1 implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } //提供服务的方法 @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("Hello Servlet"); } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }
-
配置Servlet
<!--配置Servlet--> <servlet> <servlet-name>demo1</servlet-name> <servlet-class>com.mark.web.servlet.ServletDemo1</servlet-class> </servlet> <!--映射--> <servlet-mapping> <servlet-name>demo1</servlet-name> <url-pattern>/demo1</url-pattern> </servlet-mapping>
-
在浏览器中输入http://localhost:8080/demo1 会发现IDEA控制台上输出Hello Servlet
2.3 执行原理
- 当服务器接收到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
- 查找web.xml文件,是否有对应的<url-pattern>标签体内容
- 如果有则再找到对应的<servlet-class>全类名
- tomcat会将字节码文件加载进内存,且创建其对象
- 调用其方法
2.4 Servlet中的方法
public class ServletDemo2 implements Servlet {
/**
* 初始化方法
* 在Servlet被创建时执行,只执行一次
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init...");
}
/**
* 获取ServletConfig对象
* ServletConfig是Servlet的配置对象
* @return
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 提供服务的方法
* 每一次Servlet被访问时执行,执行多次
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("servlet...");
}
/**
* 获取Servlet的一些信息:版本、作者等等
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 销毁方法
* 在Servlet被杀死时执行(在服务器正常关闭时执行),只执行一次
*/
@Override
public void destroy() {
System.out.println("destroy...");
}
}
-
Servlet的生命周期方法:
-
被创建:执行init方法,只执行一次
-
默认情况下,第一次被访问时,Servlet被创建
-
可以指定Servlet的创建时机:修改web.xml配置文件
<servlet> <servlet-name>demo2</servlet-name> <servlet-class>com.mark.web.servlet.ServletDemo2</servlet-class> <!--指定Servlet的创建时机 1.第一次被访问时创建 <load-on-startup>的值为负数(默认值为-1) 2.在服务器启动时创建 <load-on-startup>的值为0或正整数(一般配0-10) --> <load-on-startup>5</load-on-startup><!--当服务器启动时加载--> </servlet>
-
init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
- 多个用户同时访问时,可能出现线程安全问题
- 解决:尽量不要在Servlet中定义成员变量,即使定义了成员变量,也不要对其修改值
-
-
提供服务:执行service方法,执行多次
- 每次访问Servlet时,Service方法都会被调用一次
-
被销毁:执行destory方法,只执行一次
- Servlet被销毁时执行。服务器关闭时,Servlet被销毁。
- 只有服务器正常关闭时,才会执行destory方法
- destory方法在Servlet被销毁前执行,一般用于释放资源
-
2.5 注解配置
- Servlet 3.0:
- 好处:支持注解配置。可以不需要web.xml
- 步骤:
- 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
- 定义一个类,实现Servlet接口
- 复写方法
- 在类上使用@WebServlet注解,进行配置
@WebServlet(urlPatterns = "/demo")
- 还可以直接写
@WebServlet("/demo")
引号中直接写资源路径
- 还可以直接写
2.6 IDEA与Tomcat的相关配置
- IDEA会为每一个Tomcat部署的项目单独建立一份配置文件
- 查看控制台的log:
Using CATALINA_BASE: "C:\Users\Mark\AppData\Local\JetBrains\IntelliJIdea2021.3\tomcat\2cbcd2b9-3ae3-45b3-b582-c86f8e7e95e4"
- 查看控制台的log:
- 工作空间项目 和 tomcat部署的web项目
- tomcat真正访问的是"tomcat部署的web项目","tomcat部署的web项目"对应着”工作空间项目“的web目录下的所有资源。
- WEB-INF目录下的资源不能被浏览器直接访问
- 断点调试:加断点后Debug(小虫子)
3. 深入了解Servlet
3.1 Servlet体系结构
Interface Servlet
-----GenericServlet抽象类 实现了Servlet接口
-----HttpServlet抽象类 继承于GenericServlet抽象类
-
GenericServlet:将Servlet接口中的其他方法做了默认空实现,只将service()作为抽象
-
将来定义Servlet类时,可以继承GenericServlet,实现service()即可
@WebServlet("/demo2") public class ServletDemo2 extends GenericServlet { @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("Demo2..."); } }
-
-
HttpServlet:对HTTP协议的一种封装,可以简化操作。接收数据不再需要在service()中写if-else判断HTTP的方式为get还是post
-
步骤:
-
定义类继承HttpServlet
-
复写doGet()或者doPost()方法
@WebServlet("/demo3") public class ServletDemo3 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("doGet...");//默认为get方式 } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("doPost...");//可以通过表单方式改用post } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="demo3" method="post"> <!--如果配置中Deployment-Application context为/则可以写为/demo3 但是如果为/路径 则只能写demo3,不能加/ --> <input name="username"> <input type="submit" value="提交"> </form> </body> </html>
-
-
3.2 Servlet的相关配置
-
urlpartten:Servlet的访问路径
-
一个Servlet可以定义多个访问路径,浏览器可以通过任意一个访问路径访问
@WebServlet({"/d4","/dd4","/demo4"}) public class ServletDemo4 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo4..."); } }
-
路径定义规则:
-
/xxx
-
/xxx/xxx
:多层路径,目录结构@WebServlet("/user/demo4") public class ServletDemo4 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo4..."); } }
@WebServlet("/user/*")//访问时可以通过任意路径访问,/*是访问优先级最低的 public class ServletDemo4 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo4..."); } }
-
*.do
@WebServlet("*.do")//※不要加/ 访问时通过xxx.do访问 xxx为任意路径 public class ServletDemo4 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo4..."); } }
-
-
4. HTTP协议
4.1 概述
- 概念:HTTP即Hyper Text Transfer Protocol,超文本传输协议
- 传输协议:定义了客户端和服务器端通信时,发送数据的格式
- 特点:
- 基于TCP/IP的高级协议
- HTTP的默认端口号:80
- 基于请求/响应模型的 一次请求对应一次响应
- 是无状态的协议:每次请求之间相互独立,不能交互数据
- 历史版本:
- 1.0 每一次请求响应都会建立新的连接
- 1.1 服用连接
4.2 请求消息数据格式
-
请求行
请求方式 请求url 请求协议/版本
Get /myServlet/login.html HTTP/1.1
-
HTTP协议有7中请求方式,常用的有两种
-
GET:
-
请求参数在请求行中(在URL后):
http://localhost:8080/myServlet/demo3?username=aaa
-
请求的url长度有限制
-
不太安全
-
-
POST:
- 请求参数在请求体中
- 请求的url长度没有限制的
- 相对安全
-
-
-
请求头:浏览器告诉服务器浏览器自身的一些信息
请求头名称:请求头值
- 常见的请求头:
- User-Agent:浏览器告诉服务器,浏览器访问服务器使用的浏览器版本信息
- 可以以在服务器端获取该头的信息,解决兼容性问题
- Referer:告诉服务器,当前请求从哪里来
- 作用:
- 防盗链:
- 统计工作:
- 作用:
- User-Agent:浏览器告诉服务器,浏览器访问服务器使用的浏览器版本信息
- 常见的请求头:
-
请求空行:就是用于分割POST请求的请求头和请求体的
- 空行
-
请求体(正文):封装POST请求消息的请求体
-
字符串格式:
GET /myServlet/login.html HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate, br Referer: http://localhost:8080/myServlet/login.html Connection: keep-alive Cookie: JSESSIONID=7AAA4903DCD0183D7F0A8ECD445090E1 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
5. Request
5.1 原理
request和response对象的原理
- request和response对象是由服务器创建的。我们来使用它们
- request对象是来获取请求消息,response对象是来设置响应消息
5.2 Request获取请求消息
5.2.1 Request继承的体系结构
-
ServletRequest --- 接口
- HttpServletRequest --- 接口(继承于ServletRequest)
- org.apache.catalina.connector.RequestFacade --- 实现类(tomcat编写)
5.2.2 Request获取请求消息
-
获取请求行数据
Get /myServlet/demo1?name=zhangsan HTTP/1.1
-
方法:
-
获取请求方式:GET
String getMethod()
-
⭐获取虚拟目录:/myServlet
String getContextPath()
-
获取Servlet路径:/demo1
String getServletPath
() -
获取get方式的请求参数:?name=zhangsan
String getQueryString()
-
⭐获取请求URI:/myServlet/demo1
String getRequestURI()
String getRequestURL()
: 返回http://myServlet/demo1
-
获取协议以及版本:HTTP/1.1
String getProtocol()
-
获取客户机的IP地址
String getRemoteAddr()
public class RequestDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取请求方式:GET String method = request.getMethod(); System.out.println(method); //⭐获取虚拟目录:/myServlet String contextPath = request.getContextPath(); System.out.println(contextPath); //获取Servlet路径:/demo1 String servletPath = request.getServletPath(); System.out.println(servletPath); //获取get方式的请求参数:?name=zhangsan String queryString = request.getQueryString(); System.out.println(queryString); //⭐获取请求URI:/myServlet/demo1 String requestURI = request.getRequestURI(); System.out.println(requestURI); StringBuffer requestURL = request.getRequestURL(); System.out.println(requestURL); //获取协议以及版本:HTTP/1.1 String protocol = request.getProtocol(); System.out.println(protocol); //获取客户机的IP地址 String remoteAddr = request.getRemoteAddr(); System.out.println(remoteAddr); } }
-
-
-
获取请求头数据
-
方法:
- ⭐
String getHeader(String name)
:通过请求头的名称获取请求头的值 Enumeration getHeaderNames()
:获取所有的请求头名称
@WebServlet("/RequestDemo2") public class RequestDemo2 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取所有请求头名称 Enumeration<String> headerNames = request.getHeaderNames(); //遍历 while (headerNames.hasMoreElements()){ String name = headerNames.nextElement(); //根据名称获取请求头名称 String value = request.getHeader(name); System.out.println(name+"--"+value); } } } host--localhost:8080 user-agent--Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0 accept--text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/ *;q=0.8 accept-language--zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 accept-encoding--gzip, deflate, br connection--keep-alive cookie--JSESSIONID=324F172A257FA6B8DF4869314F01E64B; Webstorm-ec1c0726=62f9d3ca-a2fa-4d36-aef6-9086650fec79 upgrade-insecure-requests--1 sec-fetch-dest--document sec-fetch-mode--navigate sec-fetch-site--none sec-fetch-user--?1
@WebServlet("/RequestDemo3") public class RequestDemo3 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取请求头数据:user-agent String agent = request.getHeader("user-agent"); //判断agent的浏览器版本 if (agent.contains("Chrome")) { System.out.println("谷歌"); } else if (agent.contains("Firefox")) { System.out.println("火狐"); } } }
@WebServlet("/RequestDemo4") public class RequestDemo4 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取请求头数据:referer String referer = request.getHeader("referer"); System.out.println(referer);//http://localhost:8080/myServlet/login.html //防盗链 if (referer != null) { if (referer.contains("/myServlet")) { // System.out.println("正常访问"); response.setContentType("text/html;charset=utf-8"); response.getWriter().write("正常访问"); } else { // System.out.println("你进不去哦"); response.setContentType("text/html;charset=utf-8"); response.getWriter().write("你进不去哦"); } } } }
- ⭐
-
-
获取请求体数据
-
请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
-
步骤:
- 获取流对象
BufferReader getReader()
:获取字符输入流,只能操作字符数据ServletInputStream getInputStream()
:获取字节输入流,可以操作所有类型数据
- 再从流对象中拿数据
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取请求消息体 //1. 获取字符流 BufferedReader br = request.getReader(); String line = null; while ((line = br.readLine()) != null) { System.out.println(line); } } username=aaa&password=ddd
- 获取流对象
-
5.2.3 Request其他功能
-
获取请求参数通用方式
-
String getParameter(String name)
:根据参数名称来获取参数值 username=aaa&password=ddd@WebServlet("/RequestDemo6") public class RequestDemo6 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); System.out.println("get"); System.out.println(username); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // String username = request.getParameter("username"); // System.out.println("post"); // System.out.println(username); this.doGet(request,response);//执行doGet的代码逻辑,节省代码重复 } }
<form action="/myServlet/RequestDemo6" method="post"> <input type="text" name="username" placeholder="username"><br> <input type="text" name="password" placeholder="password"><br> <input type="submit" value="注册"> </form>
-
String[] getParameterValues(String name)
:根据参数名称来获取参数值的数组 hobby=xx&hobby=game@WebServlet("/RequestDemo6") public class RequestDemo6 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String[] hobbies = request.getParameterValues("hobby"); for (String hobby : hobbies) { System.out.println(hobby); } } }
<form action="/myServlet/RequestDemo6" method="get"> <input type="text" name="username" placeholder="username"><br> <input type="text" name="password" placeholder="password"><br> <input type="checkbox" name="hobby" value="game">游戏 <input type="checkbox" name="hobby" value="study">学习<br> <input type="submit" value="注册"> </form>
-
Enumeration getParameterNames()
:获取所有请求参数的名称Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()){ String name = parameterNames.nextElement(); System.out.println(name); String value = request.getParameter(name); System.out.println(value); System.out.println("----"); }
-
Map<String,String[]> getParameterMap()
:获取所有参数的map集合Map<String, String[]> parameterMap = request.getParameterMap(); Set<String> keyset = parameterMap.keySet(); for (String name : keyset) { //根据key键获取值 String[] values = parameterMap.get(name); System.out.println(name); for (String value : values) { System.out.println(value); } System.out.println("---------"); }
-
中文乱码问题
-
get方式:tomcat8已经将get方式乱码问题解决
-
post方式:会乱码
-
解决方式:获取参数前设置request编码
request.setCharacterEncoding("utf-8");
-
-
-
-
请求转发:一种在服务器内部的资源跳转方式
-
步骤:
-
通过request对象来获取请求转发器对象:
RequestDispatcher getRequestDispatcher(String path)
-
使用RequestDispatcher对象来进行转发:
forward(ServletRequest request, ServletResponse response)
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("demo8被访问"); //转发到demo9资源 // RequestDispatcher requestDispatcher = request.getRequestDispatcher("/RequestDemo9"); // requestDispatcher.forward(request,response); request.getRequestDispatcher("/RequestDemo9").forward(request,response); }
-
-
特点:
- 浏览器地址栏路径不发生变化
- 只能转发到当前服务器内部资源中
- 转发是一次请求,多个资源使用同一次请求
-
共享数据
-
域对象:一个有作用范围的对象,可以在范围内共享数据
-
request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
-
方法:
void setAttribute(String name,Object obj)
:存储数据Object getAttribyte(String name)
:通过键获取值removeAttribute(String name)
:通过键移除键值对
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("demo8被访问"); //存储数据 request.setAttribute("msg","hello"); //转发到demo9资源 request.getRequestDispatcher("/RequestDemo9").forward(request,response); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("demo9被访问"); //获取数据 Object msg = request.getAttribute("msg"); System.out.println(msg); }
-
-
获取ServletContext动态获取虚拟目录
-
ServletContext getServletContext()
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext servletContext = request.getServletContext(); System.out.println(servletContext); //org.apache.catalina.core.ApplicationContextFacade@5e3dfad6 }
-
5.3 用户登陆
5.3.1 需求
1.编写login.html登录页面
username & password 两个输入框
2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表
3.使用JdbcTemplate技术封装JDBC
4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误
5.3.2 分析
5.3.3 开发步骤
-
创建项目,导入HTML页面,配置文件、jar包
-
创建数据库环境
CREATE DATABASE Login; USE Login; CREATE TABLE USER( id INT PRIMARY KEY auto_increment, username VARCHAR(32) UNIQUE NOT NULL, password VARCHAR(32) NOT NULL );
-
创建包com.mark.domain,创建类User
package com.mark.domain; /** - @ClassName User - @Description TODO 用户的JavaBean(实体类) - @Author Mark - @Date 2022/7/31 20:44 - @Version 1.0 */ 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.mark.dao,创建类UserDao,提供login()方法
package com.mark.dao; import com.mark.domain.User; import com.mark.util.JDBCUtils; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; /** * @ClassName UserDao * @Description TODO 操纵数据库中User表的类 * @Author Mark * @Date 2022/7/31 20:48 * @Version 1.0 */ public class UserDao { //声明JDBCTemplate对象共用 private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource()); /** * 登陆方法 * @param loginUser 只有用户名和密码 * @return User包含用户全部数据, 没有查询到,返回null */ public User login(User loginUser) { try { String sql = "select * from user where username=? and password = ?"; User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), loginUser.getUsername(), loginUser.getPassword()); return user; } catch (DataAccessException e) { e.printStackTrace();//记录日志 return null; } } }
-
编写com.mark.web.servlet.LoginServlet类
- login.html中form表单的action路径的写法:
- 虚拟目录+Servlet的资源路径
package com.mark.web.servlet; import com.mark.dao.UserDao; import com.mark.domain.User; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); User loginUser = new User(); loginUser.setUsername(username); loginUser.setPassword(password); UserDao dao = new UserDao(); User user = dao.login(loginUser); if (user == null) { //登陆失败 request.getRequestDispatcher("/FailServlet").forward(request, response); } else { //登陆成功 request.setAttribute("user", user); request.getRequestDispatcher("/SuccessServlet").forward(request, response); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
package com.mark.web.servlet; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/FailServlet") public class FailServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //给页面写一句话 response.setContentType("text/html;charset=utf-8"); response.getWriter().write("登录失败,用户名或密码错误"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } }
package com.mark.web.servlet; import com.mark.domain.User; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/SuccessServlet") public class SuccessServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取request域中共享的user对象 User user = (User) request.getAttribute("user"); if (user != null) { //给页面写一句话 response.setContentType("text/html;charset=utf-8"); response.getWriter().write("登录成功!" + user.getUsername() + ",欢迎您"); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
- login.html中form表单的action路径的写法:
5.3.4 BeanUtils工具类
完成(简化)数据的封装,用于封装JavaBean
request.setCharacterEncoding("utf-8");
// String username = request.getParameter("username");
// String password = request.getParameter("password");
// User loginUser = new User();
// loginUser.setUsername(username);
// loginUser.setPassword(password);
//获取所有请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
//创建User对象
User loginUser= new User();
//使用BeanUtils封装
try {
BeanUtils.populate(loginUser,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
-
JavaBean:标准的Java类,一般放在domain包里
- 要求:
- 类必须被public修饰
- 必须提供空参的构造器
- 成员遍历必须使用private修饰
- 提供公共的getter和setter方法
- 功能:封装数据
- 要求:
-
概念:
-
成员变量
-
属性:setter和getter方法截取后的产物。
-
例如:getUsername() ---> Username --->username
-
大多数时候和成员变量一样,少数情况不一样
private String gender; public void setHehe(String gender) { this.gender = gender; }
-
-
-
方法:
-
setProperty():设置属性值
@Test public void test(){ User user = new User(); try { BeanUtils.setProperty(user,"username","张三"); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } System.out.println(user); }
-
getProperty():获取属性值
String username = null; try { username = BeanUtils.getProperty(user, "username"); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); }
-
⭐populate():封装
-
6. HTTP协议:响应消息
-
数据格式:
- 响应行
- 组成:协议/版本 响应状态码 状态码 描述
- 响应状态码:服务器告诉客户浏览器本次请求和响应的一个状态
- 状态码都是3位数字
- 分类:
- 1xx:服务器接收客户端消息但没有接收完成,等待一段时间后,发送1xx状态码
- 2xx:成功
- 3xx:重定向。代表:302(重定向)、304(访问缓存)
- 4xx:客户端错误。
- 代表:
- 404(请求路径没有对应资源)
- 405(请求方式没有doxxx方法)
- 代表:
- 5xx:服务器端错误。代表:500(服务器内部出现异常)
- 响应头
- 格式:头名称 : 值
- 常见的响应头::
- Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
- Content-disposition:服务器告诉客户端以什么格式打开响应体数据
- 值:
- in-line:默认值,在当前页面内打开
- attachment:以附件形式打开响应体。常用于文件下载:attachment;filename=xxx
- 值:
- 响应空行
- 响应体:传输的数据
- 响应行
-
响应字符串格式
HTTP/1.1 200 OK Bdpagetype: 1 Connection: keep-alive Content-Type: text/html; charset=utf-8 Date: Mon, 01 Aug 2022 04:36:04 GMT Server: BWS/1.1 <html> <head> <title>$Title$</title> </head> <body> hello,Response </body> </html>
7. Response对象
7.1 Response功能介绍
- 设置响应消息
- 设置响应行
- 设置状态码:
setStatus(int sc)
- 设置状态码:
- 设置响应头
setHeader(String name, String value)
- 设置响应体
- 使用步骤:
- 获取输出流
- 字符输出流:
PrintWriter getWriter()
- 字节输出流:
ServletOutputStream getOutputStream()
- 字符输出流:
- 使用输出流,将数据输出到客户端浏览器
- 获取输出流
- 使用步骤:
- 设置响应行
7.2 Response案例
7.2.1 完成重定向
-
重定向:资源跳转的方式
@WebServlet("/ResponseDemo1") public class ResponseDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Demo1..."); //访问ResponseDemo1会自动跳转到ResponseDemo2 /* //1.设置状态码为302 response.setStatus(302); //设置响应头location response.setHeader("location","/myServlet/ResponseDemo2"); */ //简单的重定向 response.sendRedirect("/myServlet/ResponseDemo2"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
重定向特点:redirect
- 地址栏发生变化
- 重定向可以访问其他站点的资源
- 重定向是两次请求:一次重定向请求,一次定向路径请求。不能使用request对象共享数据
- 转发的特点:forword
- 转发地址栏路径不变
- 转发只能访问当前服务器下的资源
- 转发是一次请求,可以使用request对象来共享数据
-
路径写法:
-
路径的分类:
-
相对路径:通过相对路径不可以确定唯一资源
- 如./index.html
- 不以/开头,以.开头路径
规则:找到当前资源和目标资源之间的相对位置关系
-
绝对路径:通过绝对路径可以确定唯一资源
- 如:http://localhost:8080/myServlet/ResponseDemo2 /myServlet/ResponseDemo2
- 以/开头的路径
规则:判断定义的路径是给谁用的?判断请求将来从哪发出
- 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
- 建议虚拟目录动态获取:
request.getContextPath()
- 建议虚拟目录动态获取:
- 给服务器使用:不需要加虚拟目录
动态获取虚拟目录:
//动态获取虚拟目录 String contextPath = request.getContextPath(); //简单的重定向 response.sendRedirect(contextPath+"/ResponseDemo2");
-
-
7.2.2 服务器输出字符数据到浏览器
-
步骤:
-
获取字符输出流
-
输出数据
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取字符输出流 PrintWriter pw = response.getWriter(); //输出数据 //pw.write("hello response"); pw.write("<h1>hello response</h1>"); }
-
-
中文乱码问题:编解码使用的字符集不一致。客户端浏览器于电脑一致:使用GBK2312,而tomcat使用ISO-8859-1
-
request.setCharacterEncoding("GBK");
获取流对象之前设置流的默认编码。 -
告诉浏览器,服务器发送的消息体数据的编码,建议浏览器使用该编码解码
response.setHeader("content-type", "text/html;charset=utf-8");
简单形式:
response.setContentType("text/html;charset=utf-8");
-
7.2.3 服务器输出字节数据到浏览器
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("content-type", "text/html;charset=utf-8");
//获取字节输出流
ServletOutputStream sos = response.getOutputStream();
//输出数据
sos.write("hello".getBytes());
}
7.2.4 验证码案例
-
本质:图片
-
目的:防止恶意表单注册
package com.mark.web.response; import javax.imageio.ImageIO; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; @WebServlet("/CheckCodeServlet") public class CheckCodeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //创建一个对象,在内存中代表图片(验证码图片对象) int width = 100; int height = 50; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); //美化图片 //填充背景色 Graphics g = image.getGraphics();//画笔对象 g.setColor(Color.PINK);//设置画笔颜色 g.fillRect(0, 0, width, height);//填充颜色 //画边框 g.setColor(Color.BLUE); g.drawRect(0, 0, width - 1, height - 1); //写验证码 String str = "QWERTYUIOPASDGFHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789"; //生成随机角标 Random random = new Random(); for (int i = 1; i <= 4; i++) { int index = random.nextInt(str.length()); //获取字符 char ch = str.charAt(index); g.drawString(ch + "", width / 5 * i, height / 2); } //干扰线 g.setColor(Color.GREEN); for (int i = 1; i < 10; i++) { //随机生成坐标点 int x1 = random.nextInt(width); int x2 = random.nextInt(width); int y1 = random.nextInt(height); int y2 = random.nextInt(height); g.drawLine(x1, y1, x2, y2); } //将图片输出到页面展示 ImageIO.write(image, "jpg", response.getOutputStream()); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script> /* 点击超链接或者图片,换一张 1.给超链接和图片绑定单击事件 2.重新设置src的属性值 */ window.onload=function () { //获取图片对象 var img = document.getElementById("checkCode"); //绑定单击事件 img.onclick=function () { //加时间戳 var date = new Date().getTime(); img.src="/myServlet/CheckCodeServlet?"+date; } } </script> </head> <body> <img id="checkCode" src="/myServlet/CheckCodeServlet"/> <a id="change" href="">看不清,换一张</a> </body> </html>
8. ServletContext对象
8.1 概述
- 概念:代表整个web应用,可以和程序的容器(服务器)来通信
- 功能:
- 获取MIME类型
- 域对象:共享数据
- 获取文件的真实(服务器)路径
8.2 获取
-
通过request对象获取
- request.getServletContext();
- 通过HttpServlet获取:this.getServletContext();
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.通过request对象获取 ServletContext context1 = request.getServletContext(); //2.通过HttpServlet获取 ServletContext context2 = this.getServletContext(); System.out.println(context1); System.out.println(context2); System.out.println(context1==context2); /* org.apache.catalina.core.ApplicationContextFacade@483e3ce3 org.apache.catalina.core.ApplicationContextFacade@483e3ce3 true */ }
8.3 功能
-
获取MIME类型:
-
MIME类型:在互联网通信过程中定义的一种文件数据类型标准
- 格式:大类型/小类型 eg: text/html image/jpeg
-
获取:
String getMimeType(String file)
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = this.getServletContext(); //定义文件名称 String filename="a.jpg"; //获取MIME类型 String mimeType = context.getMimeType(filename); System.out.println(mimeType);//image/jpeg }
-
-
域对象:共享数据
-
setAttribute(String name,Object value)
-
getAttribute(String name)
-
removeAttribute(String name)
-
ServletContext对象的范围:所有用户所有请求的数据
@WebServlet("/ServletContextDemo3") public class ServletContextDemo3 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = this.getServletContext(); context.setAttribute("msg","haha"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } }
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = this.getServletContext(); Object msg = context.getAttribute("msg"); System.out.println(msg); }
-
-
获取文件真实(服务器)路径
- 方法:
String getRealPath(String path)
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = this.getServletContext(); //获取文件的服务器路径 String realPath = context.getRealPath("/b.txt");//web目录下资源访问 //File file = new File(realPath); System.out.println(realPath); //D:\Code\IJavaMyJavaWeb\out\artifacts\myServlet_war_exploded\b.txt String realPath1 = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问 System.out.println(realPath1); //D:\Code\IJavaMyJavaWeb\out\artifacts\myServlet_war_exploded\WEB-INF\c.txt String realPath2 = context.getRealPath("/WEB-INF/clsses/a.txt");//src目录下的资源路径 System.out.println(realPath2); //D:\Code\IJavaMyJavaWeb\out\artifacts\myServlet_war_exploded\WEB-INF\clsses\a.txt }
- 方法:
8.4 案例:文件下载
-
文件下载需求:
- 页面显示超链接
- 点击超链接后弹出下载提示框
- 完成图片文件下载
-
分析:
- 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
- 任何资源都必须弹出下载提示框
- 使用响应头设置资源的打开方式:
content-disposition:attachment;filename=xx;
-
步骤:
-
定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
-
定义Servlet
- 获取文件名称
- 使用字节输入流加载文件进内存
- 指定response的响应头
content-diposition:attachment;filename=xx;
- 将数据写出到response输出流
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <a href="/myServlet/img/1.jpg">图片1</a> <a href="/myServlet/img/1.mp4">视频1</a> <hr> <a href="/myServlet/DownloadServlet?filename=1.jpg">图片2</a> <a href="/myServlet/DownloadServlet?filename=1.mp4">视频2</a> </body> </html>
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.获取请求参数、文件名称 String filename = request.getParameter("filename"); //使用字节输入流加载文件进内存 //找到文件服务器路径 ServletContext servletContext = this.getServletContext(); String realPath = servletContext.getRealPath("/img/" + filename); //用字节流关联 FileInputStream fis = new FileInputStream(realPath); //设置response的响应头 //设置响应头类型content-type String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型 response.setHeader("content-type", mimeType); //设置响应头打开方式content-disposition response.setHeader("content-disposition", "attachment;filename" + filename); //将输入流的数据写出到输出流中 ServletOutputStream sos = response.getOutputStream(); byte[] buff = new byte[1024 * 8]; int len = 0; while ((len = fis.read(buff)) != -1) { sos.write(buff, 0, len); } fis.close(); }
-
-
中文文件名问题:
- 解决思路:
- 获取客户端浏览器使用的版本信息
- 根据不同的版本信息,设置filename的编码方式不同
//解决中文文件名问题 //获取user-agent请求头 String agent = request.getHeader("user-agent"); //使用工具类方法编码文件名 filename = DownloadUtils.getFileName(agent,filename);
- 解决思路:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)