# JavaWeb **Repository Path**: lyxbiu/java-web ## Basic Information - **Project Name**: JavaWeb - **Description**: 老杜JavaWeb入门到精通 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2024-12-30 - **Last Updated**: 2025-06-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 需要提前准备的技术 * JavaSE(Java语言的标准版,Java提供的最基本的类库) * Java环境搭建 * java的基础语法 * Java的面向对象 * 数组 * 常用类 * 异常 * 集合 * 多线程 * IO流 * 反射机制 * ...... - MySQL(数据库) - 最基本的要求是:能够编写增删改查等简单的SQL语句 * JDBC(Java语言连接数据库) * 这是一套Java语言连接数据库接口 * WEB前端 * HTML(网页) * CSS(层叠样式语言,修饰HTML) * javascript(一种脚本语言,运行在浏览器当中,让浏览器可以增删改,让网页产生更强的交互式效果) * WEB后端 * Servlet(Server Applet:服务端的java小程序) * JSP * AJAX * JQuery * MyBatis * Spring * SpringMVC * SpringBoot * SpringCloud * ...... # Servlet ## 关于系统架构 1. 系统架构包括什么形式 * C/S架构 * B/S架构 2. C/S架构 * Clinet/Server(客户端/服务器) * C/S架构的软件或者说系统有哪些? - QQ * C/S架构的特点:必须安装特定的客户端软件 * C/S架构的优点和缺点是什么? - 优点: - 速度快(软件中的数据大部分都是集成到客户端软件当中,很少的数据从服务端传送过来) - 体验好 - 界面酷炫 - 服务器压力小 - 安全(大量的数据集成在客户端软件当中,并且客户端有很多个,服务器只有一个,就算服务器受损,问题也不大,数据在客户端上有缓存) - ...... - 缺点: - 升级维护比较差劲。(升级维护比较麻烦,每一个客户端都需要升级,有一些软件不是那么安装) 3. B/S架构 - Browser/Server(浏览器/服务器) - https://www.baidu.com - https://www.jd.com - B/S结构的系统是一个特殊的C/S系统 - 实际上B/S结构的系统还是一个C/S系统,只不过这个C比较特殊,这个Client是一个固定不变的浏览器 - B/S结构系统的优缺点 - 优点: - 升级维护方便,成本低 - 不需要安装特定的客户端软件,用户操作方便 - 缺点: - 速度慢(不是因为带宽低,是因为所有的数据都在都在服务器上,用户发送的每一个请求都是需要服务端响应数据) - 体验差(浏览器只支持三个语言:HTML、CSS、JavaScript) - 不安全(所有数据都在服务器) - ...... 4. C/S和B/S结构的系统哪个好? - 不同结构的系统在不同的业务下有不同的适应场景 - 娱乐性软件:C/S - 公司内部使用的业务软件:B/S 5. 开发B/S结构的系统,实际上就是开发网站,其实就是开发一个Web - 开发一个Web系统需要掌握哪些技术? - Web前端 - HTML - CSS - JavaScript - Web后端 - Java(Java做Web开发我们称为JavaWEB开发,JavaWEB最核心的规范:Servlet【Server Applet:服务器的Java小程序】) - c - python - ...... 6. JavaEE是什么? - Java包括三大块 - JavaSE - Java标准版(一套类库,别人写好的一套类库,只不过这个类库是标准类库,走EE,或者走ME,这个SE一定是基础,先学) - JavaEE - Java企业版(一套类库,别人写好的一套类库,只不过这个类库可以帮助我们完成企业级项目的开发,专门为企业内部提供解决方案的一套(多套)类库) - 别人写好的,可以用来开发企业级项目 - 可以做WEB开发 - Java比较火爆的原因就是这个JavaEE方向 - JavaME - Java微型版(一套类库,只不过这个类库帮助我们进行电子卫星设备内核程序的开发) - 机顶盒内核程序、吸尘器内核程序、电冰箱内核程序...... - JavaEE实际上包括很多规范,13种规范,其中Servlet即是JavaEE规范之一。学Servlet其实还是学Java语言 ## B/S通信原理 - WEB系统的访问过程 - 第一步:打开浏览器 - 第二步:找到地址栏 - 第三步:输入一个网址 - 第四步:回车 - 第五步:在浏览器上会显示响应的结果 - 关于域名 - https://www.baidu.com - www.baidu.com是一个域名 - 在浏览器上输入域名,回车后,域名解释器会将域名解释出来一个具体的IP地址和端口号等 - IP地址是什么? - 计算机在网络当中的一个身份证,在同一个网络当中,IP地址是唯一的 - A计算机要想和计算机B通信,首先你需要知道计算机B的IP地址,有了IP地址才能建立连接 - 端口号是什么? - 一个端口代表一个软件(一个端口代表一个应用,一个应用仅代表一个服务) - 每一个软件启动之后都有一个端口号 - 在同一台计算机上,端口具有唯一性 - 一个WEB系统的通信原理? - 第一步:用户输入网址(URL) - 第二部:域名解析器进行域名解析:http://110.242.68.3:80//index.html - 第三步:浏览器软件在网络中搜索110.242.68.3这台主机,找到这台主机 - 第四步:定位110.242.68.3这台主机上的服务器软件,因为是80端口,可以很轻松的定位到80端口对应的服务器软件 - 第五步:80端口对应的服务器软件得知浏览器想要的资源名是:index.html - 第六步:服务器软件找到index.html文件,并将index.html文件的内容直接输出响应到浏览器上 - 第七步:浏览器接收到来自服务器的代码(HTML、CSS、JavaScript) - 第八步:浏览器渲染,执行HTML、CSS、JS代码、展示效果 - 什么是URL? - 统一资源定位符(http://www.baidu.com) - 什么是请求?什么是响应? - 请求和响应实际上说的是数据的流向不同 - 从Browser端发送数据到Server端,我们称为请求。英文名:request - 从Server端向浏览器Browser端发送数据,我们称为响应。英文名:response - B->S(请求request) - S->B(响应response) ## 关于WEB服务器软件 - WEB服务器软件有哪些? - Tomcat(WEB服务器) - jetty(WEB服务器) - JBOSS(应用服务器) - WebLogic(应用服务器) - WebSphere(应用服务器) - 应用服务器和WEB服务器的关系? - 应用服务器实现了JavaEE的所有规范(JavaEE有13个不同的规范) - WEB服务器只实现了JavaEE的Servlet+JSP两个核心 - JBOSS服务器内嵌了Tomcat服务器 - 下载Tomcat - apache官网:https://www.apache.org/ - tomcat官网:https://tomcat.apache.org/ - tocmat免费开源的轻量级WEB服务器 - tomcat还有一个名称: catalina( catalina是美国一个岛屿,据说作者是在这个小岛上开发了一个轻量级的WEB服务器、体积小、运行速度快、因此tomcat又称为catalina) - tomcat是Java语言写的 - tomcat要想运行,必须先有jre(Java运行环境) - 配置Java运行环境 - JAVA_HOME:jdk根目录 D:\Program Files\Java\jdk-16.0.1 - PATH:%JAVA_HOME%\bin - Tomcat的安装 - 绿色版本的安装非常简单,直接zip解压即可,解压就是安装 - 启动Tomcat - bin目录下有一个文件:startup.bat,通过它可以启动Tomcat - xxx.bat是个什么文件?bat文件是windows操作系统专用的。bat文件是批处理文件,这种文件可以编写大量的windows的dos命令,然后执行bat文件就相当于批量的执行dos命令 - startup.sh,这个文件在windows当中无法执行,在Linux环境当中可以使用,在Linux环境下可以执行的是shell命令,大量的shell命令编写在shell文件当中,然后执行shell命令就可以批量的执行shell命令 - tomcat服务器提供了bat和sh文件,说明了tomcat服务器的通用性 - 分析startup.bat文件得出,执行这个命令,实际上最后是执行:catalina.bat文件 - catalina文件中有这杨一行配置:MAINCLASS=org.apache.catalina.startup.Bootstrap(这个类就是main方法所在的类) - tomcat服务器就是用Java语言写的,既然是java语言写的,那么启动tomcat就是指定main方法 - 我们尝试启动tomcat,dos命令窗口中输入startup.bat来启动tomcat服务器 - 启动Tocmat服务器只配置path对应的bin目录是不行的(启动bin中的.bat)。有两个环境变量需要配置: - JAVA_HOME:jdk的根目录 - CATALINA_HOME:Tomcat的根目录 - 关于Tomcat的目录 - bin:Tomcat服务器的命令文件存在的目录,比如:启动Tomcat、关闭Tomcat等 - conf:Tomcat配置文件存放的的目录。(server.xml文件或者可以配置端口号,默认端口号是8080) - lib:Tomcat核心程序目录,因为Tomcat服务器是Java写的,这里的jar包里面都是class文件 - temp:Tomcat服务器的临时目录,存放临时文件 - webapps:存放大量的webapp(web application:web应用) - work:存放JSP文件翻译之后的Java文件以及翻译之后的class文件 - 配置Tomcat环境变量需要哪些变量 - JAVA_HOME:jdk的根目录 - CATALINA_HOME::Tomcat的根目录 - PATH:%CATALINA_HOME%\bin;%JAVA_HOME%\bin - 启动Tomcat:startup.bat - 关闭Tomcat:shutdown.bat(shutdown文件可以重命名,shutdown和window关机命令同名) - 怎么测试Tomcat服务器有没有启动成功? - 打开浏览器,在浏览器上输入URL即可 - http://ip地址:端口号 - ip地址是什么 - 本机的ip地址是:127.0.0.1,或者是localhost - 端口号 - Tomcat默认端口号:8080 ## 实现一个最基本的web应用 - 第一步:找到CATALINA_HOME\webapps目录 - 因为所有的webapp要放在webapps目录下。(没有为什么,这是Tomcat服务器要求的。如果不放在这里,Tomcat服务器找不到你的应用) - 第二步:在CATALINA_HOME\webapps目录下新建一个子目录,起名:xxx - 这个目录xxx就是你这个webapp的名字 - 第三步:在xxx目录下新建资源文件,例如:index.html - 编写index.html文件的内容 - 第四步:启动tomcat服务器 - 第五步:打开浏览器,在浏览器地址栏输入URL - URL:http://localhost:8080/xxx/index.html - 我们在浏览器输入地址和我们点超链接效果一致吗?: - 点击超链接相当于直接在浏览器地址栏数输入URL,既然效果一样,我们可以直接使用超链接 ```html login d page ``` ## B/S结构系统的角色和协议 ![1661784654174](C:\Users\18055243628\AppData\Roaming\Typora\typora-user-images\1661784654174.png) - 有哪些角色(在整个BS结构的系统当中,有哪些人参与进去了) - 浏览器软件的开发团队(谷歌浏览器、火狐浏览器等) - WEB Server的开发团队(Tocmat、Jertty,WebLogic等) - DB Server的开发团队(Oracle、MySQL等) - webapp的开发团队(WEB应用是我们JavaWEB程序员开发的) - 角色和角色之间需要遵守哪些规范,哪些协议 - webapp的开发团队 和 WEB Server的开发团队 之间有一套规范:JavaEE规范之一Servlet规范 - Servlet规范的作用是什么? - WEB Server 和 webapp解耦合 - Browser 和 Web Server 之间有一套传输协议:HTTP协议(超文本传输协议) - webapp开发团队 和 DB Server的开发团队之间有一套规范:JDBC规范 ![1661785338816](C:\Users\18055243628\AppData\Roaming\Typora\typora-user-images\1661785338816.png) ## 模拟Servlet本质 - 充当SUN公司,指定Servlet规范 - javax.servlet.Servlet接口 - 充当Tomcat服务器的开发者 - Tomcat - 充当Webapp的开发者 - BankServlet implements Servlet - UserListServlet implements Servlet - UserLoginServlet implements Servlet - 通过我们分析: - 对于我们Javaweb程序员来说,我们只要做两件事: - 编写一个类实现Servlet接口 - 将编写的类写到配置文件当中,在配置文件中:指定请求路径 和 类名的关系 - 注意 - 这个配置文件的文件名和文件路径都是SUN公司制定好的,不能随便起和随便放 - 严格意义上来说Servlet其实并不是一个简单的接口 - Servlet规范中规定 - 一个合格的webapp应该是一个怎样的目录结构 - 一个合格的webapp应该有一个怎样的配置文件 - 一个合格的webapp配置文件路径应该放在哪里 - 一个合格的webapp中java程序放在哪里 - 这些都是Servlet规范中规定好的 - Tomcat服务器要遵循Servlet规范,Javaweb程序员也要遵循这个Servlet规范,这样Tomcat服务器和webapp才能解耦合。遵循Servlet规范的webapp,这个webapp就可以放在不同的WEB服务器中运行 ## 开发一个带有Servlet的webapp - 开发步骤是怎么样的 - 第一步:在webapps下新建一个目录,起名crm(这个crm就是webapp的名字) - 注意:crm就是这个webapp的根 - 第二步:在webapp的根下新建一个目录:WEB-INF - 注意:这个目录的名字是Servlet规范中规定的。必须全部大写 - 第三步:在WEB-INF目录下新建一个目录:classes - 注意:这个目录的名字是Servlet规范中规定的,必须全部小写。另外这个目录下一定存放的是java程序编译之后的classes文件(这个存放的是字节码文件) - 第四步:在WEB-INF目录下新建一个目录:lib - 注意:这个目录不是必须的。但是如果一个webapp需要第三方jar包的话,这个jar包要放在lib目录下,这个目录的名字是Servlet规范中规定的,必须全部小写。这也是Servlet规范的 - 第五步:在WEB-INF目录下新建一个文件:web.xml - 注意:这个文件是必须的,这个文件是Servlet规范中规定的。一个合法webapp,web.xml文件是必须的,这个web.xml文件是一个配置文件,这个配置文件中描述了请求路径和Servlet类之间的对照关系 - 这个文件最好从其他webapp文件中拷贝,最好别手写,没必要 - ```xml ``` - 第六步:编写一个java小程序,这个Java小程序不能随便写,这个Java小程序必须实现Servlet接口 - 这个Servlet接口不在JDK当中 - Servlet接口是Oracle提供的(最原始是SUN公司提供的) - Servlet接口是JavaEE的规范中的一员 - Tomcat服务器实现了Servlet规范,所以Tomcat服务器也需要Servlet接口。Tomcat服务器当中有这个接口。Tomcat服务器的CATALINA_HOME\lib下有一个servlet-api.jar,解压之后会发现里面有一个Servlet.class文件 - 重点:从javaEE9开始Servlet接口全名就变了:jakarta.servlet.Servlet - 注意:编写java小程序的时候,Java源代码放在哪里都随意,只需将编译之后的class文件放入classes目录下即可 - 第七步:编写我们的HelloServlet - 重点:你怎么让你写的HelloWord编译通过呢?配置环境变量CLASSPATH - CLASSPATH:“,;D:\apache-tomcat-10.0.23\apache-tomcat-10.0.23\lib\servlet-api.jar” - 思考一个问题,以上配置CLASSPATH和Tomcat服务器运行有没有关系? - 没有任何关系,以上配置这个变量只是为了让你的HelloWord能够正常编译生成class文件 - 第八步:将编译生成的HelloServlet.class文件拷贝到WEB-INF\classes目录下 - 第九步:在web.xml文件中编写配置信息,让“请求路径”和“Servlet类名”关联在一起 - 这一步用专业术语描述:在web.xml文件中注册Servlet类 - ```xml fsdsjsjsjsj com.bjpowernode.servlet.HelloServlet fsdsjsjsjsj /fdsa/fd/saf/d/sa/fd/sa/fd ``` - 第十步:启动Tomcat服务器 - 第十一步:打开浏览器,在浏览器地址栏输入url,这个url必须是: - http://localhost:8888/crm/fdsa/fd/saf/d/sa/fd/sa/fd - 重点:浏览器上的请求路径不能随便写,必须和web.xml文件中的url-pattern一致 - 浏览器上的请求路径和web.xml文件中的url-pattern的唯一区别就是:浏览器上的请求路径带项目名:/crm - 浏览器上编写的路径太复杂,可以使用超链接(**非常重要:html文件只能放在WEB-INF目录外面**) - 以后我们不需要编写main方法,tomcat服务器负责调用main方法,Tomcat服务器启动的时候执行的就是main方法。我们Javaweb程序员只需要编写servlet接口的实现类,然后将其注册到web.xml文件中即可 - 总结一下一个合法的webapp目录结构应该是怎么样的? ``` webapp-root |------WEB-INF |------classes |------lib |------web.xml |------html |------css |------js |------image ... ``` - 浏览器发送请求,最终服务器调用Servlet中的方法,这是怎样的过程? - 用户输入url,或者点击连接:http://localhost:8888/crm/fdsa/fd/saf/d/sa/fd/sa/fd - 然后Tomcat服务器接收到请求,截取路径:/crm/fdsa/fd/saf/d/sa/fd/sa/fd - Tomcat服务器找到crm项目 - Tomcat服务器在web.xml文件中查找/fdsa/fd/saf/d/sa/fd/sa/fd对应的servlet是:com.bjpowernode.servlet.HelloServlet - Tomcat服务器通过反射机制,创建:com.bjpowernode.servlet.HelloServlet的对象 - Tomcat服务器调用com.bjpowernode.servlet.HelloServlet对象的service方法 ## 关于JavaEE版本 - JavaEE被Oracle捐献了,Oracle将JavaEE规范捐献给了Apache - Apache将JavaEE换名了,以后叫jakartaEE - JavaEE8版本升级之后的JavaEE9,不再是JavaEE9,叫做jakarta9 - JavaEE8的时候对应的Servlet类名是:javax.servlet.Servlet - jakartaEE9对应的Servlet类名是:jakarta.servlet.Servlet(包名换了) - 如果你之前的项目还是使用javax.servlet.Servlet,那么你的项目无法直接部署到Tomcat10+版本上。你只能部署到Tomcat9-版本上,在Tomcat9以及Tomcat9之前的版本中还是能够识别javax.servlet这个包 ## 解决Tomcat服务器启动时DOS命令窗口乱码问题(控制台乱码) - 将CATALINA_HOME/conf/logging.properties文件中内容修改如下: - java.util.logging.ConsoleHandler.encoding = GBK ## 向浏览器中响应一段HTML代码 ```java public void service(ServletRequest request,ServletResponse response)throws ServletException,IOException{ response.setContentType("text/html"); PrintWriter out=response.getWriter(); out.print("

Servlet

"); } ``` ## 在servlet中连接数据库 - servlet是用Java代码写的,所以在servlet中完全可以直接编写JDBC代码连接数据库 - 在一个webapp中去连接数据库,需要将jar包放在WEB-INF/lib目录下 ## 使用集成开发环境开发Servlet程序 - 集成开发工具很多,目前使用最多的是: - Intellij IDEA(这个最多,IDEA在提示功能方面强于Eclipse,JetBrain公司开发的,收费) - Eclipse(这个少一些),Eclipse是IBM公司开发的,Eclipse寓意”日食“,表示将太阳吃掉,太阳是SUN公司。IBM团队开发Eclipse寓意是吞并SUN公司,但是2009年SUN公司被Oracle公司并购了,IBM没有成功并购SUN公司。 - 使用IDEA集成开发Servlet - 第一步:New Project(可以先创建一个Empty Project【空工程】,然后在空工程下新建Module【模块】,这个不是必须,也可以直接建立非空的Project) - 第二步:新建模块【File->new->Module】 - 这里新建的是一个普通的JavaSE模块(这里不要新建Java Enterprise模块) - 这个Module自动会放在javaWeb的Project下面 - 这个Module起名:servlet01 - 第三步:让Module变成JavaEE的模块 - 在Module右键:Add framework Support...(添加支持框架) - 弹出的窗口中选择:Web Application (选择的是webapp的支持) - 选择了webapp支持后,IDEA会自动给你生成一个符合Servlet规范的webapp目录结构 - **重点:需要注意的是,在IDEA工具根据Web Application模板生成的目录中有一个web目录,这个目录就代表webapp的根** - 第四步:(非必须):根据Web Application生成的资源中有index.jsp,这里我选择删除这个文件 - 第五步:编写Servlet(StudentServlet) - Class StudentServlet implements Servlet - 这个时候发现Servlet.class文件没有,怎么办?将CATALINA_HOME/lib/servlet-api.jar和jsp-api.jar添加到classpath当中(这里的classpath说的是IDEA的classpath) - File->Project Structrue->Modules->Dependecies->+加号->Add jars... - 实现jakarta.servlet.Servlet中五个方法 - 第六步:在Servlet当中的service方法中编写业务逻辑代码(这里我们连接数据库) - 第七步:在WEB-INF目录下新建一个子目录:lib(这个目录名不可以随便写,必须全部小写),并且将数据库驱动jar包放到lib目录下 - 第八步:在web.xml文件中完成StudentServlet类的注册(请求路径和servlet对应起来) ```xml studentServlet com.bjpowernode.javaWeb.servlet.StudentServlet studentServlet /servlet/student ``` - 第九步:给一个html页面,在HTML页面中编写一个超链接,用户点击这个超链接,发送请求,Tomcat执行后台的StudentServlet - student.html - 这个文件不能放到WEB-INF目录里面,只能放到WEB-INF外面 - student.html文件内容 ```html student page student list ``` - 第十步:让IDEA工具关联Tomcat服务器,关联的过程中将webapp部署到Tomcat服务器中 - IDEA工具右上角,绿色小锤子右边有一个:Add Configuration - 左上角加号,点击Tomcat Server->local - 在弹出的界面设置服务器Server的参数(基本上不用) - 在当前窗口中有一个Deployment(点击这个用来部署webapp),继续点击加号,部署即可 - 修改Application Context为:/xmm - 第十一步:启动Tomcat服务器 - 右上角有绿色小虫子,点击绿色小虫子可以采用debug模式启动Tomcat服务器 - 我们开发者建议使用debug模式启动Tomcat - 第十二步:打开浏览器,在浏览器中输入:http://localhost:8888/xmm/servlet/student ## Servlet对象的生命周期 - 什么是Servlet对象生命周期 - Servlet对象什么时候被创建 - Servlet对象什么时候被销毁 - Servlet对象创建了几个 - Servlet对象的生命周期表示:一个Servlet对象从出生到最后死亡,整个过程是怎么样的 - Servlet对象由谁维护 - Servlet对象的创建、销毁、调用,javaweb程序员无权干预 - Servlet对象的生命周期由Tomcat服务器(Web Server)全权负责 - Tomcat服务器我们通常又称为WEB 容器(WEB Container) - WEB容器来管理Servlet的死活 - 思考:我们自己new的Servlet对象接受WEB容器的管理吗? - 我们自己new的Servlet对象不受WEB容器管理 - WEB容器创建的Servlet对象,这些Servlet对象都会被放在一个集合(HashMap),只有放在这个HashMap集合中的Servlet才能够被WEB容器管理,自己new的Servlet对象不会被WEB容器管理(自己new的Servlet对象不在容器当中) - WEB容器的底层应该有一个HashMap集合,在这个集合中存储了Servlet对象和请求路径之间的关系 ![1661853058746](C:\Users\18055243628\AppData\Roaming\Typora\typora-user-images\1661853058746.png) - 研究:Tomcat服务器在启动的时候,Servlet对象有没有被创建?(默认情况下) - 在Servlet中提供一个无参数的构造方法,启动服务器的时候看看构造方法是否执行 - 经过测试,默认情况下,服务器在启动的时候Servlet对象不会被实例化 - 这个设计很合理,用户发送请求之前,如果提前创建出所有的Servlet对象,必然是耗费内存的,并且创建出来的Servlet如果一直没有用户访问,显然这个Servlet是一个废物,没有必要创建 - 怎么让服务器启动的时候创建Servlet? - 在servlet标签中添加子标签,在子标签中填写整数,越小的整数优先级越高 ```xml aservlet com.bjpowernode.javaWeb.servlet.AServlet 0 aservlet /a ``` - Servlet生命周期 - 默认情况下服务器启动的时候AServlet并没有被实例化 - 用户发送一次请求的时候,控制台输出以下信息: ```txt AAA无参构造方法执行了 AServlet's init method is execute AServlet's service method is execute ``` - 根据以上输出内容得出结论: - 用户发送第一次请求的时候Servlet对象被实例化(AServlet的构造方法执行了,并且执行的是无参构造方法) - AServlet对象被创建出来之后,Tomcat服务器马上调用AServlet的init方法(init方法在执行的时候,AServlet对象已经存在,已经被创建出来) - 用户发送第一次请求的时候,init方法执行之后,Tomcat服务器马上调用了AServlet对象的service方法 - 用户继续发送第二次请求,控制台给出以下信息: ``` AServlet's service method is execute ``` - 根据以上输出结果得知,用户发送第二次,或者第三次,或者第四次请求的时候,Servclet对象并没有新建,还是使用之前创建好的Servlet对象,直接调用该Servlet对象的service方法,这说明: - 第一:Servlet对象是单例的(单实例的,但是要注意的:Servlet对象是单实例的,但是Servlet类并不符合单例模式。我们称之为假单例。之所以单例是因为Servlet对象的创建我们程序员管不着,这个对象只能由Tomcat服务器说的算,Tomcat只创建一个,所以导致了单例,但是是属于假单例。真单例模式,构造方法是私有化的) - 第二:无参构造方法、init方法只是在第一次用户发送请求的时候执行,也就是无参构造方法只执行一次,init方法也只被Tomcat服务器调用一次 - 第三:只要用户发送一次请求:service方法必然会被Tomcat服务器调用一次,发送100次,service方法会被调用100次 - 关闭服务器的时候,控制台输出以下信息 ``` AServlet's destroy method is execute ``` - 通过以上输出内容,可以得出以下结论: - Servlet的destroy方法只被Tomcat服务器调用一次 - destroy方法是在什么时候被调用? - 在服务器关闭的时候 - 因为服务器在关闭的时候要销毁AServlet对象的内存 - 服务器在销毁AServlet对象内存之前,Tomcat服务器会自动调用AServlet对象的destroy方法 - destroy方法调用的时候,对象是否被销毁了? - destroy方法执行的时候AServlet对象还在,没有被销毁。destroy方法执行结束之后,AServlet对象的内存才会被Tomcat释放 - Servlet对象更像一个人的出书 - Servlet无参构造方法的执行,标志着一个人的出生 - Servlet对象的init方法的执行,标志着你正在接受教育 - Servlet对象的service方法的执行,标志着你已经开始工作,已经开始为人类提供服务了 - Servlet对象的destroy方法的执行,标志着临终 - 关于Servlet类中方法的调用次数 - 构造方法只执行一次 - init方法只执行一次 - service方法:用户调用一次则执行一次 - destroy方法只执行一次 - 当我们Servlet类中编写一个有参数构造方法,如果没有手动编写无参数构造方法会出现什么问题? - 报错:500错误 - 注意:500错误是HTTP协议的错误状态吗 - 如果没有无参数构造方法,无法实例化Servlet对象,会报错 - 所以,一定要注意:在Servlet开发当中,不建议程序员定义构造方法,因为定义不当,一不小心会导致无法实例化Servlet对象 - Servlet对象无参构造方法是在对象第一次创建的时候执行,并且只执行一次。init方法也是在对象第一次创建的时候执行,并且只执行一次。那么无参数构造方法可以替代init方法吗? - 不能 - Servlet规范中要求:作为javaweb程序员,编写servlet类的时候,不建议手动编写构造方法,因为编写构造方法,很容易让无参数构造方法消失,这个操作可能导致Servlet对象无法实例化,所以init方法是有存在的必要的 - init、service、destroy方法中使用做多是哪一个? - 使用做多的是service方法,所以service方法一定要实现的,因为service方法是处理用户请求的核心方法 - 什么时候使用init方法 - init方法很少有 - 通常在init方法中做初始化操作,并且这个初始化只需要执行一次。例如:初始化数据库连接池、初始化线程池... - 什么时候使用destroy方法 - destroy方法也很少使用 - 通常在destroy方法当中,进行资源关闭。 - Servlet中的方法 - ```java void destroy(); ServletConfig getServletConfig(); String getServletInfo(); void init(ServletConfig config); void service(ServletRequest req, ServletResponse res); ``` ## GenericServlet - 我们编写一个Servlet类直接实现Servlet接口有什么缺点? - 我们只需要service方法,其他方法大部分情况下是不需要使用的,代码很丑陋 - 适配器设计模式Adapter - 编写一个GenericServlet类,这个类是抽象类,其中有一个抽象方法service - GenericServlet实现Servlet接口 - GenericServlet是一个适配器 - 以后编写的所有Servlet类继承GenericServlet,重写service方法 - GenericServlet类是否需要改造一下?更利于子类程序的编写? - init方法中的ServletConfig对象是谁创建?谁传递过来的? - 都是Tomcat服务器 - Tomcat服务器先创建了ServletConfig对象,然后调用init方法,将ServletConfig对象传给init方法 ## ServletConfig - ServletConfig是什么 - jakarta.servlet.ServletConfig - 显然ServletConfig是Servlet规范中的一员 - ServletConfig是一个接口 (jakarta.servlet.Servlet是一个接口) - 谁实现了这个接口 - public class org.apache.catalina.core.StandardWrapperFacade implements ServletConfig(){} - 结论:Tomcat服务器实现了ServletConfig接口 - 思考:如果把Tomcat服务器换成jetty服务器,输出ServletConfig对象时,还是这个结果吗? - 不一定,包名类名可能和Tomcat不一样,但是实现了ServletConfig这个规范 - 一个Servlet对象中有一个ServletConfig对象。Servlet对象和ServletConfig对象一一对应 - ServletConfig对象是谁创建的?什么时候创建的? - Tomcat服务器创建的 - 在创建Servlet对象创建的时候,同时创建了ServletConfig对象 - ServletConfig有什么用? - Config是哪个单词缩写? - Configuration - ServletConfig对象被翻译为:Servlet对象的配置信息对象 - 一个Servlet对象就有一个ServletConfig对象 - 两个Servlet对象就有两个ServletConfig对象 - ServletConfig对象中包装了什么信息? - ```xml config2 com.bjpowernode.javaWeb.servlet.ConfigTextServlet2 ``` - ServletConfig对象中包装的信息是: - web.xml文件中****标签配置信息 - Tomcat服务器解析web.xml,将web.xml文件中****标签中的配置信息自动包装到ServletConfig对象中 - ServletConfig中的方法 - Enumeration getInitParameterNames() 获取所有初始化参数的name - String getInitParameter(String name) 通过初始化信息的name获得value - ```xml config com.bjpowernode.javaWeb.servlet.ConfigTextServlet diriver com.mysql.cj.jdbc.Driver url jdbc:mysql://localhost:3306/library user root password admin ``` - 以上****标签中的****是初始化参数,这个参数信息会自动被Tomcat封装到ServletConfig对象中 - 事实上获取一个ServletConfig对象的初始化参数,可以不用获取ServletConfig对象,直接通过this也可以,因为GenericServlet也实现了ServletConfig接口 - String getServletName() 获取Servlet的name - ServletContext ServletContext() 获得ServletContext对象 - 以上四个方法,在自己编写的Servlet类中也可以通过this去调用(这个Servlet继承了GenericServlet) - 总结 - ServletConfig对象是Servlet配置信息对象 - ServletConfig对象中封装了标签的配置信息 - 一个Servlet对应一个ServletConfig对象 - ServletConfig是Tomcat服务器创建的,并且Servlet对象也是Tomcat服务器创建的,并且默认情况下,他们都在用户发送第一次请求的时候创建 - Tomcat服务器调用Servlet对象中的init方法时需要传一个ServletConfig对象给init方法 - ServletConfig接口的实现类是Tomcat服务器实现的(Tomcat服务器就是WEB服务器) - ServletConfig接口有哪些方法 - ```java String getServletName(); ServletContext getServletContext(); String getInitParameter(String name); Enumeration getInitParameterNames(); ``` - 以上四种方法在Servlet中通过this也可以获得,因为GenericServlet实现了ServletConfig接口 ## ServletContext - ServletContext是什么 - ServletContext是接口,是Servlet规范中的一员 - ServletContext是谁实现的? - Tomcat服务器实现的 - public class org.apache.catalina.core.ApplicationContextFacade implements ServletContext{} - ServletContext对象是谁创建的?什么时候创建的? - ServletContext对象在WEB服务器启动的时候创建 - ServletContext对象是WEB服务器创建的 - 对于一个webapp来说,ServletContext对象只有一个 - ServletContext对象在服务器关闭时销毁 - ServletContext怎么理解? - ServletContext什么意思? - Servlet对象的环境对象(Servlet对象的上下文对象) - ServletContext对象其实对应的就是整个web.xml文件 - 50个学生,每个学生都是一个Servlet对象,这50个学生都在同一个教室,那么这个教室就相当于ServletContext对象。放在ServletContext对象中的数据,所有的servlet可以共享,比如教室中的空调,所有学生都可以操作 - Tomcat是一个容器,一个容器可以放多个webapp,一个webapp对应一个ServletContext对象 - ServletContext中常用的方法? - ```java String getInitParameter(String name);//通过初始化参数的name获得value Enumeration getInitParameterNames();//通过初始化参数所有的name ``` ```xml pageSize 10 startIndex 0 ``` - ```java //获取应用的根(非常重要),这个方法可以动态获得应用的根路径 //在java源码中,不要将应用的根路径写死,因为你永远不知道这个应用在最终部署的时候,起一个什么名字 public String getContextPath(); ``` - ```java //获取文件的绝对路径(真实路径) //不加/也可以 默认是从web根下开始找 public String getRealPath(String path); ``` - ```java //通过ServletContext对象也可以记日志 public void log(String msg); public void log(String message, Throwable throwable); //这些日记记录到哪里?CATALINA_HOME/logs目录下 //localhost.2022-08-31.log //Tomcat服务器下都有哪些日志文件 //catalina.2022-08-31.log 服务器端的Java程序运行的控制台信息 //localhost.2022-08-31.log ServletContext对象的log方法记录的日志信息存储到这个文件中 //localhost_access_log.2022-08-31.txt 访问日志 ``` - ```java //ServletContext还有一个名字(应用域) //如果所有的用户共享一份数据,并且这个数据很少的被修改,并且这个数据量很少,可以将这些数据放到ServletContext这个运用域中 //为什么是所有用户共享?因为ServletContext对象只有一个,只有共享的数据放进去才有意义 //为什么数据量要少?因为数据量比较大的话,太占用推内存,并且这个对象的生命周期比较长,服务器关闭的时候,这个对象才会被销毁。大数据量会影响服务器的性能 //为什么数据几乎不修改?所有用户共享的数据,如果涉及到修改操作,必然会存在线程并发所带来的安全问题。所以放在ServletContext对象中的数据一般都是只读的 //数据量小、所有用户共享、又不修改,这样的数据放到ServletContext这个应用域中,会大大提升性能。因为作用域相当于一个缓存,放到缓存里的数据,下次再用的时候,不需要从数据库中再次获取,大大提升性能 //存(怎么向ServletContext应用域中存数据) public void setAttribute(String name,Object value); //取(怎么从ServletContext应用域中存数据) public Object getAttribute(String name); //删(怎么删除ServletContext应用域中数据) public void removeAttribute(String name); ``` - 注意:以后我们编写Servlet类不会直接继承GenericServlet类的,因为我们是B/S结构的系统,这种系统我们是基于HTTP超文本传输协议的,在Servlet规范中,提供了一个类叫做HttpServlet,它是专门为HTTP协议准备的一个Servlet类。我们编写的Servlet类要继承HttpServlet。(HttpServlet是HTTP协议专用的)使用HttpServlet处理HTTP协议更便捷。但你需要知道它的继承结构 - ```java jakarta.sevlet.Servlet(接口)【爷爷】 jakarta.servlet.GenricServlet(抽象类)【儿子】 jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)【孙子】 我们以后编写的Servlet要继承HttpServlet类 ``` - 我们学过哪些缓存机制 - 字符串常量池 - “abc”先在字符串常量池中查找,如果有,直接拿来用,没有就新建,然后再放入字符串常量池 - 推内存中整数型常量池 - [-128-127]一共256个Integer类型的引用,放在整数型常量池,没有超出这个范围,直接从常量池中取 - 连接池(Connection Cache) - 这里所说的连接池中的连接是Java连接数据库对象:Connection对象 - JVM是一个进程,MySQL也是一个进程,进程与进程之间打开通道是很费劲的,也很费资源的。怎么办?可以先创建N个Connection连接对象,将连接对象放到一个集合中,我们把这个放Connection对象的集合称为连接池。用户每一次连接数据库不需要新建连接对象,省去了新建的环节,直接从连接池中获取连接,大大提升了访问效率 - 连接池 - 最小连接数 - 最大连接数 - 连接池可以提高访问效率,当然也可以保证数据的安全性 - 线程池 - Tomcat服务器本身就支持多线程 - Tomcat服务器是在用户发送一次请求,就新建一个Thread线程对象吗? - 当然不是,实际上是Tomcat服务器启动的时候,就会创建好N多个线程Thread对象,然后将线程对象放到集合中,称为线程池。用户发送请求过来之后,需要一个对应的线程对象来处理这个请求,效率比较高 - 所以的WEB服务器,或者是应用服务器,都是支持多线程的,都有线程机制 - redis - NoSQL数据库,非关系型数据库,缓存数据库 - ServletContext应用域中储存数据 ## HTTP - 什么是协议 - 协议实际上是某些人或某些组织制定好的一套规范。大家按照这个规范来,这样可以做到沟通无障碍 - 什么是HTTP协议 - HTTP协议:是W3C制定的一种超文本传输协议(通信协议) - W3C - 万维网联盟组织 - 负责制定标准的:HTTP HTML4.0 HTML5 XML DOM等规范都是W3C制定的 - 万维网之父:蒂姆-伯纳斯-李 - 什么是超文本 - 超文本说的是:不是普通文本,比如流媒体:声音、视频、图片等 - HTTP协议支持:不但可以传送普通文本,同样支持声音、视频、图片等流媒体信息 - 这种协议游走在B和S之间,B向S发送数据要遵循HTTP协议,S向B发送输数据同样是需要遵循HTTP协议,这样B和S才能解耦合 - HTTP协议包括: - 请求协议 - 浏览器向WEB服务器发送数据时需要遵循一套标准 - 响应协议 - WEB服务器向浏览器发送数据时需要遵循一套标准 - HTTP请求协议 - HTTP请求协议包括:4部分 - 请求行 - 请求头 - 空白行 - 请求体 - HTTP请求协议报文(Get): - ```txt GET /servlet05/getServlet?username=123&password=123 HTTP/1.1 请求行 Host: localhost:8888 请求头 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.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 Referer: http://localhost:8888/servlet05/index.html Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 Pragma: no-cache Cache-Control: no-cache 空白行 请求体 ``` - HTTP请求协议报文(Post): - ``` POST /servlet05/postServlet HTTP/1.1 请求行 Host: localhost:8888 请求头 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.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 Content-Type: application/x-www-form-urlencoded Content-Length: 25 Origin: http://localhost:8888 Connection: keep-alive Referer: http://localhost:8888/servlet05/index.html Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 Pragma: no-cache Cache-Control: no-cache 空白行 username=123&password=123 请求体 ``` - 请求行 - 包括三部分 - 第一部分:请求方式 - get(常用的) - post(常用的) - delete - put - head - options - trace - 第二部分:URI - URI?:统一资源标识符。代表网络某个资源的名字,但是通过URI是无法定位资源的 - URL?:统一资源定位符。代表网络中某个资源,通过URL可以定位到该资源 - URI和URL有什么关系? - URL包括URI。 - http://localhost:8888/servlet05/index.html URL - /servlet05/index.html URI - 第三部分:HTTP协议版本号 - 请求头 - 请求的主机 - 主机的端口 - 平台信息 - cookie信息 - ...... - 空白行 - 用来区分 请求头 和 请求体 - 请求体 - 向服务器发送的具体数据 - HTTP响应协议 - HTTP响应协议包括:4部分 - 状态行 - 响应头 - 空白行 - 响应体 - HTTP响应协议报文: - ``` HTTP/1.1 200 ok 状态行 Content-Type:text/html;charset=UTF-8 响应头 Content-Length:157 Date:Thu, 01 Sep 2022 04:52:30 GMT Keep-Alive:timeout=20 Connection:keep-alive 空白行 响应体 get Servlet

get请求

``` - 状态行 - 三部分组成 - 第一部分:协议版本号(HTTP/1.1) - 第二部分:状态码(HTTP协议规定的响应状态号,不同的响应结果对应不同的号码) - 200 表示响应成功,正常结束 - 400 表示访问的资源不存在,通常是因为要么是你路径写错了,要么时路径写对了但是的服务器对应的资源没有启动成功。总之404代表前端错误 - 405 表示前端发送的请求方式与后端请求的处理方式不一致 - 比如:前端发送POST请求,后端处理方式时按照GET方式进行处理 - 500 表示服务器端的程序出现了异常,一般会认为是服务端的错误导致 - 以4开头的,一般是浏览器端的错误导致的 - 以5开头的,一般是服务器端的错误导致的 - 第三部分:状态的描述信息 - ok:表示正常成功结束 - not found:表示资源找不到 - 响应头: - 响应的内容类型 - 响应的内容长度 - 响应的时间 - ...... - 空白行: - 用来分割 响应头 和响应体 - 响应体: - 响应体就是响应的内容,这些内容是一个长的字符串,这个字符串被浏览器渲染,解释并执行,最终展示出效果 - 怎么查看协议内容? - 使用浏览器:F12,然后找到network,通过这个面板可以查看协议的内容 - 怎么向服务器发送Get请求?怎么向服务器发送Post请求? - 到目前为止,只有一种情况可以发送POST请求:使用form表单,并且form表单中的method属性为:method="post" - 其他所以情况一律属于GET请求 - 在浏览器地址栏直接输入URL - 在浏览器上直接点击超链接 - 使用form表单,没有写method属性,默认是get - 或者使用form表单,form表单中method属性是:method="get" - ...... - GET请求和POST请求有什么区别? - get请求发送数据的时候,数据会挂在URL的后面,并且URL的后面加了一个"?","?"后面是数据。这样会导致发送的数据会回显在浏览器地址栏上(get请求在请求行上发送数据) - servlet05/getServlet?username=123&password=123 - post请求发送数据的时候,在请求体当中发送,不会回显到浏览器地址栏上(post请求在请求体当中发送数据) - /servlet05/getServlet - get请求只能发送普通的字符串,并且发送的字符串长度有限制。不同的浏览器限制不同 - get请求无法发送大数据量 - post请求可以发送任何类型的数据,包括普通字符串、流媒体信息:视频、声音、图片 - post请求可以发送大数据量,理论上没有长度限制 - get请求在W3C中是这样说的:get请求比较适合从服务器端获取数据 - post请求在W3C中是这样说的:post请求比较适合向服务器端传送数据 - get请求是安全的,为什么?因为get请求只是从服务器上获取数据 - post请求是危险的,为什么?因为post请是向服务器提交数据,如果数据通过后门进入服务器中,服务器是很危险的,另外post请求是为了提交数据,所以一般情况下拦截请求的时候,大部分会拦截(监听)post请求 - get请求支持缓存,post请求不支持缓存 - 任何一个get请求最终的“响应结果”都会被浏览器缓存起来。 - 一个get请求对应一个资源 - 事实上,你只要发送了get请求,浏览器第一件事先从本地浏览器缓存中找,找不到的时候才会去服务器上找,这种缓存机制的目的是为了提高用户的体验 - post请求之后,服务器“响应结果”不会被浏览器缓存起来,因为这个缓存没有意义 - 如果我们有一个需求:不希望get请求走缓存,每一次get请求都从服务器上找资源 - 只要每一次get请求路径不同即可 - 怎么解决 - 使用时间戳 lt=系统毫秒数 - http://localhost:8888/servlet05/getServlet?username=123&password=123<=21567 - http://localhost:8888/servlet05/getServlet?username=123&password=123<=系统毫秒数 - GET请求和POST请求如何选择? - 如果想从服务器获取资源,建议使用get请求 - 如果是为了向服务器提交资源,建议使用post请求 - 不管你是在get请求还是在post请求,发送的数据格式都是相同的,只是位置不同 - name=value&name=value&name=value - name是什么? - 以form表单为例:form表单中input标签的name - value是什么? - 以form表单为例:form表单中input标签的value ## 模板方法设计模式 - 什么是设计模式? - 解决某个问题的固定的解决方案 - 有哪些设计模式? - GoF设计模式 - 通常我们所说的23种设计模式(Gang of Four:4人组提出的设计模式) - 单例模式 - 工厂模式 - 代理模式 - 门面模式 - ...... - JavaEE设计模式 - DAO - DTO - VO - PO - ...... - 什么是模板方法设计模式? - 在模板类的模板方法定义核心算法骨架,具体的实现步骤可以延迟到子类中去完成 - 模板类通常是一个抽象类,模板类中的模板方法定义核心算法,这个方法通常是final(但也可以不是final的) - 模板类当中的抽象方法就是不确定实现的方法,这个不确定怎么实现的事交给子类去做 ## HttpServlet源码分析 - HttpServlet类是专门为HTTP协议准备的,比GenericServlet更适合HTTP协议下的开发 - HttpServlet在哪个包下? - jakarta.servlet.http.HttpServlet - 到目前为止我们接触了servlet规范中哪些接口? - jakarta.servlet.Servlet 核心接口 - jakarta.servlet.ServletConfig Servlet配置信息接口 - jakarta.servlet.ServletContext Servlet上下文接口 - jakarta.servlet.ServletRequest Servlet请求接口 - jakarta.servlet.ServletResponse Servlet响应接口 - jakarta.servlet.ServletException Servlet异常类 - jakarta.servlet.GenericServlet 标准通用的Servlet类(抽象类) - http包下都有哪些类和接口?jakarta.servlet.http.* - jakarta.servlet.http.HttpServlet (HTTP协议专用的Servlet类,抽象类) - jakarta.servlet.http.HttpServletRequest(HTTP协议专用的请求对象,接口) - jakarta.servlet.http.HttpServletResponse(HTTP协议专用的响应对象,接口) - HttpServletRequest对象中封装了哪些信息? - HttpServletRequest,简称request对象 - HttpServletRequest封装了请求协议的全部内容 - Tomcat服务器(WEB服务器)将“请求协议”中的数据全部解析出来,然后将这些数据全部封装到request对象中 - 也就是说,我们只要面向HttpServletRequest,就可以获得请求协议中的数据 - HttpServletRequest对象是专门用来响应HTTP协议到浏览器的 - HttpServlet源码分析 ```java public class HelloWordServlet extends HttpServlet { // 通过无参构造方法创建对象 public HelloWordServlet() { } // 没有提供init方法,那么必然执行HttpServlet的init方法 // HttpServlet类中没有init方法,会继续执行HttpServlet父类GenericServlet中的init方法 public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); } public void init() throws ServletException { } // 没有提供service方法,那么必然执行HttpServlet的service方法 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; //将ServletRequest和ServletResponse向下转型为HttpServletRequest和HttpServletResponse try { request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastException e) { throw new ServletException(lStrings.getString("http.non_http")); } //调用重载的service方法 service(request, response); } //这个service方法的两个参数都是带有Http //这个service方法是一个模板方法 HttpServlet就是一个模板类 //在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取请求方式 //注意:getMethod()方法获取的请求方式,可能是其中之一: //GET POST PUT DELETE HEAD OPTIONS TRACE String method = req.getMethod(); //如果请求方式是GET,执行doGet方法 if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); } catch (IllegalArgumentException iae) { // Invalid date header - proceed as if none was set ifModifiedSince = -1; } if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } } //HttppServlet中doGet和doPost方法 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //报405错误 String msg = lStrings.getString("http.method_get_not_supported"); sendMethodNotAllowed(req, resp, msg); } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String msg = lStrings.getString("http.method_post_not_supported"); sendMethodNotAllowed(req, resp, msg); } } ``` - 通过以上源码分析,假如前端发送的请求是get请求,但是程序员重写的是doPost方法;或者是前端发送post请求,后端程序员重写的doGet方法,会发生什么? - 发生405错误,405表示前端错误,发送的请求方式不对,不是服务器需要的请求方式 - 只要HttpServlet类中的doGet方法或者doPost方法执行了,必然会出现405错误 - 怎么避免405错误? - 后端重写了doGet方法,前端一定要发送get请求 - 后端重写了doPost方法,前端一定要发送post请求 - 你会看到,为了避免错误,有些人在Servlet中将doGet和doPost方法都进行了重写 - 这样确实可以避免405错误,但不建议。405错误还是有用的,该报错的时候就该让它报错 - 如果同时重写了doGet和doPost方法,还不如直接重写service方法 - 我们编写的HelloServlet类继承HttpServlet类,直接重写HttpServlet类中的service()方法行吗? - 可以,不过你享受不到405错误,享受不到HTTP协议专属的东西 - 到今天我们终于得到了一个最终的Servlet类的开发过程 - 第一步:编写一个Sevlet,直接继承HttpServlet - 第二步:重写doPost方法或者doGet方法,到底重写谁,由javaweb程序员说的算 - 第三步:将Servlet类配置到web.xml文件中 - 第四步:准备前端的页面 ## 关于一个web站点的欢迎页面 - 什么是一个web站点的欢迎页面 - 对于一个webapp来说,我们可以设置它的欢迎页面的 - 设置了web站点欢迎页面后,当你访问这个webapp的时候,或者访问这个web站点的时候,没有指定任何资源路径,这个时候会默认访问你的欢迎页面 - 怎么设置欢迎页面 - 第一步:在IDEA工具的web目录下新建了一个login.html - 第二步:在web.xml文件中进行以下配置 - ```xml login.html ``` - 注意:设置欢迎界面的时候,这个路径不需要以“/”开始,并且这个路径默认是从webapp的根下开始找 - 第三步 :启动服务器在浏览器地址栏输入地址 - http://localhost:8888/servlet07 - 如果在webapp的根下新建一个目录,目录中再给个文件,那么这个欢迎页面如何设置? - web.xml文件中这样配置 - ```xml 目录名/文件名 ``` - 一个webapp是可以设置多个欢迎页面的 - ```xml page1/page2/page.html login.html ``` - 注意:越靠上的优先级越高。找不到往下找 - 当我的文件名设置为index.html的时候,不需要再web.xml文件中进行配置,为什么? - 这是因为Tomcat服务器已经提前配置好了 - 实际上设置欢迎页面有两个地方可以配置 - 一个是webapp内部的web.xml文件(这个地方属于局部配置) - 一个是再CATALINA_HOME/conf/web.xml文件中进行配置(这个地方属于全局配置) - ```xml index.html index.htm index.jsp ``` - 注意原则:局部优先级(就近原则) - 欢迎页可以是一个Servlet吗? - 当然可以,欢迎页就是一个资源,既然是一个资源,那么可以是静态资源,也可以是动态资源 - 静态资源:index.html等 - 动态资源:Servlet ## 关于WEB-INF目录 - 放在WEB-INF目录下的资源是受保护的,在浏览器上不能通过路径直接访问。所以像HTML、CSS、image等静态资源一定要放到WEB-INF目录之外 ## HttpServletRequest接口详解 - HttpServletRequest是一个接口 - 全限定名称:jakarta.servlet.http.HttpServletRequest - HttpServletRequest接口是Servlet规范中的一员 - HttpServletRequest的父接口是:ServletRequest - ```java public interface HttpServletRequest extends ServletRequest {} ``` - HttpServletRequest接口实现类是谁写的?HttpServletRequest对象是给谁创建的? - 通过测试:org.apache.catalina.connector.RequestFacade实现了HttpServletRequest接口 - Tomcat服务器是实现了HttpServletRequest接口 - HttpServletRequest对象中有什么信息?都包装了什么信息? - HttpServletRequest对象是Tomcat服务器负责创建的,这个对象封装了HTTP的请求协议 - 实际上是用户发送请求的时候,遵循了HTTP协议,发送的是HTTP的请求协议,Tomcat服务器将HTTP协议中的信息以及数据全部解析出来,然后Tomcat服务器把这些信息封装到HttpServletRequest对象中,传给了我们javaweb程序员 - javaweb程序员面向HttpServletRequest接口编程,调用方法就可以获得请求的信息了 - request和response对象的生命周期 - 一个是请求对象,一个是响应对象。这两个对象只在当前请求有效 - 一次请求对应一个request - ...... - HttpServletRequest接口中有哪些方法? - 怎么获取前端用户提交的数据? - ```java public String getParameter(String name); 获取value这个一维数组的第一个元素(最常用) public Map getParameterMap(); 这个获取Map public Enumeration getParameterNames(); 这个获取Map集合的key public String[] getParameterValues(String name); 根据key获取value //以上的4个方法,和获取用户提交的数据有关 ``` - 思考:如果是你,前端的form表单提交了数据之后,你准备怎么存储这些数据,采用什么样的数据结构? - 前端提交的数据格式:username=123&userpwd=122&aihao=s&aihao=d&aihao=tt - 我会采用map集合 - ```java Map key存储String value存储String 如果采用这种想法,key重复的时候value被覆盖 ------------------------------------------ 改进后: Map key存储String value存储String[] ``` - 注意:前端表单提交数据的时候,前端永远提交字符串,后端获取的也永远是字符串 - request对象实际上又称为“请求域对象” - 应用域对象是什么? - ServletContext(Servlet上下文对象) - 请求域对象只在一次请求内有效,一次请求结束,这个请求域就销毁了。请求域对象比应用域对象范围小很多,生命周期也短很多 - 请求域和应用域都有如下三个方法 - ```java public void setAttribute(String name,Object value); public Object getAttribute(String name); public void removeAttribute(String name); ``` - 请求域与应用域的选用原则? - 尽量使用小的域对象,以为小的域对象占用的资源少 - 使用Servlet的转发机制,完成资源跳转 - 怎么跳转? - 第一步:获取请求转发器对象 - ```java RequestDispatcher requestDispatcher =request.getRequestDispatcher("/b"); //相当于把“/b”这个路径包装到请求转发器当中,实际上是把下一个跳转的资源路径告知Tomcat服务器 以"/"开始,不需要加项目名 ``` - 第二步:调用请求转发器RequestDispatcher的forward方法,进行转发 - ```java requestDispatcher.forward(request,response); //转发的时候,这两个参数很重要,request和response都是要传递给下一个资源的 ``` - 第一步和第二步可以联合在一起 - ```java request.getRequestDispatcher("/b").forward(request,response); ``` - 两个Servlet怎么共享数据? - 将数据放到ServletContext应用域当中,当然是可以的,但是应用域范围太大,占用资源太多,不建议使用 - 可以将数据放到Request域当中,然后AServlet跳转到BServlet,保证AServlet和BServlet在同一次请求中,这样就可以做到两个Servlet,或者多个Servlet共享一份数据 - 转发的下一个资源必须是一个Servlet吗? - 不一定,只要是Tomcat服务器当中的合法资源,都是可以转发的,例如:html...... - 注意:转发的时候,路径的写法要注意,转发的路径以“/”开头,不加项目名 - 关于request对象中的两个非常容易混淆的方法 - ```java String username=request.getParameter("username"); Object obj=request.getAttribute("username"); //第一个方法,获取的是用户在浏览器上提交的数据 //第二个方法,获取的是请求域当中绑定的数据 ``` - HttpServletRequest接口的其他常用方法 - ```java //获取客户端IP地址 String addr = request.getRemoteAddr(); //设置请求体的字符集 显然这个方法是处理POST请求的乱码问题。这种方式不能解决get请求乱码问题 //Tomcat10之后,request请求体当中的字符集默认是UTF-8,不需要设置字符集,不会出现乱码 //Tomcat9之前(包括9在内),如果当前请求体提交的是中文,获取之后出现乱码,怎么解决这个乱码?执行以下代码 request.setCharacterEncoding("UTF-8); //在Tomcat9之前(包括9)响应中文也会有乱码,怎么解决这个乱码? response.setContentType("text/html;charset=UTF-8"); //在Tomcat10之后,包括10在内,响应中文的时候不会再出现乱码,以上代码不需要设置UTF-8 //get请求出现乱码怎么解决? //get请求提交数据的时候是在请求行上进行的 //修改CATALINA_HOME/conf/server.xml配置文件 //注意:从Tomcat8之后,URIEncoding默认值是UTF-8,7之前的都是ISO8859-1,所有GET请求也没有乱码问题 //动态获取应用的根路径 public String getContextPath(); //获取请求方式 public String getMethod(); //获取请求的URI public String getRequestURI(); // servlet08/request //获取ServletPath(url-pattern) public String getServletPath(); // request ``` ## 使用纯Servlet做一个单表的CRUD操作 - 使用纯粹的Servlet完成单表【部门】的增删改查操作(B/S) - 实现步骤 - 第一步:准备数据库表 - ```sql #部门表 drop table if exists dept; create table dept( deptno int primary key, dname varchar(255), loc varchar(255) ); insert into dept(deptno,dname,loc) values(10,'销售部','北京'); insert into dept(deptno,dname,loc) values(20,'研发部','上海'); insert into dept(deptno,dname,loc) values(30,'技术部','广州'); insert into dept(deptno,dname,loc) values(40,'媒体部','深圳'); commit; select * from dept; ``` - 准备一套HTML页面(项目原型) - 把HTML页面准备好 - 然后把HTML页面中的链接都能跑通 - 应该设计哪些页面? - 新增页面:add.html - 修改页面:edit.html - 详情页面:detail.html - 欢迎页面:index.html - 列表页面:list.html(以list列表为核心,展开其他操作) - 第三步:分析我们系统有哪些功能 - 查看部分列表 - 新增部门 - 删除部门 - 查看部门详细信息 - 跳转到修改页面 - 修改部门 - 第四步:在IDEA中搭建开发环境 - 创建一个webapp - 向webapp添加连接数据库的jar包 - 必须在WEB-INF目录下新建一个lib目录 - JDBC的工具类 - 第五步:实现功能 - 注意:转发是一次请求,在请求转发的时候,如果当前请求是post请求,那么之后依旧是post请求 ## 在一个web应用中应该如何完成资源的跳转 - 在一个web应用中,可以通过两种方式完成资源的跳转 - 第一种方式:转发 - 第二种方式:重定向 - 转发和重定向有什么区别? - 代码上有什么区别 - 转发 - ```java //获取请求转发器 RequestDispatcher dispatcher=request.getRequestDispatcher("/dept/list"); //调用请求转发器的forward方法完成转发 dispatcher.forward(request,response); //合并成一行代码 request.getRequestDispatcher("/dept/list").forward(request,response); ``` - 重定向 - ```java //注意:路径上要加一个项目名,为什么? //浏览器发送请求,请求路径上需要添加项目名 //以下这一行代码会将请求路径"/oa/dept/list"发送给浏览器 //浏览器会自发的向服务器发送一次全选的请求:"/oa/dept/list" response.sendRedirect("/oa/dept/list"); ``` - 形式上什么区别 - 转发(一次请求) - 在浏览器地址栏上发送的请求是:http://localhost:8888/servlet10/a, 最终请求结束之后,浏览器地址栏上的地址还是这个,没变 - 重定向(两次请求) - 在浏览器地址栏上发送的请求是:http://localhost:8888/servlet10/a,最终请求结束之后,浏览器地址栏上的显示的地址是:http://localhost:8888/servlet10/b - 转发和重定向的本质区别? - 转发:是WEB服务器来控制的,A资源跳转到B资源,这个跳转动作是Tomcat服务器内部完成的 - 重定向:是浏览器完成的,具体跳转到哪个资源,浏览器说了算 - 转发和重定向如何选择? - 如果在上一个Servlet当中向request域中绑定了数据,希望从下一个Servlet中把request域里面的数据取出来,使用转发机制 - 剩下的所有请求均使用重定向(重定向使用较多) - 跳转的下一个资源有没有要求?必须是一个Servlet吗? - 不一定,跳转的资源只要是服务器内部合法的资源即可。包括:Servlet、JSP、HTML...... - 转发会存在浏览器刷新问题 ## Servlet注解,简化配置 - 分析项目中的web.xml文件 - 现在只是一个单标的CRUD,没有复杂的业务逻辑,很简单的一丢丢功能。web.xml文件中就有如此多的配置信息。如果采用这种方式,对于一个大的项目来说,这样的话web.xml文件会非常大,有可能会达到几十兆 - 在web.xml文件中进行servlet信息的配置,显然开发效率比较低,每一个都需要配置一下 - 而且web.xml文件中的配置很少被修改的,所以这种配置信息能不能直接写到java类中呢?当然可以 - Servlet3.0版本之后,推出了各种Servlet基于注解式开发,优点是什么? - 开发效率高,不需要编写大量的配置信息,直接在java类中使用注解进行标注 - web.xml文件体积变小了 - 并不是说有了注解之后,web.xml文件就不需要了 - 有一些需要变化的信息,还是需要配置到web.xml文件中。一般都是 注解+配置文件 的开发模式 - 一些不会经常变化修改的配置建议使用注解,一些可能会被修改的建议写到配置文件中 - 我们的第一个注解 - ```java jakarta.servlet.annotation.WebServlet ``` - 在类上使用:@WebServlet - WebServlet注解中有哪些属性 - name属性:用来指定Servlet的名字,等同于: - urlPatterns属性:用来指定Servlet的映射路径,可以指定多个字符串,等同于: - loadOnStartUp属性:用来指定在服务器启动阶段是否加载该Servlet类,等同于: - value属性:和urlPatterns效果一样,不同在于,当注解的属性名是value的时候,使用注解的时候属性名可以省略 - 注意:不是必须将所有属性都写上,只需要提供需要的 - 注意:属性是一个数组,如果数组中只有一个元素,使用注解的时候,属性值的大括号可以省略 - 注解对象的使用格式: - @注解名(属性名=属性值,属性名=属性值,属性名=属性值......) ## 使用模板设计模式优化oa项目 - 上面的注解解决了配置文件的问题,但是现在的oa项目仍然存在一个比较臃肿的问题 - 一个单标的CRUD,就写了6个Servlet,如果一个复杂的业务系统,这种开发模式,显然会导致类爆炸(类的数量太大) - 怎么解决这个类爆炸问题?可以使用模板方法设计模式 - 以前的设计是一个请求一个Servlet,1000个请求1000个Servlet - 可以这样做:一个请求对应一个方法,一个业务对应一个Servlet类 - 处理部门相关业务的对应一个DeptServlet。处理用户相关业务的对应一个UserServlt类。处理银行相关业务的对应一个CardServlet ## 分析使用纯粹的Servlet开发web应用的缺陷 - 在Servlet当中编写HTML/CSS/JavaScript等前端代码,存在什么问题? - java程序员编写前端代码,编写难度大,麻烦,耦合度非常高,代码不美观,维护成本太高 - 修改小小的一个前端代码,,只要有改动,就需要重新编译java代码,生成新的class文件,打一个新的web包,重新发布 - 思考一下,怎么解决这个问题? - 上面的Servlet(java程序)能不能不写了,让机器自动生成。我们程序员只需要写这个Servlet程序中的前端的“代码”,然后让机器将我们写的“前端”代码自动翻译生成“Servlet这种java程序”,然后机器自动将“java程序”翻译生成“class文件”。然后让JVM调用这个class中的方法 ## B/S结构系统的会话机制(session机制) - 什么是会话 - 会话对应的英语单词:session - 用户打开浏览器,进行一系列操作,然后最终关闭浏览器,这个整个过程叫做:一次会话。会话在服务器端也有一个对象,这个java对象叫做:session - 回顾:什么是一次请求?用户在浏览器点击一下,然后到页面停下来,可以粗略的认为是一次请求。请求对应的服务器端的对象是:request - 在Java的Servlet规范中,session对应的类名是:HttpSession(jakarta.servlet.http.HttpSession) - session机制属于B/S结构的一部分。session机制实际上是规范,不同的语言对这种会话机制都有实现 - session对象的主要作用是什么? - 保存会话状态(用户登陆成功了,这是一种登陆成功的状态,那么你怎么把登陆成功的状态一直保存下来?使用session对象可以保留会话状态) - 为什么需要session对象来保存会话状态呢? - 因为HTTP协议是一种无状态协议 - 什么是无状态? - 请求的时候,B和S是连接的,但是请求结束之后,连接就断了。为什么要这样做?HTTP协议为什么要设计成这样? - 因为这样的无状态协议可以降低服务器压力。请求的瞬间连接,请求结束之后,连接断开,这样服务器压力小 - 只要B和S断开了,那么关闭浏览器这个动作,服务器知道吗? - 不知道,服务器是不知道浏览器关闭的 - 张三打开一个浏览器A,李四打开一个浏览器B,访问浏览器后,在服务器端就会生成: - 张三专属的session对象 - 李四专属的session对象 - 为什么不使用request对象保存会话状态?为什么不使用ServletContext对象保存会话状态? - request是一次请求一个对象 - ServletContext对象是服务器启动的时候创建,服务器关闭的时候销毁,这个ServletContext对象只有一个 - ServletContext对象的域太大了 - request请求域(HttppServletRequest)、session域(HttpSession)、application域(ServletContext) - request < session < application - session对象怎么获取? - ```java HttpSession session = request.getSession(); //从服务器端获取当前的session对象,如果没有获取到任何的session对象,则新建 ``` - ```java HttpSession session = request.getSession(false); //从服务器端获取当前的session对象,如果获取不到,则不会新建,返回null ``` - session对象的实现原理 - session对象是存储在服务器端的 - 一个session对象对应一个会话 - 一次会话包含多次请求 - session的实现原理 - 在web服务器中有一个session列表,类似于map集合,这个map集合的key存储的是sessionid,这个map集合的value存储的是对应的session对象 - 用户发送第一次请求的时候:服务器会创建一个新的session对象。同时给session对象生成一个id,然后web服务器会将session对象的id发送给浏览器,浏览器将session的id保存在浏览器的缓存中 - 用户发送第二次请求的时候:浏览器会将浏览器缓存中的sessionid自动发给服务器,服务器获取到sessionid,然后从session列表中查找到对应的session对象 - JSESSIONID=xxxx 这个是以Cookie的形式保存在浏览器的内存中的,浏览器只要关闭,这个cookie就没有了 - ![1662204121126](C:\Users\18055243628\AppData\Roaming\Typora\typora-user-images\1662204121126.png) - 为什么关闭浏览器,会话结束? - 关闭浏览器之后,浏览器中保存的sessionid消失,下次重新打开浏览器之后,浏览器缓存中没有这个sessionid,自然找不到服务器中对应的session对象,session对象找不到等同于会话结束 - session对象什么时候被销毁? - 一种销毁:超时销毁 - 一种销毁:手动销毁 - Cookies禁用了,session还能找到吗? - cookie禁用了是什么意思?服务器正常发送cookie给浏览器,但是浏览器不要了,拒收了,不是服务器不发了 - 因此找不到了,浏览器每一次请求都会获得新的session对象 - cookie禁用了,session机制还能实现吗? - 可以,使用URL重写机制 - http://localhost:8888/servlet13/text/session;jsessionid=B9F40A6A6C0526231EC14DCBBB9EEF1E - URL重写机制会提高开发者的成本。开发人员在编写任何请求路径的时候,后面都要添加一个sessionid,给开发带来了很大的难度,很大的成本。所以大部分的网站都是这样设计的:你要是禁用cookie,你就别用了 - 总结一下,到目前为止我们所了解的域对象 - request(HttpServletRequest) - 请求域 - session(HttpSession) - 会话域 - application(ServletContext) - 应用域 - 这三个域的大小关系 - request < session < application - 他们三个域对象都有下面三个公用的方法 - setAttribute - getAttribute - removeAttribute - 使用原则:尽量使用小的域 - 学习了session之后,我们怎么解决oa项目中的登陆问题 - 登陆成功之后,可以将用户的登录信息存储到session当中 - 销毁session对象 - ```java session.invalidate(); ``` - 怎么让jsp页面不自己创建session? - ```jsp <%--访问jsp时不生成session对象,但要注意session为false时 内置对象session就不能用了--%> <%@page session="false" %> ``` - 怎么设置session的销毁时间 - ```xml 30 ``` - 如果没有配置,session默认销毁时间时30分钟 - 在CATALINA_HOME/conf/web.xml中可以查看session默认销毁时间 ## Cookie - session实现原理中,每一个session对象都会关联一个sessionid - JSESSIONID=8BCDCEC3DBEFE92FDF825BCEA5045CA3 - 以上这个键值对就是Cookie对象 - 对于session关联的cookie来说,这个cookie是被保存在浏览器运行的内存中 - 只要浏览器不关闭,用户再次发送请求的时候,浏览器会自动将运行的cookie发送给服务器 - 例如,这个Cookie:JSESSIONID=8BCDCEC3DBEFE92FDF825BCEA5045CA3就会再次发送给服务器 - 服务器会根据8BCDCEC3DBEFE92FDF825BCEA5045CA3这个值来找对应的session对象 - cookie怎么生成?cookie保存在什么地方?cookie有什么用?浏览器什么时候会发送cookie?发送哪些cookie给服务器? - cookie最终是保存在浏览器客户端上的 - 可以保存在运行内存中(浏览器关闭cookie就消失了) - 也可以保存在硬盘文件中(永久保存) - cookie有什么用? - cookie和session机制其实都是为了保存会话的状态 - cookie是将会话的状态保存在浏览器客户端(cookie数据存储在浏览器客户端) - session是将会话的状态保存在服务器端上(session对象是存储在服务器上) - 为什么要有cookie和session机制呢?因为HTTP协议是无状态 无连接协议 - Cookie经典案例 - 京东商城,在未登录的情况下,向购物车中放几件商品。然后关闭商城,再次打开浏览器,访问京东商城的时候,购物车中的商品还在,这是怎么做到的?我没有登录,为什么购物车中还有商品? - 将购物车的商品编号放到cookie中,cookie保存在硬盘文件中,这样即使关闭浏览器,硬盘上的cookie还在,下一次再次打开京东购物车的时候,会自动读取本地硬盘中存储的cookie,拿到商品编号,动态展示购物车中的商品 - 京东存储购物车中商品的cookie可能是这样:prodects=xxx.yyy.zzz - 注意:cookie如果清除掉,购物车中的商品就消失了 - 126邮箱中有一个功能,十天免登录 - 这个功能也是需要cookie来实现的 - 怎么实现的呢? - 用户输入正确的用户名和密码,并且同时选择了十天内免登录。登陆成功后。浏览器客户端会保存一个cookie,这个cookie保存了用户名和密码等信息,这个cookie是保存在硬盘文件中的,十天有效。在十天内用户再次访问126的时候,浏览器自动提交126的关联的cookie给服务器,服务器接收到cookie之后,获取用户名和密码,验证通过之后,自动登陆成功 - 怎么让cookie失效? - 十天后自动失效 - 改密码 - 在浏览器客户端清除cookie (ctrl+shift+del) - cookie机制和session机制其实都不属于java中的机制,实际上cookie机制和session机制都是HTTP协议的一部分,php开发中也有cookie机制和session机制,只要你是做web开发,不管是什么编程语言,cookie和session机制都是需要的 - HTTP协议中规定:任何一个cookie都是由name和value组成的,name和value都是字符串类型的 - 在java的Servlet中,对cookie提供了哪些支持呢? - 提供了一个Cookie类专门表示cookie数据:jakarta.servlet.http.Cookie - java程序员怎么把cookie数据发送给浏览器?response.addCookie(cookie); - HTTP协议中是这样规定的:当浏览器发送请求的时候,会自动携带该path下的cookie数据给服务器(URL) - 关于cookie的有效时间 - 怎么用java设置cookie的有效时间 - cookie.setMaxAge(60*60);设置cookie一小时之后消失 - 没有设置cookie有效时间,默认保存在浏览器的运行内存当中,浏览器关闭则失效 - 只要设置cookie的有效时间 > 0,cookie保存在硬盘文件中 - 设置cookie的有效时间为0,表示该cookie被删除,主要应用在:删除浏览器上同名的cookie - 设置cookie的有效时间为负数,表示该cookie不会被存储在硬盘文件中,会放在浏览器运行内存中,和不调用setMaxAge同一个效果 - 关于cookie的path,cookie关联的路径是什么? - 假设当前发出的请求路径是:http://localhost:8888/servlet14/cookie/generate 生成cookie,如果cookie没有设置path,默认的path是什么? - 默认的path是:http://localhost:8888/servlet14/cookie 以及它的子路径 - 也就是说,以后只要浏览器的请求路径是:http://localhost:8888/servlet14/cookie 这个路径以及这个路径下的子路径,cookie都会被发送到服务器 - 手动设置cookie的path - cookie.setPath("/servlet14");表示只要是这个servlet14项目的请求路径,都会提交这个cookie给服务器 - 浏览器发送cookie给服务器,服务器中的Java程序怎么接收? - ```java Cookie[] cookies = request.getCookies(); //可能返回null if(cookies!=null){ for (Cookie cookie : cookies) { //获取cookie的name String name=cookie.getName(); //获取cookie的value String value = cookie.getValue(); } } ``` - 使用cookie实现十天内免登录 - 先实现登陆功能 - 登陆成功 - 跳转到部门列表页面 - 登陆失败 - 跳转到登录失败页面 - 修改前端页面 - 在登录页面给一个复选框,复选框后面给一句话:十天内免登录 - 修改servlet中login方法 - 如果用户登陆成功并且用户选择是十天内免登录,这个时候要在login中创建cookie,用来存储用户名和密码,并且设置路径,设置有效时间,将cookie响应给浏览器(浏览器将其自动保存在硬盘文件中十天) - 用户再次访问网站欢迎页的时候,有两个走向 - 要么跳转到部门列表页面 - 要么跳转到登录页面 # JSP - 我的的第一个JSP程序 - 在WEB-INF目录之外新建一个index.jsp文件,然后这个文件中没有任何内容 - 将上面项目部署之后,启动服务器,打开浏览器,访问以下地址 - http://localhost:8888/jsp/index.jsp 展现在大家面前的是一个空白 - 实际上访问以上的这个:index.jsp,底层执行的是:index.class这个程序 - 这个index.jsp会被tocmat翻译生成index_jsp.java文件,然后tomcat服务器又会将index_jsp.java文件编译生成index_jsp.class文件 - 访问index.jsp,实际上执行的是index_jsp.class中的方法 - JSP实际上就是一个Servlet - index_jsp访问的时候,会自动翻译生成index_jsp.java,然后自动编译生成index_jsp.class,那么这个index.jsp就是一个类 - index.jsp继承了HttpJspBase,而HttpJspBase继承的是HttpServlet。所以index_jsp类就是一个Servlet类 - jsp的生命周期和Servlet生命周期完全相同。就是同一个东西,没有任何区别 - jsp和Servlet一样都是单例的(假单例) - jsp文件第一次访问的时候是比较慢的,为什么? - 第一次比较麻烦,要把jsp文件翻译生成java源文件 - java源文件要编译生成class字节码文件 - 然后通过class去创建servlet对象 - 然后调用servlet对象中的init方法 - 最后调用servlet对象中的service方法 - 第二次就比较快了,为什么? - 因为第二次直接调用单例Servlet对象中的service方法 - JSP是什么? - JSP是java程序(Jsp本质还是一个Servlet) - JSP是:JavaServlet Page的缩写(基于java语言实现的服务端的页面) - Servlet是JavaEE的13个子规范之一,那么JSP也是JavaEE的13个子规范之一 - JSP是一套规范。所有的WEB容器/WEB服务器都是遵循这套规范的,都是按照这套规范进行翻译 - 每一个WEB容器/WEB服务器都会内置一个JSP翻译引擎 - 对JSP进行错误调试的时候,还是需要直接打开JSP文件对应的java文件,检查java代码 - 既然JSP本质上是一个Servlet,那么JSP和Servlet有什么区别? - 职责不同: - Servlet的职责是什么:收集数据(Servlet的强项是做逻辑处理、业务处理、连接数据库、获取收集数据) - JSP的职责是什么:展示数据(JSP的强项是做数据展示) - JSP的基础语法 - 在jsp文件中直接编写的文字,都会自动被翻译到哪? - 翻译到Servlet类的service方法的out.write("翻译到这里"),直接翻译到双引号里,被java程序当成普通字符串打印输出到浏览器 - 在JSP中编写HTML/CSS/JavaScript代码,这些代码对于JSP来说只是一个普通字符串。但是JSP把这个普通字符串一旦输出到浏览器,浏览器就会对HTML/CSS/JavaScript进行解释执行,展现一个效果 - JSP的page指令,解决响应的乱码问题 - 通过page指令设置响应的内容类型,在内容类型的后面添加:charset=utf-8 - ```jsp <%@page contentType="text/html;charset=utf-8" %> //表示响应的内容类型是text/html,采用的字符集是utf-8 ``` - 怎么在JSP中编写java程序? - <% java语句; %> - 在这个符号当中编写的被视为java代码,被翻译到Servlet类的service方法内部 - 这里要细心点,时刻记住你正在方法体里写代码,什么可以写,什么不可以写 - 在service方法中编写的代码是有顺序的,方法中的代码遵循自上而下的顺序依次逐行执行 - 在同一个JSP当中<%%>可以出现多次 - <%! java语句; %> - 在这个符号当中编写的java程序会自动翻译到service方法之外 - 这个语法很少用,为什么? - 因为在service方法外面写静态变量和实例变量,都会存在线程安全问题。JSP就是Servlet,servlet是单例的,在多线程并发的环境下,这个静态变量和实例变量一旦有修改操作,必然会存在线程安全问题 - jsp的输出语句 - 怎么向浏览器上输出一个java变量 - ```jsp <% String name="jack"; out.write("name="+name); %> ``` - 注意:以上代码中的out是JSP九大内置对象之一,可以直接拿来用。当然,必须只能在service方法中使用 - 如果向浏览器上输出的内容没有java代码,例如输出一个字符串,可以直接在jsp文件中编写,不需要写到<%%>这里 - 如果输出的内容含有java代码,这个时候可以使用以下语法格式 - <%= %> 注意:在=后面编写要输出的内容 - <%= %> 会被翻译到哪里?会被翻译成什么? - 翻译成了这个java代码:out.print(); - 翻译到service方法当中 - 什么时候使用<%=%>输出呢?输出的内容是一个含有java的变量,输出的内容是一个动态的内容,不是一个死的字符串。如果输出是一个死的字符串,直接在JSP文件中编写即可 - 在JSP中如何编写JSP专业注释 - <%--JSP的专业注释,不会被翻译到java源代码当中--%> - - 使用Servlet+JSP完成oa项目的改造 - 使用Servlet处理业务,收集数据 - 使用JSP展示数据 - JSP文件的扩展名必须是xxx.jsp吗? - jsp文件的扩展名是可以配置的,不是固定的 - 在CATALINA_HOME/conf/web.xml,这个文件当中配置jsp文件的扩展名 - ```xml default / jsp *.jsp *.jspx ``` - javaBean其实就是java中的实体类,负责数据的封装 - JavaBean可以理解为符合某种规范 - 有无参构造方法 - 属性私有化 - 对外提供公开的set和get方法 - 实现java.io.Serializable接口 - 重写toString - 重写hashCode和equals - ...... - 由于javaBean符合javaBean规范,具有更强的通用性 - 当前oa系统存在一个问题,任何用户都可以访问该系统进行增删改这些危险操作,增加一个登录功能 - 实现登录功能 - 第一步:准备用户表 - t_user表当中存储的是用户的登录信息,包括用户名和密码 - 密码一般在数据库当中存储的是密文,一般不以明文的形式存储 - 第二步:准备一个登陆界面 - 登陆页面上应该有一个登录的表单,有用户名和密码输入的框 - form表单是post方式提交 - 第三步:后台有一个对应的Servlet来处理登录的请求 - 登陆成功:跳转到部门列表页面 - 登陆失败:跳转到登录失败页面 - 第四步:准备一个登陆失败的页面 - 登录功能实现了,目前存在大的最大的问题 - 这个登录目前只是一个摆设,没有任何作用,只要用户知道后端的请求路径,照样可以在不登录的情况下访问 - 这个登录没有真正起到拦截的作用 - Jsp的page指令 - 指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件) - 指令包括哪些 - include指令:包含指令,在jsp中完成静态包含,很少用了 - taglib指令:引入标签库的指令,JSTL标签指令 - page指令:目前重点学习的一个page指令 - 指令的使用语法 - <%@指令名 属性名=属性值 属性名=属性值 属性名=属性值......%> - 关于page指令中的有哪些属性 - ```jsp <%@page session="false"%> true表示启用jsp的内置对象session,没有session对象会创建 如果没有设置,默认值session="true" session="false"表示不启用内置对象session,当前JSP页面中无法使用内置对象session ``` - ```jsp <%@page contentType="text/json%> contentType属性用来设置响应的内容类型 ``` - ```jsp <%@page pageEncoding="utf-8" %> pageEncoding属性用来设置响应的字符集编码 也可以不使用这个属性,直接在contentType中一起设置 <%@page contentType="text/json;charset=utf-8"%> ``` - ```jsp <%@page import="java.util.List,java.util.Date" %> import语句 导包 ``` - ```jsp <%@page errorPage="/error.jsp" %> 当前页面出现异常之后,跳转到error.jsp errorPage属性用来指定页面出现错误时的跳转位置 ``` - ```jsp <%@ page isErrorPage="true" %> 启用jsp九大内置对象之一:exception exception内置对象就是刚刚发生的异常对象 默认值false ``` - Jsp的九大内置对象 - jakarta.servlet.jsp.PageContext -pageContext:页面作用域 - jakarta.servlet.http.HttpServletRequest -request:请求作用域 - jakarta.servlet.http.HttpSession -session:会话作用域 - jakarta.servlet.ServletContext -application:应用作用域 - pageContext、<%java代码%>等,看着不美观 - EL表达式可以算是jsp语法的一部分,EL表达式归属于jsp - EL表达式出现在jsp中主要是 - 从某个域中取数据,然后将其转换为字符串,然后将其输出到浏览器,这就是EL表达式的功效 - 第一功效:从某个域中取数据 - 四个域 - pageContext - request - session - application - 第二功效:将取出的数据转成字符串 - 如果是一个java对象,会调用toString方法将其转为字符串 - 第三功效:将字符串输出到浏览器 - EL表达式的语法 - ${表达式} - EL表达式的使用 - ```jsp <% //创建User对象 User user=new User(); user.setUsername("jack"); user.setPassword("123"); user.setAge(50); request.setAttribute("userObj",user); %> <%-- 使用el表达式,取出User对象--%> ${这个位置写什么?一定是存储在某个域的name} ${userObj}
等同于java代码<%=request.getAttribute("userObj")%> ${userObj} 底层是怎么实现的?从域中取数据,然后调用user对象的toString方法,转为字符串,输出到浏览器 <%--输出User的属性--%> ${userObj.username}
使用这个方法的前提:userObj有getUsername()方法 ${userObj.password}
使用这个方法的前提:userObj有getPassword()方法 ${userObj.age}
使用这个方法的前提:userObj有getAge()方法 ${userObj.email}
使用这个方法的前提:userObj有getEmail()方法 表达式中的.这个语法,实际上调用的是:getXXX()方法 注意:如果没有对应的get方法,会报500异常 在el表达式中不能加双引号"" 加了双引号el表达式把它当作普通字符串直接输出到浏览器 ``` - 在没有指定范围的前提下,EL表达式优先从小的域取数据 - pageContext==null?<%=request.getAttribute("usernam")%>:""; - EL表达式中取数据包括两种方式 - 第一种:.(大部分都采用这种方式) - 第二种:[ ](如果存储的name中含有特殊字符,可以采用这种方式) - 使用[ ]的时候,需要加双引号。[ ]没有加双引号,会别当成变量;如果加了双引号,会去找user对象的username属性 - ${user["username"]} - 掌握使用EL表达式怎么从Map集合中取数据 - ${map.key} - 掌握使用EL表达式怎么从数组和List集合中取数据 - ${数组[下标]} - ${集合[下标]} - page当中有个指令,可以忽略页面整个EL表达式,被当成普通字符串 - isELIgnored="true" - 默认值是false - 可以使用反斜杠 \ 忽略指定的EL表达式,被当成普通字符串 - \\${username} - 使用EL表达式获取应用的根 - pageContext可以获得九大内置对象,但在EL表达式中没有request对象 - requestScope只代表请求范围,不代表对象 - EL表达式中有一个隐式对象:pageContext - EL表达式中的pageContext和JSP中的pageContext是同一个对象 - 使用EL表达式获取应用的根 - ```jsp ${pageContext.request.contextPath} 等同于 <%=((HttpServletRequest)pageContext.getRequest()).getContextPath()%> ``` - 除了pageContext,EL表达式其他的隐式对象 - param:和request.getParameter(name)用法相同 - ```jsp 爱好:${param.aihao}
爱好:<%=request.getParameter("aihao")%>
``` - paramValues:和request.getParameterValues(name)用法相同 - ```jsp 一位数组:${paramValues.aihao}
一位数组:<%=request.getParameterValues("aihao")%>
获取一维数组中的值:${paramValues.aihao[0]}、${paramValues.aihao[1]}、${paramValues.aihao[2]}
``` - initParam:获取ServletContext初始化参数信息 - ServletContext是Servlet上下文对象,对应JSP九大内置对象之一:application - ```jsp String a=application.getInitParameter("pageSize"); String b=application.getInitParameter("pageNum"); pageSize:${initParam.pageSize}
pageNum:${initParam.pageNum}
``` - ......(不是重点) - EL表达式的运算符 - 算数运算符:+、-、*、/、% - 在EL表达式中+号不会做字符串拼接,转不成数字会报错 - ```jsp ${3+20}
-> 23 ${3+"20"}
-> 23 ``` - 关系运算符:==、!=、>、 >=、 < 、<= 、eq - EL表达式中的 == 、eq、!= 调用了equals方法 - 逻辑运算符:!、 &&、 ||、 not、 and、 or - !和not相同用法 - 条件运算符:?: - ${empty param.username?"对不起,用户名不能为空":"admin"} - 取值运算符:[ ]、. - . 如果是作用在对象上,这个运算符后面的的属性必须是开头字母小写,并且有getXXX()这个方法才行 - empty运算符 - 判断是否为空,为空返回true,不为空返回false - ${empty param.username} - ${!empty param.username} # JSTL标签库 - 什么是JSTL标签库 - Java Standard Tag Lib(java标准的标签库) - JSTL标签库通常结合EL表达式一起使用,目的是让jsp中的java代码消失 - 标签是写在jsp中,但实际上最终还是要执行java程序(java程序在jar包中) - 使用JSTL标签库的步骤 - 第一步:引入JSTL标签库的jar包 - tomcat10之后引入的jar包是: - jakarta.servlet.jsp.jstl-2.0.0.jar - jakarta.servlet.jsp.jstl-api-2.0.0.jar - 在IDEA中怎么引入 - 在WEB-INF下新建lib,然后将jar包拷贝到lib当中。然后将其add as lib... - 第二步:在jsp中引入要使用的标签库(使用taglib指令引入标签库) - JSTL提供了很多种标签,你要引入哪个标签? - 重点掌握核心标签库 - ```jsp <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> prefix="" 这里随便起个名字,核心标签库,大家默认叫做c ``` - 第三步:在需要的位置使用标签即可,表面使用的是标签,底层还是使用java程序 - JSTL标签的原理 - <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - 以上uri后面的路径实际上指向了一个xxx.tld文件 - tld文件是一个xml配置文件 - 在tld文件中描述了”标签“和”java类“之间的关系 - 以上核心标签库对应的tld文件是:c.tld文件,它在哪? - 在jakarta.servlet.jsp.jstl-2.0.0.jar里面META-INF目录下,有一个c.tld文件 - 源码分析 - ```xml 对标签的描述 catch 标签的名字 org.apache.taglibs.standard.tag.common.core.CatchTag 标签对应的java类 JSP 标签体当中可以出现的内容 对属性的描述 var 属性的名字 false false表示该属性不是必须的,true表示该属性是必须的 false 这个描述说明了该属性是否支持EL表达式,false表示支持 ``` - jstl中核心标签库core中的有哪些常用的标签? - ```jsp <%--items="要迭代的集合" var="迭代的每一项"--%> id:${stu.id},name:${stu.name}
<%--var用来指定循环中的变量--%> <%--begin开始--%> <%--end结束--%> <%--step步长--%> <%--底层实际上,会将var存储在pageContext域中--%> ${i}
<%--varStatus表示var的状态对象,这里是个java对象,这个java对象代表了var的状态--%> <%--varStatus这个状态对象有个count属性,count属性从1递增,主要用于编号/序号,可以直接使用--%> 编号:${varStatus.count},id:${stu.id},name:${stu.name}
``` - ```jsp <%--text属性是必须,text属性支持EL表达式,text属性值是boolean类型--%> <%--if标签还有var属性和scope属性,不是必须的, scope用来指定var的存储域 page(pageContext域) request(request域) session(session域) application(application域)--%> <%--将var中的v存储到request域--%>

欢迎你${param.username}


<%--通过EL表达式将request域中的v取出 v变量值存储的是text中的值--%> ${v} ``` - ```jsp <%--和if else if else用法一样--%> 青少年 青年 中年 老年 ``` ## 改造oa - 使用什么技术改造? - 使用Servlet+JSP+EL表达式+JSTL标签库,进行改造 - 在前端HTML代码中,有一个标签叫做base标签,在这个标签中可以设置整个网页的基础路径 - 这不是java语法,也不是jsp语法,是HTML中的一个语法,base标签通常出现在head标签中 - ``` ``` - 在当前页面中,凡是路径没有以”/“开始,都会自动将base中的路径添加到这些路径之前 - ```html 等同于 ``` - 需要注意:在js代码中的路径,保险起见,最好不要依赖base路径。js代码最好写上全路径 - ```javascript ``` # Filter过滤器 - 当前oa项目存在什么问题? - DeptServlet、UserServlet、EmpServlet、OtherServlet都是处理相关的业务,在执行Servlet之前都要判断用户是否登录。如果登录了,可以继续操作,如果没有登录,需要用户登录。这段判断用户是都登录的代码是固定的,并且每一个Servlet都要用到,代码没有得到复用。并且,设置请求和响应编码的代码也没有得到复用。 - 可以使用Servlet规范中的过滤器Filter来解决这个问题 - Filter是什么?有什么用?执行原理是什么? - Filter是过滤器 - Filter可以在Servlet这个目标程序执行前添加代码,也可以在目标Servlet执行之后添加代码,之前之后都可以添加过滤器规则 - 一般情况下,都是在过滤器中编写公共代码 - 一个过滤器怎么写呢? - 第一步:编写一个java类实现一个接口:jakarta.servlet.Filter。并且实现这个接口中所有方法 - init方法:在Filter对象第一次被创建之后调用,并且只调用一次 - doFilter方法:只要用户发送一次请求,则执行一次。发送N次,执行N次。这个方法中编写过滤规则 - destory方法:在Filter对象被销毁之前调用,并且只调用一次 - 第二步:在web.xml文件中对Filter进行配置,这个配置和Servlet很像 - ```xml filter1 com.bjpoweinode.javaweb.servlet.Filter1 filter1 *.do ``` - 或者使用注解:@WebFilter({"*.do"}) - 注意: - Servlet对象默认情况下,在服务器启动的时候不会创建对象 - Filter对象默认情况下,在服务器启动的时候会新建对象 - Servlet是单例的,Filter也是单例的(单实例) - 目标程序Servlet是否执行,取决于两个条件 - 第一:在过滤器中是否编写了:chain.doFilter(request,response);代码 - 第二:用户发送的请求路径是否和Servlet的请求路径一致 - chain.doFilter(request,response); 这行代码的作用 - 执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet - 注意:Filter的优先级,天生就比Servlet高 - /a.do 对应一个Filter,也对应一个Servlet,那么一定是先执行Filter再执行Servlet - 关于Filter的配置路径 - /a.do、/b.do、/dept/add。这些配置方式都是精确匹配 - /* 匹配所有路径 - *.do 后缀匹配。不要以/开始 - /dept/* 前缀匹配 - 在web.xml文件中,Filter的执行顺序是什么? - 依靠filter-mapping标签的配置位置,越靠上优先级越高 - 过滤器的调用顺序,遵循栈数据结构 - 使用@WebFilter的时候,Filter的执行顺序是怎么样的? - 执行顺序:比较Filter这个类名的字典顺序 - 比如:FilterA和FilterB,则先执行FilterA - 比如:Filter1和Filter2,则先执行Filter1 - Filter的生命周期? - Filter的生命周期和Servlet什么周期一致 - 唯一区别是:Filter默认情况下,在服务器启动阶段就实例化,Servlet不会 - Filter这里有一个设计模式 - 责任链设计模式 - 过滤器最大的优点: - 在程序编译阶段不会确认调用顺序。因为Filter的调用顺序是配置到web.xml文件中,只要修改web.xml配置文件中的filter-mapping顺序就可以调整Filter的执行顺序。显然Filter的执行顺序是在程序运行阶段动态组合的。那么这种设计模式被称为责任链设计模式 - 责任链设计模式最大的核心思想: - 在程序运行阶段,动态组合程序的调用顺序 - 使用过滤器改造oa # Listener监听器 - 什么是监听器? - 监听器是Servlet规范中的一员,就像Filter一样,Filter也是Servlet规范中的一员 - 在Servlet中,所以的监听器接口都是以“Listener”结尾 - 监听器有什么用? - 监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机 - 特殊的时机如果想执行这段代码,你需要想到使用对应的监听器 - Servlet规范中提供了哪些监听器? - jakarta.servlet包下 - ServletContextListener - ServletContextAttributeListener - ServletRequestListener - ServletRequestAttributeListener - jakarta.servlet.http包下 - HttpSessionListener - HttpSessionAttributeListener - 该监听器需要使用@WebListener注解进行标记 - 该监听器监听什么?是session域中所有数据的变化,只要数据变化,则执行相应的方法,只要检测点在session域对象上 - HttpSessionBindingListener - 该监听器不需要使用@WebListener进行标记 - 假如你使用User类实现了监听器,那么User类在被放入session的时候触发Bind事件,User对象从session域中删除的时候,触发unbind事件 - 假设Customer类没有实现该监听器,那么Customer对象放入session域或从session域中删除的时候,不会触发bind事件 - HttpSessionIdListener - session中的id发生改变时,监听器中唯一一个方法就会被调用 - HttpSessionActivationListener - 监听session的钝化和活化 - 钝化:session对象从内存存储到硬盘 - 活化:session对象从硬盘恢复到内存中 - 实现一个监听器的步骤:以ServletConextListener为例 - 第一步:编写一个类实现ServletContextListener接口,并且实现里面的方法 - ```java //ServletContext对象被创建时调用 public void contextInitialized(ServletContextEvent sce) {} //ServletContext对象被销毁时调用 public void contextDestroyed(ServletContextEvent sce) {} ``` - 第二步:在web.xml文件中对ServletContextListener进行配置 - ```xml com.bjpowernode.javaweb.listener.MyServletContextListerner ``` - 也可以不适应配置文件,使用注解:@WebListener - 注意:所以监听器中的方法都是不需要javaweb程序员调用,由服务器负责调用 - 当某个特殊的事件发生之后,被服务器调用 - 关于Attribute的监听,以HttpSessionAttributeListener为例 - ```java //向session域中添加属性时监听 public void attributeAdded(HttpSessionBindingEvent se) {} //向session域中替换属性值时监听 public void attributeReplaced(HttpSessionBindingEvent se) {} //向session域中删除属性时监听 public void attributeRemoved(HttpSessionBindingEvent se) {} ``` - HttpSessionBindingListener - ```java //向session域中添加数据时触发事件 public void valueBound(HttpSessionBindingEvent event) {} //从session域中删除数据时触发事件 public void valueUnbound(HttpSessionBindingEvent event) {} ``` - 思考一个业务场景: - 请编写一个功能,记录网站实时在线的用户的个数 - 我们可以通过服务器端有没有分配session对象统计人数,因为一个session代表一个用户,有一个session就代表一个用户。如果你采用这种逻辑去实现的话,session有多少个,在线用户就有多少个。这种方式的话:HttpSessionListener够用了,session对象只要新建,就count++,然后将count存储到ServletContext域当中,在线显示在线人数 - 业务发生改变,只统计登录的用户在线数量,这个怎么办? - session.setAttribute("user",userObj); - 用户登录的标志是什么?session中曾经存储过User类型的对象,那么这个时候就可以让user类型的对象实现HttppSessionBindingListener监听器,以后只要User对象存储到session域中,就count++,然后将count存储到ServletContext对象中,页面显示在线人数即可 - 实现oa项目中当前登录的在线人数 - 什么代表用户登录l? - session.setAttribute("user",userObj); User类型的对象只要在session中存储过,表示有新用户登录 - 什么代表用户退出了? - session.removeAttribute("user"); User类型的对象从session域中移除了 - 或者有可能是session销毁了(session超时) # MVC架构模式 - M(Model:数据/业务)V(View:视图/展示)C(Controller:控制器) - C:是核心,是控制器,是司令官 - 控制器调用Model处理业务 - 控制器调用View进行展示 - M:处理业务/处理数据的一个秘书 - 连接服务器处理数据/业务 - 并将处理数据返回给控制器 - V:负责也页面展示的一个秘书 - 通过jsp、html......展示 - 将展示结果返回给控制器 - DAO - 什么是DAO? * Data Access Object(数据访问对象) * DAO实际上是一种设计模式,属于javaEE的设计模式之一(不是23种设计模式) - DAO只负责数据库表的CRUD,没有人任何的业务逻辑在里面 - 没有任何业务逻辑,只负责数据库表的增删改查的对象,有一个特殊的称谓:DAO对象 - 为什么叫AccountDao * 这是因为这个DAO负责处理t_act这种表 - 一般情况下,一张表对应一个DAO对象 - 封装数据的实体类 - 有的人也会把这种专门封装数据的对象叫:pojo对象 有的人也会把这种专门封装数据的对象叫:bean对象(javabean:咖啡豆) 有的人也会把这种专门封装数据的对象叫:领域模型对象(domain对象) - AccountService:专门处理Account业务的一个类 - 业务类一般起名:XxxService、XxxBiz - 一个业务对应一个方法 - MVC和三层架构 - ![1662455953876](C:\Users\18055243628\AppData\Roaming\Typora\typora-user-images\1662455953876.png) - 解决事务问题 - 事务一定是在业务逻辑层控制的 - 一般是一个业务方法对应一个事务 - 使用ThreadLocal解决事务问题 - ThreadLocal源码分析 - ThreadLocal中数据以键值对存储(Thread,Connection) - ```java private Map map=new HashMap<>(); public void set(T obj){ //向当前线程内添加数据 Thread.currentThread()拿到当前线程对象 map.put(Thread.currentThread(),obj); } public T get(){ //从当前线程内获得数据 return map.get(Thread.currentThread()); } public void remove(){ //从当前线程中移除数据 map.remove(Thread.currentThread()); } ``` - 关闭ThreadLocal中资源的时候为什么一定要使用remove方法? - Tomcat服务器有一个线程池,这些线程都是提前创建好的,也就是说一个人用过了t1线程后,下一次可能还会用到t1,可能存在重复使用。 - 目前项目存在的缺陷? - 在service层控制了事务,service方法中的事务控制代码看着优点别扭,以后能不能不用? - 可以使用动态管理机制来解决这个问题。 - 目前虽然面向接口编程了,但是并没有完全解决对象和对象之间的依赖关系,怎么办? - 可以使用spring的IOC容器来解决这个问题 # 关于web中"/"路径问题 - 直接写结论 - 加"/"是绝对路径,是**根路径** - 不加"/"是相对路径 - 前端和后端根路径 - 前端的根路径是 http://127.0.0.1/ - 后端的根路径是 http://127.0.0.1/web_content/ - 前端:跳转发生在前端,/ 是web服务器的根路径 ,不加 / 是相对当前页面的路径 (如重定向) 后端:跳转发生在后端,/ 是web应用的根路径 ,不加 / 是相对当前页面的路径 (如请求转发) 注意:重定向的跳转发生在前端,即浏览器再次发出请求,而请求转发的跳转发生在服务器端