admin管理员组文章数量:1567915
Servlet
关于系统架构
1.系统架构包括什么形式?
C/S 架构
Client/Server(客户端 /服务器)
特点:需要安装特定的软件
优点:
速度快(软件中大部分都是集成到客户端软件中的,很少量从服务器端传送过来)
体验好(速度快)
界面酷炫(专门的语言实现,灵活)
服务器压力小
安全(在服务器上有缓存)
缺点:
升级维护差劲(每个客户端都要升级)
B/S架构(Browser,浏览器/服务器):
实际上是一个特殊的C/S
优点:
升级维护方便,成本低(只需要升级服务器即可)
不需要安装客户端软件,只需要打开浏览器,输入网址即可
缺点:
速度慢
体验差(只有HTML语言等)
不安全(数据都在服务器上)
BS架构通信原理(没有涉及到java程序)
第一步:输入网址
第二步:域名解析器进行域名解析: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 JS)
第八步:浏览器渲染,执行HTML CSS JS代码,展示效果
什么是请求?什么是响应?
从browser端发送到server端,称为请求 request
从server端发送到browser端,称为响应 response
servlet对象的生命周期
-
我们自己new的Servlet对象,是不受web容器影响的
- web容器创建的集合对象都会被放到一个集合(hashmap)当中,只有放到这个集合当中的servlet才能够被WEB 容器管理
-
怎么在服务器在启动的时候创建Servlet对象呢?
-
在servlet标签中添加0子标签在,在孩子标签中填写整数,越小的整数优先级越高。
-
<servlet> <servlet-name>aServlet</servlet-name> <servlet-class>com.bjpowernode.javaweb.servlet.AServlet</servlet-class> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>aServlet</servlet-name> <url-pattern>/a</url-pattern> </servlet-mapping>
-
-
servlet生命周期
-
默认情况下服务器启动的时候AServlet对象并没有被实例化
-
用户发第一次请求的时候,控制台给了以下内容:
-
AServlet无参构造方法执行了 AServlet init服务执行 AServlet service服务执行
-
根据以上得出结论:
- 用户在第一次发送请求的时候Servlet对象被实例化(AServlet的构造方法被执行了,并且执行的是无参构造方法)
- AServlet对象被创建出来之后,Tomcat服务器马上调用了AServlet对象的init方法(init方法执行的时候,AServlet对象已 经存在,已经被创建出来了)
- 用户第一次发送请求的时候,init方法执行之后,Tomcat服务器马上调用AServlet对象的service方法
-
用户继续发送第二次请求,控制台输出以下内容:
-
AServlet service服务执行
-
得出结论
- 第一:servlet对象是单例的
- 第二:无参构造方法,init只在第一次用户发送请求的时候执行
- 第三:只要用户发送一次请求:service方法必然会执行一次请求
-
关于servlet类中的方法的调用次数:
- 构造方法只执行一次
- init方法只执行一次
- service方法:用户发送一次请求执行一次请求,发送N次执行N次
- destro方法只执行一次
-
当我们在servlet中写一个有参构造时,会发生什么?
- 报错:500
- 500:服务器内部错误
-
注意:无参构造方法不能代替init方法!!
- 原因:Servlet规范中有要求,不建议手动编写构造方法,因为手动编写,容易让无参构造方法消失,可能会导致servlet对象无法实例化,因此init方法有存在的必要
-
编写一个GenericServlet类,是抽象类,其中的抽象方法为service
- GenericServlet实现Servlet接口
- GenericServlet是一个适配器,就是说,后面的只需要去调用GenericServlet即可,不需要去重写方法
- 以后编写的Servlet类,只需要继承GenericServlet,重写service方法即可
-
改造GenericServlet类
- 提供了GenericServlet过后,init还会执行吗?
- 会执行,会执行GenericServlet类中的init方法
!!!!白雪!!!可以直接继承GenericServlet类
ServletConfig
-
是servlet规范中的一种
-
谁去实现这个接口?
- Tomcat服务器去实现
-
一个Servlet对象中有一个ServletConfig对象
-
ServletConfig对象被翻译为:servlet对象的配置信息对象
- 一个Servlet就有一个配置信息对象
- 两个Servlet就有两个配置信息对象
-
ServletConfig对象中包含了那些信息?
<servlet> <servlet-name>configTest</servlet-name> <servlet-class>com.bjpowernode.javaweb.servlet.ConfigTestServlet</servlet-class> </servlet>
-
Tomcat将web.xml文件中的中的配置信息自动封装到ServletConfig对象中
-
ServletConfig接口中哪些方法?
<servlet> <servlet-name>configTest</servlet-name> <servlet-class>com.bjpowernode.javaweb.servlet.ConfigTestServlet</servlet-class> <!-- 配置一个Servlet对象的初始化信息--> <init-param> <param-name>driver</param-name> <param-value>com.mysql.cj.jdbc.Driver</param-value> </init-param> <init-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/bjpowernode</param-value> </init-param> <init-param> <param-name>user</param-name> <param-value>root</param-value> </init-param> <init-param> <param-name>password</param-name> <param-value>123123</param-value> </init-param> </servlet>
-
以上标签中的是初始化参数,初始化参数信息会被Tomcat封装到ServletConfig对象当中
-
servleConfig中有四个方法:
第一个:
-
public String getInitParameter(String name)
第二个:
public Enumeration getInitParameterNames()
第三个:
public ServletContext getServletContext()
第四个:
public String getServletName();
以上的四个方法,在自己编写的Servlet类当中可以使用this去调用。(这个Servlet继承了GenericServlet)
ServletContext
-
是什么?
- ServletContext是接口,是Servlet规范中的一员。
-
是谁实现的?
- tomcat服务器实现
-
谁创建的?什么时候创建的?
- 在web服务器启动的时候创建
- 对于一个webAPP来说 ServletContext对象只有一个
- 在服务器关闭的时候销毁
-
怎么理解?
- Servlet对象的环境对象(上下文对象)
- ServletContext对象其实对应的就是整个web.xml文件
- 放在ServletContext对象当中的数据,所以Servlet一定是共享的
- Tomcat是一个容器,一个容器当中可以放多个webapp,一个webAPP对应一个ServletContext对象
- 一个应用一个webAPP只有一个
-
ServletContext接口中有哪些常用的方法?
-
public String getInitParameter(String name);//通过初始化参数的name获取value public Enumeration<String> getInitParameterName(); //获取所有的参数的name
//以上两个方法是ServletContext对象的方法,这个方法获取的是什么信息?是以下的配置信息 <context-param> <param-name>pageSize</param-name> <param-value>10</param-value> </context-param> <context-param> <param-name>startIndex</param-name> <param-value>0</param-value> </context-param> //以上的配置信息属于应用级配置信息,一般一个项目中共享的配置信息会放到以上的标签当中 //如果你的配置信息只是想给某一个servlet作为参考,那么你配置servlet标签当中即可,使用ServletConfig对象来获取
-
//获取应用根路径(非常重要),因为在java源代码当中有一些地方可能会需要应用的根路径,这个方法可以动态的获取应用根路径 //在java源代码中,不要将应用的根路径写死,因为你永远不知道这个应用在部署时,起什么名字 public String getContextPath(); //String contextPath = application.getContextPath();
-
//获取绝对路径 public String getRealPath(String path);
-
//通过ServletContext对象也可以记录日志 public void log(String message); public void log(String message,Throwable t); //这些日志记录到哪儿了? //localhost.2022-05-07.log ServletContext对象的log方法记录日志信息存储到这个文件中 //catalina.2022-05-07.log 服务器端的java程序运行的控制台信息 //localhost_access_log.2022-05-07.txt 访问日志
-
//ServletContext对象还有一个名字(应用域,还有其他域 eg:请求域,会话域) //如果所有用户共享一份数据,并且这个数据很少被修改,数据量少可将其放到ServletContext这个应用域中 //为什么是所有用户共享? 不然无意义 //为什么是数据量要小? 数据量大会影响内存,性能 //为什么说数据量很少修改,或者几乎不修改? //所有用户共享的数据必然存在线程安全问题,放在ServletConetext中的数据一般都只是可读的 //可以大大提升效率 //存 public void setAttribute(String name,Object value); //map.put(k,y) //取 public void getAttribute(String name); //map.get(k) //删 public void remove Attribute(String name); //map.remove(k)
-
注意:以后我们编写Servlet类的时候,实际上是不会去直接继承GenericServlet类的。使用HttpServlet处理协议会更方便
-
jakarta.servlet.Servlet //(接口) 【爷爷】 jakarta.servlet.GenericServlet //(抽象类)【儿子】 jakarta.servlet.http.HttpServlet //(抽象类)【孙子】 //编写的Servlet要继承HttpServlet
-
-
-
缓存机制
-
堆内存中的字符常量池
-
堆内存中的整型常量池
-
连接池
- JVM
- java.sql.Connection
- 最小连接池
- 最大连接数
- 提高访问效率,也可以保证数据库的安全性
-
线程池
- tomcat服务器本身支持多线程
- 不是发一次请求就新建一个线程
-
redis
- noSql,非关系型数据库,缓存数据库
-
向ServletContext应用域中存储数据,也等于是将数据放到缓存cache里面去了
-
HTTP协议
-
什么是协议?
- 就是 一套标准的规范
-
什么是HTTP协议?
- HTTP协议:W3C制定的超文本传输协议
-
HTTP协议包括?
- 请求协议
- 浏览器 向 web服务器发送数据
- 响应协议
- web服务器 响应 浏览器发送的数据
- 请求协议
-
HTTP请求协议(B->S):
-
包括四部分:
- 请求行
- 包括三部分:
- 第一部分:请求方式
- get(常用)
- post(常用)
- delete
- put
- head
- options
- trace
- 第二部分:URI /servlet05/getServlet
- 什么是URI:统一资源标识,代表网络中某个资源的名字,但是不能定位
- 什么是URL:统一资源定位符 ,可以通过URL获得定位到的资源
- 他俩的区别?
- URL包括URI
- http://localhost:8080/servlet05/index.html 这是URL
- 第三部分:HTTP协议版本号
- 第一部分:请求方式
- 包括三部分:
- 请求头
- 请求的主机
- 请求的端口
- 浏览器信息
- 平台信息
- cookie等的信息
- 空白行
- 用来分隔请求头和请求体的
- 请求体
- 向服务器发送具体数据
- 请求行
-
HTTP请求具体报文:
-
GET请求:
-
GET /servlet05/getServlet?username=zhangsan&password=123 HTTP/1.1 请求行 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Connection: keep-alive Cookie: Idea-645b4828=22e7e245-a393-40d2-a134-ac9753082ab2 Host: localhost:8080 请求头 Referer: http://localhost:8080/servlet05/ Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36 sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows"
-
-
post请求:
-
POST /servlet05/postServlet HTTP/1.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cache-Control: max-age=0 Connection: keep-alive Content-Length: 40 Content-Type: application/x-www-form-urlencoded Cookie: Idea-645b4828=22e7e245-a393-40d2-a134-ac9753082ab2 Host: localhost:8080 Origin: http://localhost:8080 Referer: http://localhost:8080/servlet05/ Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36 sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows"
-
-
-
-
HTTP响应协议(S->B):
-
包括四部分:
- 状态行
- 第一部分:协议版本号 HTTP/1.1 200
- 第二部分:状态码 200 404 405 500等
- 第三部分:状态的描述信息
- ok表示结束
- not found 表示资源找不到
- 第一部分:协议版本号 HTTP/1.1 200
- 响应头
- 空白行
- 用来分隔响应头和响应体的
- 响应体
- 状态行
-
HTTP响应协议具体报文:
-
HTTP/1.1 200 状态行 Content-Type: text/html;charset=UTF-8 响应头 Content-Length: 71 Date: Sun, 08 May 2022 09:53:49 GMT Keep-Alive: timeout=20 Connection: keep-alive 空白行 响应体(用"<br>"网页换行) wiwiwi ttttttt dochin dfsd55fd5sf d asd asd s
-
-
-
怎么查看协议的内容:
- 使用谷歌浏览器 :F12 ,找到network
-
GET请求和POST请求的区别?
- get请求发送数据的时候,数据会挂在URI的后面,并在后面添加了个“?” 后面跟的是数据,发送的数据会回显到浏览器的地址上
- http://localhost:8080/servlet05/getServlet?username=zhangsan&password=123
- post发送数据的时候在请求体当中不会回显到浏览器的地址栏上
- 他们的请求数据格式相同,只是位置不同而已
- name=value&name=value
- name是什么?
- 以form表单为例:name就是form表单input标签当中的name
- value是什么?
- 以form表单为例:name就是form表单input标签当中的value
- get请求只能发送普通的字符串给服务器,并且字符串受浏览器限制
- post请求可以发送任何类型,包括普通字符串 eg:声音,视频,图片
- get请求适合从服务器获取数据
- post请求适合向服务器传送数据
- get是安全 的
- post是危险的,如果数据从后门进入到服务器,服务器就会危险
- get请求支持缓存
- 任何一个get请求最终的响应结果都会被浏览器缓存起来
- post请求不支持缓存
-
什么时候发送GET和POST请求?
- form表单当中的method=“post‘ 用post
- 其他所有情况都用GET
模板方法设计模式
-
什么是设计模式?
- 某个问题的固定的解决方案(可以被重复使用)
-
有哪些设计模式?
- Gof设计模式:
- 常说的23种设计模式
- 单例模式
- 工厂模式
- 代理模式
- 门面模式
- 责任链设计模式
- 观察者模式
- 模板方法设计模式
- …
- JavaEE设计模式:
- DAO
- DTO
- VO
- PO
- pojo
- …
- …
- Gof设计模式:
-
什么是模板方法设计模式?
- 在模板类的模板方法当中定义核心算法骨架,具体的实现步骤延迟到子类当中完成
-
模板类通常是一个抽象类,模板类当中的模板方法定义核心算法,这个方法通常是final的(也可以不是final类)
-
模板类当中的抽象方法就是不确定实现的方法,这个不确定实现的事交给子类去做
HttpServlet源码分析
-
HttpServlet类是专门为HTTP协议准备的。比GenericServlet更加适合HTTP协议下的开发。
-
HttpServlet源码分析:
-
public class HelloServlet extends HttpServlet { //用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法。 public HelloServlet(){ } public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {} } //用户第一次请求的时候,HelloServlet对象对象第一次创建之后,这个init方法会执行 public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); } public void init() throws ServletException { }
-
如果前端发的是dopost,而后端是doget方法,那么会报405错误
- 前后端必须执行同样的方法,也就是doget对应doget dopost对应dopost
-
怎么避免405错误?
- 前端发送doget方法,后端必须重写doget方法
- 前端发送dopost方法,后端也必须重写dopost方法
- 这两个方法由后端决定
-
最终的servle类的开发步骤:
- 第一步:编写一个servlet类,直接继承HttpServlet
- 第二步:重写doGet方法或者doPost方法,到底重写谁,由程序员说了算。
- 第三步:将Servlet类配置到web.xml路径当中
- 第四步:准备前端(form表单),指定请求路径
-
关于一个web站点的欢迎页面
-
什么是一个web站点的欢迎页面?
- 对于一个webapp来说是可以设置欢迎页面的,在没有指定任何资源路径访问的时候,这个时候回默认访问欢迎页面
-
怎么设置欢迎页面?
-
第一步:web目录下创建一个login.html文件
-
第二步:在web.xml文件下进行以下 的配置
-
<welcome-file-list> <welcome-file>login.html</welcome-file> </welcome-file-list>
-
注意:设置欢迎页面的时候,这个路径不需要以“/”开始,并且这个路径是默认从webapp的根下开始查找
-
-
第三步:启动服务器
- http://localhost:8080/servlet07
-
-
如果在webapp的根下新建一个目录,目录中再给一个文件,那么这个欢迎页面该如何设置呢?
-
在webapp的根下新建page1目录
-
在webapp的根下新建page2目录
-
在web.xml文件中应该这样配置
-
<welcome-file-list> <welcome-file>page1/page2/page.html</welcome-file> </welcome-file-list>
-
注意:路径不需要”/“开始,并且路径默认以webapp的根下开始找
-
-
-
一个webapp是可以设置多个欢迎页面的
-
<welcome-file-list> <welcome-file>page1/page2/page.html</welcome-file> </welcome-file-list>
-
注意:越靠上的优先级越高,找不到的继续向下找。都找不到就报404错
-
-
为什么当文件名设置为index.html,并且没有在web.xml文件中配置欢迎页面时会默认index?
-
因为tomcat服务器提前就已经配置好了
-
有两个地方可以配置欢迎页面:
-
一个是在web.xml文件当中配置 (在这里配置的是属于局部配置)
-
一个是在CATALINA_HOME/conf/web.xml文件中配置 (在这个地方配置的是属于全局变量)
-
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list>
-
-
注意原则:局部优先原则。(就近原则)
-
-
-
欢迎页可以是一个servlet
-
可以是静态资源,也可以是动态资源
-
步骤
-
第一步:写一个servlet:
-
public class WelcomeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.print("<h1>Welcome to bjpowernode!!!</h1>"); }
-
第二步:在web.xml文件中配置servlet
-
<servlet> <servlet-name>welcomeServlet</servlet-name> <servlet-class>com.bjpowernode.javaweb.servlet.WelcomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>welcomeServlet</servlet-name> <url-pattern>/welcome/ssds/ssss</url-pattern> </servlet-mapping>
-
-
第三步:在web.xml文件中配置欢迎页
-
<welcome-file-list> <!-- 路径不要以"/"开始--> <welcome-file>welcome/ssds/ssss</welcome-file> </welcome-file-list>
-
-
-
关于web-INF目录
- 在WEB-INF下的文件打开浏览器访问会报404错误
- 因为在WEB-INF目录下的资源是受保护的
HTTPServlet详解
-
HttpServletRequest是一个接口,全限定名称:jakarta.servlet.http.HttpServletRequest
-
HttpServletRequest接口是Servlet规范中的一员。
-
HttpServlet接口的父接口:ServletRequest
- 面向HttpServletRequest接口编程,调用方法就可以获取到请求的信息
-
request和response对象的生命周期?
- request和response对象,一个是请求对象,一个是响应对象,只在当前请求中有效
- 一次请求对应一个request
- 两次请求对应一个response
- 。。。。。
-
常用的方法?
-
怎么获取前端浏览器用户提交的数据?
-
String getParameter(String name); //获取这个一维数组当中的第一个元素,这个方法最常用!!! Map<String,String[]> getParameterMap();//获取map Enumeration<String> getParameterNames();//获取map集合当中所有的key String[] getParameterValues(java.lang.String name);//获取map集合当中所有的值 //以上的四个方法,和获取用户提交的数据有关系
-
注意:前端永远提交的是字符串,后端获取的永远也是字符串
-
-
request对象实际上又称为“请求域”对象
- 应用域对象是什么?
-
ServletContext(servlet上下文对象)
-
什么情况下考虑想ServletContext这个应用域当中绑定数据呢?
- 第一:所有用户数据共享
- 第二:这个共享数据量很小
- 第三:这个共享的数据很少的修改操作
- 在以上三个条件都满足的情况下,使用这个应用域对象,可以大大提高我们程序的执行效率
- 实际上向应用域当中绑定数据,就相当于把数据放到了缓存(cache)当中,然后用户从缓存当中取,减少io的操作,大大提升系统的性能,所以缓存技术是提高性能的重要手段
-
见过哪些缓存技术?
- 字符串常量池
- 整数型常量池([-128~127],但凡是在这个范围当中的Integer对象不再创建新对象,直接从这个整数型常量池中获取。大大提升系统性能。)
- 数据库连接池(提前创建好N个连接对象,将连接对象放到集合当中,使用连接对象的时候,直接从缓存中拿。省去了连接对象的创建过程。效率提升。)
- 线程池(tomcat服务器支持多线程,提前创建好N个对象,将线程对象存储到集合中,然后用户请求过来,直接从线程池当中获取线程对象,直接拿来用,提升系统性能)
-
ServletContext当中有三个操作域的方法:
-
void setAttribute(String name, Object obj); // 向域当中绑定数据。 Object getAttribute(String name); // 从域当中根据name获取数据。 void removeAttribute(String name); // 将域当中绑定的数据移除 // 以上的操作类似于Map集合的操作。 Map<String, Object> map; map.put("name", obj); // 向map集合中放key和value Object obj = map.get("name"); // 通过map集合的key获取value map.remove("name"); // 通过Map集合的key删除key和value这个键值对。
-
-
请求域对象
-
请求域对象要比“应用域”对象的范围小很多,生命周期短很多,请求域只在一次请求内有效
-
一个请求对象request对应一个请求对象
-
请求域也有这三个方法:
-
void setAttribute(String name, Object obj); // 向域当中绑定数据。 Object getAttribute(String name); // 从域当中根据name获取数据。 void removeAttribute(String name); // 将域当中绑定的数据移除
-
-
请求域和应用域的选用原则
- 尽量使用小的域对象,以为小的域对象占用的资源少
-
-
跳转
-
转发
-
//第一步,获取请求转发 // 相当于把"/b"这个路径包装到请求转发器当中,实际上是把下一个跳转的资源路径告知给tomcat服务器 RequestDispatcher dispatcher = request.getRequestDispatcher("/b"); // 第二步,调用请求转发器RequestDispatcher的forward()方法,进行转发, // 转发的时候 request response都要传递给这一个对象 dispatcher.forward(request,response); //两步代码可以连在一起 request.getRequestDispatcher("/b").forward(request,response);
-
-
-
两个Servlet怎么共享数据?
- 将数据放到ServletContext应用域当中,当然是可以的但是应用域作用范围太大,占用资源太多,不建议使用
- 可以将数据放到request域中当中,然后AServlet转发到BServlet,保证AServlet和BServlet在同一次请求当中,这样就可以做到两个servlet或者多个servlet共享数据
-
转发的下一个资源必须是一个Servlet吗?
- 不一定,只要是tomcat合法资源,都可以转发,eg:html
- 注意:转发的时候,路径的写法要注意,转发的路径以“/”开始,不加项目名。
-
关于request对象中两个非常容易混淆的方法:
-
//uri?username=zhangsan&userpwd=123&sex=1 String name = request.getParameter("username"); //之前一定是执行过:request.setAttribute("name",new Object()) //然后才会取数据 Object obj =request.getAttribute("name"); //这两个方法的区别是什么?? //第一个方法:获取的是用户在浏览器上提交的数据 //第二个方法:获取的是请求域当中绑定的数据
-
-
HttpServletRequest接口中的其他常用方法:
-
//获取客户端的IP地址 String remoteAddr = request.getRemoteAddr(); //get请求行上提交数据 //设置请求体的字符集(post请求的乱码问题,这种方式并不能解决get请求乱码的问题) request.setCharacterEncoding("UTF-8"); //tomcat10过后不用设置,默认就是UTF-8 //服务器响应时的乱码问题解决方式 //tomcat10过后就不需要 response.setContentType("text/html;charset=utf-8"); //get请求乱码问题 //get请求发送请求的时候,数据是在请求行上提交的,不是在请求体上提交的 //get请求乱码怎么解决? //在Tomcat10文件里面找到这个logging.properties文件进去修改解决 java.util.logging.ConsoleHandler.encoding = GBK //获取应用的根路径 String contextPath = request.getContextPath(); //获取请求方式 String method = request.getMethod(); //获取请求的URI String requestURI = request.getRequestURI(); //获取servlet path String servletPath = request.getServletPath();
-
-
- 应用域对象是什么?
-
使用纯Servlet做一个单表的CRUD 操作
-
使用Servlet完成单表【对部门的】的增删改查的操作
-
实现步骤:
-
第一步:准备一张数据库表(sql脚本)。
-
#部门表 drop table if exists dept; create table dept( deptno int primary key, dname varchar(100), loc varchar(100) ); 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 update dept set dname=?,loc=? where deptno=?
-
-
第二步:准备一套项目页面(项目原型)
-
将html页面准备好
-
然后将HTML页面的链接都跑通。(页面流转完成)
-
应该设计哪些页面?
-
新增页面 add.html
-
修改页面 edit.html
-
详情页面 detail.html
-
欢迎页面 index.html
-
列表页面 list.html(以列表页面为核心,展开其他操作)
-
-
-
第三步:分析这个系统包括哪些功能?
- 什么叫功能?
- 只要这个操作连接了数据库,就叫做一个独立的功能
- 包括哪些功能?
- 查看部门列表
- 新增部门
- 删除部门
- 查看部门详细信息
- 跳转到修改页面
- 修改部门
- 什么叫功能?
-
第四步:在idea当中搭建开发环境
- 创建一个webapp(给这个webapp导相应要用的jar包)
- 向webapp添加连接数据库的jar包(mysql驱动)
- 在WEB-INF下新建lib包,然后将mysql驱动拷贝进去
- JDBC工具类
- 将所有HTML页面拷贝到web目录下
-
第五步:
-
实现第一个功能:查看部门列表
- 怎样去实现一个功能?
- 建议:可以从后端往前端一步一步写,也可以从前端往后端写。都可以,但是千万不要想起来什么写什么,写的代码的过程最好是程序执行的过程,程序执行到哪里,就写到哪里,这样一个顺序流下来之后,基本没多大的错误。
- 从哪开始?
- 假设从前端开始,那么一定是从用户点击按钮那里开始的。
- 怎样去实现一个功能?
-
第一:先修改前端页面的超链接,因为用户先点击的是这个超链接。
-
<a href="/oa/dept/list">查看部门列表</a>
-
-
第二:编写web.xml文件
-
<servlet> <servlet-name>list</servlet-name> <servlet-class>com.bjpowernode.oa.web.action.DeptListServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>list</servlet-name> <!-- web.xml文件里的这个文件也是以"/"开始!!,但是不需要加项目名--> <url-pattern>/dept/list</url-pattern> </servlet-mapping>
-
-
第三:编写DeptListServlet类继承HttpServlet类,重写doGet方法
-
package com.bjpowernode.oa.web.action; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author:HuangC * @ClassName:DeptListServlet * @Package:com.bjpowernode.oa.web.action.DeptListServlet * @Description: * @CreateDate:2022年05月10日 * @Version:1.8 **/ public class DeptListServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
-
-
第四:在DeptListServlet类中的doGet方法中连接数据库,查询所有的部门,动态的展示部门列表页面
-
分析list页面中哪部分是固定死的,哪部分需要动态展示的。
-
list.html页面中的内容所有的双引号要替换成单引号,因为out.print(“ ”)这里有一个双引号,容易冲突。
-
写完这个功能后的感悟:繁琐,用servlet开发很繁琐:
-
while (rs.next()){ String deptno=rs.getString("deptno"); String dname=rs.getString("dname"); String loc=rs.getString("loc"); out.print(" <tr>"); out.print(" <td>"+(++i)+"</td>"); out.print(" <td>"+deptno+"</td>"); out.print(" <td>"+dname+"</td>"); out.print(" <td align='center'>"); out.print(" <a href=''>删除</a>"); out.print(" <a href='edit.html'>修改</a>"); out.print(" <a href='detail.html'>详情</a>"); out.print(" </td>"); out.print(" </tr>"); }
-
-
-
第六步:查看部门详情:
-
建议:从前端往后端一步步实现,首先考虑用户点击的什么?用户点的东西在哪里?
-
要先找到用户点的“详情”在哪里
-
<a href='写一个路径'>详情</a>
-
这个“详情”是要连接数据库的,所以超链接是要执行java代码的,所以要将这里的路径修改一下修改之后是:
-
<a href='/oa/dept/detail'>详情</a> //前端发送请求要加项目名!!!!!!
-
-
这个路径是要加项目名的!!!
-
-
-
技巧:
-
out.print("<a href='"+contextPath+"/dept/detail?deptno="+deptno+"'>详情</a>");
-
重点:向服务器提交数据的格式:url?name=value&name=value&name=value&name=value
-
这里的问号,必须是英文的问号,不能是中文的问号。
-
-
解决404问题,写web.xml文件。
-
<servlet> <servlet-name>detail</servlet-name> <servlet-class>com.bjpowernode.oa.web.action.DeptDetailServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>detail</servlet-name> <!-- web.xml文件里的这个文件也是以"/"开始!!,但是不需要加项目名--> <url-pattern>/dept/detail</url-pattern> </servlet-mapping>
-
编写一个类:DeptDetailServlet继承HttpServlet,重写doGet方法
-
package com.bjpowernode.oa.web.action; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author:HuangC * @ClassName:DeptDetailServlet * @Package:com.bjpowernode.oa.web.action.DeptDetailServlet * @Description: * @CreateDate:2022年05月10日 * @Version:1.8 **/ public class DeptDetailServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //中文思路(思路来源于:你要做什么?目标:查看部门信息) //第一步:获取部门编号 //第二步:根据部门编号查询数据库,获取该部门的编号对应的部门信息 //第三步:将部门信息响应到浏览器上。(显示一个详情) } }
-
-
在doGet方法中:连接数据库,根据部门编号查询部门的信息,动态展示详情页。
-
-
-
第七步:删除部门
-
怎么开始?从哪里开始?从前端页面开始,用户点击删除按钮的操作,提示用户是否删除,避免误删,(在前端界面写js代码提示)
-
<a href="JavaScript:.void(0)" onclick="del(10)">删除</a> <script type="text/javascript"> function del(dno){ var ok = window.confirm("亲,删除了不可恢复哦!!!"); if(ok){ document.location.href="/oa/dept/delete?deptno="+dno; } }
-
-
以上的前端程序要写到后端的代码当中:
- DeptDetailServlet类的doGet方法当中是,使用out.print()将以上的前端代码输出到浏览器上
-
解决404问题:
-
http://localhost:8080/oa/dept/delete?deptno=10
-
web.xml文件配置:
-
<!-- 删除部门--> <servlet> <servlet-name>delete</servlet-name> <servlet-class>com.bjpowernode.oa.web.action.DeptDeleteServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>delete</servlet-name> <!-- web.xml文件里的这个文件也是以"/"开始!!,但是不需要加项目名--> <url-pattern>/dept/delete</url-pattern> </servlet-mapping>
-
编写DeptDeleteServlet类,重写doGet方法
-
package com.bjpowernode.oa.web.action; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author:HuangC * @ClassName:DeptDeleteServlet * @Package:com.bjpowernode.oa.web.action.DeptDeleteServlet * @Description:删除部门 * @CreateDate:2022年05月11日 * @Version:1.8 **/ public class DeptDeleteServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //根据部门编号删除部门 } }
-
删除成功或者失败的一个处理(这里选择了转发,并没有选择重定向机制)
-
//判断删除成功了还是失败了 if (count==1){ //删除成功 //仍然跳回到部门列表页面 //部门列表的显示需要执行另一个Servlet。转发!! request.getRequestDispatcher("/dept/list").forward(request,response); }else { //删除失败 request.getRequestDispatcher("/erro.html").forward(request,response); }
-
-
-
-
-
第八步:新增部门
- 注意,最后保存成功过后会,转发到、dept/list的时候,会出现405,为什么?
- 第一:保存用户的是post请求,底层要执行doPost方法
- 第二:转发是一次请求,之前是post,之后是post,因为他是一次请求。
- 第三:/dept/list Servlet当中只有一个doGet方法。
- 怎么去解决?两种方法
- 第一种:在/dept/list Servlet中添加doPost方法,然后在doPos方法中调用doGet
- 第二种:重定向
- 注意,最后保存成功过后会,转发到、dept/list的时候,会出现405,为什么?
-
第九步:跳转到修改部门的页面(同前面的步骤)
-
第十步:修改部门
-
在一个web应用中应该如何完成资源的跳转
-
在web应用中可以通过两种方式完成资源的跳转:
- 第一种:转发
- 第二种方式:重定向
-
两种方式有什么区别?
-
代码上有什么区别?
-
转发:
-
//获取请求转发器对象 RequestDispatcher dispatcher=request.getReqestDispatcher("/dept/list") //调用请求转发器对象的forward方法完成跳转 dispaer.forward(request,response); //合并一行代码 request.getReqestDispatcher("/dept/list").dispaer.forward(request,response); //转发的时候是一次请求,AServlet转发到BServlet,在转发到DServlet,不管转发了 多少次都在同一个request中 //调用forward的时候,会将request和response转发到下一个servlet中
-
重定向
-
//注意:路径上要加项目名:为什么? //浏览器发送请求,请求路径上需要添加项目名 //以下这一行代码会将请求路径“/oa/dept/list”发送给浏览器 //浏览器胡自发的向服务器发送一次全新的请求 response.sendRedirect(request.getContextPath()+"/b");
-
-
-
-
形式上有什么区别?
- 转发(一次请求)
- 在浏览器地址栏上发送的请求是:http://localhost:8080/servlet10/a ,最终请求结束后,浏览器地址看上的地址还是这个没变
- 重定向(两次请求)
- 在浏览器地址栏上发送的请求是http://localhost:8080/servlet10/a ,最终在地址栏上显示的 是http://localhost:8080/servlet10/b
- 转发(一次请求)
-
转发和重定向的本质区别
- 转发是由web服务器来控制的 ,A资源跳转到B资源,这个跳转动作是Tomcat服务器内部完成的
- 重定向:是你浏览器完成的,具体跳转到哪个资源,是浏览器说了算
-
使用例子去描述
- 借钱(转发):
- 找张三借钱,张三其实没有钱,但是张三够义气,张三自己找李四借了钱,然后张三把钱借给了我,但是我只求了一个人,我只认为这个钱就是张三的
- 借钱(重定向):
- 找张三借钱,张三没有钱,张三有个好哥们,叫李四,李四是个富二代,于是张三将李四的家庭住址告诉了我,我按照这个地址去找李四,然后从李四那里借了钱,虽然在这个过程中,我求了两个人,并且我知道最终这个钱是李四借给我的
- 借钱(转发):
-
-
转发和重定向应该如何选择?什么时候使用转发,什么时候使用重定向?
- 如果在上一个servlet当中向request绑定了数据,希望下一个servlet当中把request域里面的数据取出来,使用转发机制
- 剩下所有的请求均使用重定向(重定向使用较多)
将oa项目中的资源跳转修改为合适的跳转方式
- 删除之后重定向
- 修改之后重定向
- 保存之后重定向
- 重定向
- 成功
- 失败
Servlet注解,简化配置
-
分析oa项目中的web.xml文件
- 现在只是一个单表的crud,没有复杂的业务逻辑,很简单的一丢丢功能,web.xml文件中就有很多的配置信息,如果采用这种方式,对于一个大项目来说,web.xml文件会非常庞大
- 在web.xml文件中进行servlet配置。显然开发效率比较低,每一个都要配置一下
- 而且web.xml文件中的配置很少被修改的,所以这种配置信息能不能写到java类当中呢,肯定可以
-
在servlet3.0过后就出现了servlet基于注解式开发
- 优点
- 开发效率高,不需要写大量的配置文件,直接在类上使用注解标注
- web.xml文件体积变小
- 优点
-
是不是说有了过后。web.xml文件就不需要了
- 有一些需要变化的信息,还是要配置到web.xml文件中,一般都是注解+配置文件的开发模式。
- 一些不会经常变化的修改的配置建议使用注解,一些可能会被修改的建议写到配置文件中
-
我们的第一个注解:
-
import jakarta.servlet.annotation.WebServlet
-
在servlet类上使用:@WebServlet,WebServlet注解中有哪些属性呢?
- name属性:用来指定Servlet的名字,等同于
- urlPatterns属性:用来指定Servlet的映射路径,可以指定多个字符串,等同于
- loadOnStartUp属性:用来指定服务器启动阶段是否加载该Servlet,等同于
- value属性:当注解的属性名是value的时候,使用注解的时候,value属性名是可以省略的
- 注意!!:不是必须将所有属性都写上,只需要提供需要的。(需要什么就写什么)
- 注意!!:属性是一个数组,如果数组中只有一个元素,使用注解的时候,属性的大括号可以省略。
-
-
注解对象的使用格式:
- @注解名称(属性名=属性值,属性名=属性值,属性名=属性值…)
使用模板方法设计模式优化oa项目
- 上面的注解解决了配置文件的问题,但是oa项目仍然存在一个比较臃肿的问题
- 一个单表的crud,就写了六个Servlet,如果一个复杂的业务系统,这中打开方式,显然会导致类爆炸。(类的数量太大)
- 怎么解决这个问题?可以用模板方法设计模式
- 怎么解决类爆炸的问题?
- 以前的设计是一个请求一个Servlet类,1000个请求就是1000个类,导致类爆炸。
- 可以这样做:一个请求对应一个方法,一个业务对应一个Servlet类
- 处理部门相关业务的对应一个DeptServlet,处理用户相关业务的对应一个UserServlet。处理卡片业务的对应一个cardServlet
分析使用纯粹servlet开发web应用的缺陷
-
在servlet当中编写HTML/CSS/JavaScript等前端代码,存在什么问题
- java程序中编写前端代码,编写难度大,麻烦
- java程序中编写前端代码,显然程序的耦合度非常高
- java程序中编写前端代码,代码不美观
- java长须中编写前端代码,维护成本高
- 修改一个前端代码,只要有改动,就需要重新编译代码
关于B/S结构系统的会话机制(session机制)
-
什么是会话?
- session
- 用户打开浏览器进行一系列操作,然后将浏览器关闭的整个过程:叫做一次会话,会话在服务器端也有一个对应的对象:session
- 什么是一次请求:用户在浏览器上点击了一下,然后到页面停下来,可以粗略认为是一次请求,请求对应的服务器端的java对象是request
- 一个会话包含多次请求(一次对话对应多次请求。)
-
在java的规范当中个,session规范的类名:HttpSession(jakarta.servlet.http.HttpSession)
-
session属于B/S结构的一部分,如果使用php开发,也是有session这种机制的,session机制实际上是一种规范,不同的语言对这个会话机制都有实现
-
session最主要的作用是保存会话。(用户登录成功了,这是一种登录成功的状态,怎么能把登录状态记录下来呢?可以使用session保留会话状态)
-
为什么需要session对象来保存会话状态呢?
- 因为Http协议是一种无状态协议。
- 什么是无状态:请求的时候,B和S是连接的,但是请求结束后,连接就断了。HTTP为什么要设计成这样?
- 因为这样的无状态的协议,可以降低服务器的压力,请求的瞬间是连接的,请求结束之后,连接断开,这样服务器压力小
- 只要B和S断开了,那么关闭浏览器这个操作,服务器知道吗?
- 不知道,服务器不知道浏览器关闭的
-
张三打开一个浏览器A,李四打开一个浏览器B,访问浏览器之后,在服务器会生成:
- 张三专属的session对象
- 李四专属的session对象
-
为什么不适用request对象保存会话状态?为什么不使用ServletContext对象保存会话状态?
- request.setAttribute()方法(存),request.setAttribute()方法(取),ServletContext也有这个方法
- request是请求域,ServletContext是应用域
- ServletContext对象是服务器启动的时候创建,服务器关闭的时候销毁,这个ServletContext对象只有一个。
- ServletContext的域太大
- request请求域(HttpServletRequest),session会话域(HTTPSession),application应用域(ServletContext)
- request<session<applcation
- request.setAttribute()方法(存),request.setAttribute()方法(取),ServletContext也有这个方法
-
session的实现原理:
- JSESSIONID=xxxxxxx 这个是以Cookie的形式保存在浏览器的内存当中的,浏览器只要关闭,这个cookie就没了
- HttpSession session =request.getSession();
- 没有获取到任何一个session对象就新建
- 谁访问的时候就是谁获取这个对象
- 在web服务器当中有一个session列表,类似map集合。
- 这个map集合的key存储的是sessionId
- 这个map集合的value值存储的是对应的session对象
- 用户发送第一次请求的时候,服务器创建 一个新的session对象,同时给session生成一个id,然后web服务器会将session的id发送给浏览器,浏览器将session的id保存到浏览器的缓存当中
- 用户发送第二次请求的时候,会自动将浏览器缓存当中的sessionId自动发送给服务器,服务器获取到sessionId。然后在session列表中找到对应的session对象
-
为什么 关闭浏览器,会话结束?
- 关闭浏览器后,浏览器中的session消失,下次重新打开浏览器之后,浏览器缓存中没有这个sessionId。自然找不到服务器中对应的额session对象,session对象找不到等同于会话结束
- 关闭浏览器,内存消失,cookie消失,sessionId消失,会话等同结束
-
Cookie,session还能找到吗?
- cookie禁用是什么意思?
- 指的是服务器正常发送cookie给浏览器,但是浏览器不要了,拒收了,并不是服务器不发了
- 找不到了,每一次请求都会获取到新的session对象
- cookie禁用了,session机制还能实现吗?
- 可以,需要使用URL重写机制。
- http://localhost:8080/servlet12/test/session;jsessionid=88139C4FBE61AB4CF9F641C5A7B7D906
- URL重写机制会提高开发者成本,开发人员在请求路径时,后面都要添加sessionid,给开发带来了很大的难度,很大的成本,所以大部分网站是这样设计的:你要是禁用cookie,你就别用了。
- cookie禁用是什么意思?
-
session什么时候被销毁?
- 浏览器关闭的时候,服务器无法监测到浏览器关闭,所以session的销毁要依靠session超时机制,但也有一种可能:用户可以点击安全退出,用户可以点击这个按钮,这样浏览器就知道你退出了,服务器就监测到你退出了,服务器就自动销毁session对象
- 一种销毁:超时销毁
- 一种销毁:手动销毁
-
到目前为止了解的域对象:
-
request(对应的类名:HttpServletRequest)
- 请求域
-
session(对应的类名:HttpSession)
- 会话域(用户级别的)
-
application(对应的类名:Servlet)
- 作用域(项目级别的)
-
这三个域对象的大小关系
- request<session<application
-
他们三个域对象都有以下公共的方法
- setAttribute(向域当中绑定数据)
- getAttribute(从域当中获取数据)
- removeAttribute(删除域当中的数据)
-
使用原则:尽量使用小的域
-
-
session掌握之后,我们怎么解决oa项目中的登录问题,怎么让登录起作用?
- 登录成功过后,可以将用户的登录信息存储到session当中,也就是说session中如果有用户信息,就表示登录成功了,如果没有session信息,就说明没有登录过,则跳转登录页面
-
销毁session对象:
-
session.invalidate();
-
Cookie
-
session的实现原理中,每一个session对象都会关联一个sessionId,例如:
- JSESSIONID=DC8F604FA5F703391F327EF899F6A08C
- 以上的这个键值对数据其实就是cookie对象
- 对于session关联的浏览器来说,这个cookie是被保存在浏览器的“运行内存当中”
- 只要浏览器不关闭,用户再次发送请求的时候,会自动将运行内存中的cookie信息发送给服务器
- 例如:这个Cookie:JSESSIONID=DC8F604FA5F703391F327EF899F6A08C就会再次发送给服务器
-
cookie怎么生成?保存在什么地方?有啥用?浏览器在什么时候访问cookie?发送哪些给服务器?
-
cookie最终是保存在浏览器客户端上的
- 可以保存在运行内存中(浏览器只要关闭cookie就消失)
- 也可以保存在硬盘文件中(永久保存)
-
cookie有啥用?
- cookie和session机制其实就是为了保存会话的状态
- cookie是将会话的状态保存到浏览器客户端上(cookie存储在浏览器客户端上)
- session是将会话的状态保存在服务器端上(session是储存在服务器上)
- 为什么要有cookie和session机制呢?因为HTTP协议是无状态 无连接协议
-
cookie的经典案例
-
京东商城:在未登录的情况下,向购物车中放几件商品,然后关闭商城,再次打开浏览器,访问京东商城的时候,购物车的商品还在,这是怎么做的?我没有登录,为什么购物车里面还有商品呢?
- 将购物车中的商品编号放到cookie中,cookie保存到硬盘当中,硬盘中的cookie只要还在,下一次打开京东商城的时候,查看购物车的时候,会自动读写本地硬盘中存储的cookie,拿到商品编号,动态展示购物车中的商品
- 京东存储购物车中商品的cookie可能是这样的 products=xxxx,yyyy,zzzz,kkkkkk
- 注意:cookie如果清楚,购物车中的商品就消失了
- 将购物车中的商品编号放到cookie中,cookie保存到硬盘当中,硬盘中的cookie只要还在,下一次打开京东商城的时候,查看购物车的时候,会自动读写本地硬盘中存储的cookie,拿到商品编号,动态展示购物车中的商品
-
126邮箱的十天内免登陆
- 这个功能需要cookie来实现
- 怎么实现的呢?
- 用户输入正确的用户名和密码,并且同时选择十天内免登陆,登录成功后,浏览器客户端会保存一个cookie,这个cookie中保存了用户名和密码等的消息,这个cookie是保存在硬盘文件当中的,十天内有效,在十天内用户再次访问126 的时候,浏览器自动提交126关联的cookie给服务器,服务器接收到cookie请求后,获取用户名和密码,验证,通过之后,自动登录成功
- 怎么让cookie失效?
- 十天过后自动失效
- 或者改密码
- 或者在客户端浏览器上清楚cookie
-
-
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)
-
关于cookie的有效时间
- 怎么用java设置cookie有效时间
- cookie.setMax(60*60); 设置cookie在一小时之后失效
- 没有设置有效时间,默认保存在浏览器的运行内存当中,浏览器关闭则cookie消失
- 只要设置了cookie的有效时间>0,这个cookie一定会存储到硬盘文件当中。
- 设置cookie的有效时间=0呢?
- cookie被删除,同名cookie被删除
- 设置cookie的有效时间<0呢?
- 保存在运行内存中,和不设置是一样的
- 怎么用java设置cookie有效时间
-
关于cookie的path,cookie关联的路径:
-
假设现在发送的请求是:http://localhost:8080/servlet13/cookie/generate生成的cookie,如果cookie没有生成path,默认的生成path是什么?
- 默认的path:http://localhost:8080/servlet13/cookie以及他的子路径
- 也就是说。以后只要浏览器的请求路径是http://localhost:8080/servlet13/cookie这个路径以及他的子路径,cookie都会被发送到服务器
-
手动设置cookie的path
- cookie.setPath(“/servlet13”);表示只要是这个servlet13项目的请求路径,都会提交cookie给服务器
-
-
浏览器发送cookie给服务器了,服务器中的java程序怎么接收?
-
Cookie[]cookies=request.getCookies(); //这个方法可能返回null if (cookies!=null){ //遍历数组 for (Cookie cookie: cookies) { //获取cookie的name和value String name = cookie.getName(); String value = cookie.getValue(); System.out.println(name + "=" + value); } }
-
-
使用cookie实现十天免登录
- 先实现登录功能
- 登陆成功
- 跳转到部门列表页面
- 登录失败
- 跳转到登录失败页面
- 登陆成功
- 修改前端页面
- 在登录页面给一个复选框,复选框后面给一句话:十天内免登录
- 用户选择了复选框,就表示要支持十天内免登录
- 用户没有选择复选框,就表示不支持十天内面登录功能
- 修改servlet中的login方法
- 如果用户登录成功了,并且用户登录时选择了十天内免登陆功能,这个时候应该在Servlet的login方法中创建cookie,用来存储用户名和密码 ,并且重置路径,设置有效期,将cookie响应给浏览器。(浏览器将自动保存在硬盘文件中10天)
- 用户再次访问该网站的时候,访问这个网站首页的时候,有两个走向:
- 要么跳转到部门列表页面
- 要么跳转到登录页面
- 以上分别有两个走向,这显然是需要编写java程序进行控制
- 先实现登录功能
JSP
-
项目部署后,实际上访问的是:index.jsp,底层执行的是: index.jsp.class 这个java程序
- 这个index.jsp被tomcat翻译成为index.jsp.java文件
-
jsp实际上就是个servlet
-
idnex.jsp访问的时候,会自动翻译生成index.jsp.java,自动编译生成index.jsp.class,那么index.jsp这就是一个类
-
jsp继承HttpBase,而HttpBase类继承HTTPServlet,所以index.jsp就是一个Servlet类
-
jsp的生命周期和Servlet完全相同,完全就是一个东西,没有任何区别
-
jsp和Servlet一样,都是单例的
-
-
jsp文件第一次访问的时候是比较慢的,为什么?
- 为什么大部分的运维人员在给客户演示项目的时候,为什么会提前先把所有的jsp文件先访问一遍。
- 第一次比较麻烦
- 要把jsp文件编译生成java源文件
- java源文件要编译生成class字节码文件
- 然后通过class去创建servlet对象
- 然后调用servlet对象的init方法
- 最后调用servlet对象的service方法
- 第二次就比较快?为什么
- 因为第二次直接调用servlet对象的service方法即可
-
jsp是什么?
- jsp是java程序。(jsp的本质还是一个servlet)
- jsp是:javaServer Page的缩写(基于java语言实现的服务器前端的页面)
- Servlet是JavaEE的13个子规范之一,那么jsp同样也是
- jsp是一套规范,所有的web容器/web服务器都是遵循这套规范的,都是按照这套规范进行翻译
- 每一个web容器/web服务器都会内置一个翻译引擎
-
对jsp进行错误调试的时候,还是要直接打开jsp文件对应的java文件,检查java代码
-
开发jsp的最高境界
- 眼前是jsp代码,但是脑袋中呈现的是java代码
-
jsp本质上是servlet,那么他们的区别是什么?
- 职责不同
- servlet的职责是什么:收集数据(servlet的强项是:逻辑处理,业务处理,然后链接数据库,获取收集数据。)
- jsp的职责是什么:展示数据(jsp的强项是做数据的显示)
- 职责不同
-
jsp的基础语法
-
在jsp文件中直接编写文字,都会自动翻译到哪里?
- 翻译到servlet类的service方法的out.write(“翻译到这里”),直接翻译到双引号里,被java程序当做普通字符创打印输出到浏览器。
- 在jsp编写的HTML/CSS/js代码,这些代码对于jsp来说只是一个普通的字符串,但是jsp把这个普通的字符串输出到浏览器,浏览器就会对HTML/CSS/JS 进行解释执行,展现一个效果
-
jsp的配置指令,解决响应时的中文乱码问题
-
通过page指令来设置响应的内容类型,在内容类型的最后面添加:charset=UTF-8
-
<%@ page contentType="text/html;charset=UTF-8"%> <--表示响应的内容类型是text/html,字符集是UTF-8> -->
-
-
<%@ page import="java.util.List.java.util.ArrayList"%> //导包
-
-
怎么在jsp中编写java程序
- <%java语句;%>
- 在这个符号当中编写的被视为java程序,被翻译到servlet方法内部
- 在这里要细心,要记住方法体里面能写什么,不能写什么!!!
- service方法当中不能写静态代码块,不能写方法,不能声明变量
- 要符合java语句的规范!!
- <%! %>
- 在这个符号当中编写的java程序会自动编译到service方法外
- 这个语法很少用,为什么?不建议使用,在service方法外面写,都会有线程安全问题,jsp是servlet,servlet是单例的,多线程开发环境下,这个静态变量和实例变量一旦有修改操作,必然存在线程安全问题
- jsp的输出语句
- 怎么向浏览器输出一个java变量
- <%String name=“jack”; out.write(“name”=name)%>
- 注意以上代码中的out是九大内置对象之一,可以直接拿来用,只能在service内部才能用!!!!!!
- 如果向浏览器上输出的内容中没有java代码 ,例如:输出的字符串是一个固定的字符串,可以直接在jsp中编写,不需要写到<%%>这里
- 如果输出的内容中含有java代码,可以使用以下代码:
- <%=%>在等号后面编写输出的内容
- ,这个符号会被翻译到哪里,最终翻译成什么??
- 翻译成这个java代码 out.print();
- 翻译到service方法当中
- <%java语句;%>
-
在jsp中如何编写jsp的专业注释
- <%–jsp的专业注释,不会被翻译到java源代码程序当中–%>
-
jsp基础语法总结
- 直接编写普通字符串
- 翻译到service方法的out.print() 里面去
- <%%>
- 翻译到service方法体内部,里面是一条条的java语句
- <%!%>
- 翻译到方法体之外
- <%=%>
- 翻译到方法体内部,翻译为:out.print()
- <%@page %>
- page指令:通过contextType属性来设置响应的内容类型
- 直接编写普通字符串
-
使用servlet加jsp改造oa项目
-
使用servlet处理业务,收集数据
-
使用jsp展示数据
-
将之前原型中的html文件全部修改为jsp文件,然后在jsp文件头部添加指令(指定contentType防止中文乱码)然后将所有的jsp文件拷贝到web目录下
-
完成所有页面的正常流转(解决所有的404问题)。(页面仍然能够正常的跳转,修改超链接的请求路径。)
- <%=request.getContextPath()%> 在jsp中动态的获取应用的根路径
-
Servlet中连接数据库,查询所有的部门,遍历结果集
- 遍历结果集的过程中,取出部门编号,部门名,位置等信息,封装成java对象。
- 将java对象存放到List集合当中。
- 将list集合存储到request域中
- 转发forward到jsp中去
-
在jsp中
- 从request域中取出list集合
- 遍历list集合,取出每个部门对象,动态生成tr
-
只用jsp一个技术能不能开发一个应用?
-
可以使用完成所有的 功能,因为jsp就是servlet
-
但是不建议,最好是一个负责收集数据,一个负责展示数据
-
jsp中编写 的java代码越少越好
-
-
包名bean是什么意思?
- Javabean(Java的logo是冒着热气的咖啡,javabean被翻译为咖啡豆)
- java是一杯咖啡,咖啡又是由一粒一粒的咖啡豆研磨而成
- 整个java程序中很多bean存在,由很多bean组成
- 什么是javabean?实际上javabean可以理解为符合某种规范的java类,比如:
- 有无参数构造方法
- 属性私有化
- 对外提供公开的getSet方法
- ’实现java.io.serializable接口
- 重写toString方法
- 重写hascode和equals方法
- 。。。。。
- Javabean其实就是java当中的实体类,负责数据的封装
- 由于javabean符合javabean规范,具有更强的通用性
- 完成剩下的所有功能的改造
-
-
-
当前的oa应用存在的问题
-
任何一个用户都可以访问这个系统,都可以对这个系统进行增删改查,这些危险的操作,我只想让合法的用户去使用,不合法的用户不能使用
- 加一个登录功能,登录成功可以访问,登录失败不可以访问
-
实现登录功能的步骤
-
步骤1:实现一个登录页面
- 登录的表单。有用户名和密码输入的框
- 用户点击登录,提交表单,提交用户和密码,form表单是post方式提交
-
步骤2:数据库当中添加一个用户表:t_user
- t_user表当中存储的是用户的登录信息,最基本的包括:登录的用户名和登录的密码。
- 密码一般在数据库中存储的是密文,一般不以明文的方式存储。(这里先使用明文的方式。)
- 向t_user表中插入数据
-
步骤3:后台要有一个对应的Servlet来处理登录的请求
- 登陆成功:跳转到部门列表页面
- 登录失败:跳转到失败的页面
-
步骤4:再提供一个登录失败的页面
-
-
-
登录功能实现了,目前存在的最大问题是:
- 这个登录功能目前只是一个摆设,没有任何作用,只要用户知道了后端的请求路径,照样可以在不登录的情况下访问
- 这个登录没有起到拦截的作用,怎么解决?
-
jsp指令
-
指令的作用:指导jsp的翻译引擎如何工作,(指导当前的jsp翻译引擎如何翻译jsp文件)
-
指令包括哪些?
- include指令:包含指令,在jsp中完成静态包含,很少用。
- taglib指令:引入标签库的指令,这个到JSTL学习
- page指令:重点学习这个
-
指令的使用语法是什么?
- <%@指令名 属性名=属性值 属性名=属性值 属性名=属性值…%>
-
关于配置指令当中都有哪些常用的指令?
-
<%@page session="true/false" %> <%-- true表示启用jsp的内置对象session,表示一定启动session对象,没有session会创建,如果没有设置默认值就是TRUE,session="false"表示不启动内置对象session,当前jsp页面无法使用内置对象session--%> <%@page contentType="text/html; ISO-8859-1" pageEncoding="utf-8" %> <%-- contentType专门用来设置响应的内容类型 pageEncoding="utf-8" 表示设置响应时采用的字符集 也可以这样设置字符集 <%@page contentType="text/html;charset=UTF-8" %> --%>
-
<%@page import="java.util.*" %>导包
-
<%@page errorPage="/error.jsp" %> 当前页面出现错误的时候,errorPage用来指定跳转到的错误界面
-
<%@page isErrorPage="true" %> 表示启用九大内置对象之一:exception,默认值是FALSE
-
-
-
jsp的九大内置对象
-
jakarta.servlet.jsp.PageContext pageContext 页面作用域
-
jakarta.servlet.http.HttpServletRequest request 请求作用域
-
jakarta.servlet.http.HttpSession session 会话作用域
-
jakarta.servlet.ServletContext application 应用作用域
-
pageContext<request<session<application
-
以上四个作用域都有:setAttribute、getAttribute、removeAttribute方法
-
以上作用域的使用原则:尽可能使用小的域
-
-
-
java.lang.Throwable.exception
-
jakarta.servlet.ServletConfig config
-
java.lang.Object page(其实是this,当前的servlet对象)
-
jakarta.servlet.jsp.JspWriter out(负责输出)
-
jakarta.servlet.http.HttpServletResponse response(负责响应)
EL表达式
-
EL表达式是干什么用的?
- Expression language(表达式语言)
- EL表达式可以代替jsp中的java代码,让jsp文件的程序看起来更加流畅,整洁和美观
- jsp中夹杂着各种java代码,列如<%java代码%>、<%==%>等等。导致jsp文件很混乱。不好看,不好维护,所以才有了EL表达式
- EL表达可以算是jsp语法的一部分,EL表达式归属于JSP
-
EL表达式在jsp中出现,主要是
- 从某个作用域中取数据,然后将其转换成字符串,然后输出到浏览器,这就是EL表达式的功效,三大功效
- 第一功效:从某个域中取数据
- pageContext
- request
- session
- application
- 第二功效:将取出的数据转换成字符串
- 如果是一个java对象,也会自动调用java对象的toString方法将其转换为字符串
- 第三功效:将字符串输出到浏览器
- 和这个一样:<%=%>将其输出到浏览器
- 第一功效:从某个域中取数据
- 从某个作用域中取数据,然后将其转换成字符串,然后输出到浏览器,这就是EL表达式的功效,三大功效
-
EL表达式很好用,基本的语法格式:
- ${表达式}
-
EL表达式的使用:
-
<% //创建User对象 User user = new User(); user.setusername("jackson"); user.setPassword("123456"); user.setAge(65); //将user存储到某个域当中,因为EL 表达式只能从某个范围取数据 request.setAttribute("userObj",user); %> //使用EL表达式取 ${userObj} 等同于java代码:<%=request.getAttribute("userObj")%> 不能这样写:${"userObj"} 面试题: ${obj}和${"obj"}的区别是啥? ${obj}表示从某个域中取出数据,并且取得这个数据的name是“obj”,之前一定有这个代码:setAttribute("obj",对象); ${"obj"}表示直接将"obj"当做普通字符串输出到浏览器,不会从某个域取数据 ${obj}底层是怎么做的?从域中取数据,取出user对象,然后调用user对象的toString方法,转换成字符串,输出到浏览器 <%-- 如果想输出对象的属性值,怎么办?--%> <%--你想输出的是user对象的username属性--%> ${userObj.username} 使用这个语法的前提,user对象必须有getUsername()方法 ${userObj.password} 使用这个语法的前提,user对象必须有getPassword()方法 ${userObj.age} 使用这个语法的前提,user对象必须有getAge()方法 ${userObj.email} 使用这个语法的前提,user对象必须有getEmail()方法 EL表达式中,这个语法,实际上调用了底层的getxxx()方法 城市:${userObj.addr.city} 街道:${userObj.addr.street} 邮编:${userObj.addr.zipcode} 以上的EL表达式对应的java代码 user.getAddr().getCity() user.getAddr().getStreet() user.getAddr().getZipcode()
-
EL表达式默认优先从小范围中读取数据
- pageContex<request<session<application
-
EL表达式中有四个隐含的范围:
- pageScope 对应的是pageContext
- requestScope 对应的是 reques范围
- sessionScope 对应的是session范围
- applicationScope 对应的是application范围
-
EL表达式对null进行了预处理:如果是null,则向浏览器输出一个空字符串
-
EL表达式中,取数据包括两种方式:
- 第一种: . 大部分使用这种方式
- 第二种:[] 如果存储到域的时候,这个name中含有特殊字符,可以使用[]
- request.setAttribute(“abc.def”,”zhangsan”)
- ${requestScope.abc.def} 是无法取值的
- 应该这样取值:${requestScope[“abc.def”]}
-
掌握使用EL表达式,怎么从map集合中取数据:
- ${map.key}
-
掌握使用EL表达式怎么从数组和List集合中取数据
- ${数组[0]}
- ${数组[1]}
- ${list[0]}
-
page指令中有一个属性,可以忽略EL表达式
-
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="true" %> isELIgnored="true" 表示忽略EL表达式 isELIgnored="false" 表示不忽略EL表达式 isELIgnored="true" 这个是全局控制 可以使用“\”进行局部控制:\${username} 这样也可以忽略EL表达式
-
-
通过EL表达式来获取应用的根:
- ${pageContext.request.contextPath}
-
EL表达式中其他的隐式对象:
- param
- paramValues
- inParam
-
EL表达式的运算符:
-
<%--为空返回 null 不为空返回TRUE--%> ${empty a}<br> ${!empty a}<br> <hr> <%--结果为false 前半部分是Boolean型,后面是null--%> ${empty param.username == null} <br> ${empty param.password == null} <br> <hr> ${param.usernmae == null} <br> 更多见,javaweb 中的jsp项目的 12.jsp文件
-
-
JSTL标签库
-
什么是JSTL标签库?
- java Standard 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 lib”
- 什么时候需要将jar放到WEB-INF下的目录下?是这个tomcat本身没有的,就需要添加到lib目录下
- tomcat10之后引入的jar包是:
-
第二步:在jsp中引入要使用的标签库,(使用taglib指令引入)
-
JSTL有很多标签,要引入哪一种标签?重点掌握核心标签库!!!
-
这个就是核心标签库 <%@taglib prefix="c" uri="http://java.sun/jsp/jstl/core" %> prefix="这里随便起一个名字,核心标签库,默认叫c,可以自己随意取"
-
-
第三步:在需要使用标签的位置使用即可,表面使用的是标签,底层实际上还是java程序
-
-
JSTL标签的原理
-
<%@taglib prefix="c" uri="http://java.sun/jsp/jstl/core" %> 以上uri后面的路径实际上指向了一个xxx.tld文件 tld文件实际上是一个xml配置文件 在tld当中描述了“标签”和“java类”之间的关系 以上核心标签库对应的是c.tld文件,它在哪儿? 在jakarta.servlet.jsp.jstl-2.0.0.jar 里面的META-INF目录下的c.tld文件中
-
源码解析
-
<tag> <description> Catches any Throwable that occurs in its body and optionally exposes it. 对标签的描述 </description> <name>catch</name> 标签名字 <tag-class>org.apache.taglibs.standard.tagmon.core.CatchTag</tag-class> 标签对应的java类 <body-content>JSP</body-content> 标签体当中可以出现的内容,就表示所有符合jsp所有语法的代码 eg:el表达式 <attribute> <description> 对这个属性的描述 Name of the exported scoped variable for the exception thrown from a nested action. The type of the scoped variable is the type of the exception thrown. </description> <name>var</name> <required>false</required> FALSE表示属性不是必须的 <rtexprvalue>false</rtexprvalue> 这个描述说明了该属性是否支持EL表达式,FALSE表示不支持,TRUE表 示支持 </attribute> </tag>
-
-
jsp核心标签库当中有哪些常用的标签
-
c:if
-
<c:if test="布尔类型,支持EL表达式"> </c:if> <%--没有else标签的话,可以搞两个if出来 --%> <%--if标签有var属性,不是必须得--%> <%--scope属性,用来指定var的存储域,也不是必须得--%> <%--scope有四个值可以选,page(pageContext域),request(request域),session(session域),application(application域)--%> <c:if test="${not empty param.username}" var="v" scope="request"> <h1>欢迎你${param.username}。</h1> </c:if> <hr> <%--通过EL表达式将request域当中的v取出--%> <%--v变量中保存的是test的保存的值--%> ${v}
-
c:foreach
-
<c:forEach items=“集合,支持EL表达式 ” var=“集合中的元素 ” varStatus=“ 元素状态对象”> ${元素状态对象.count} </c:forEach>
-
<c:forEach var="i" begin="1" end="10" step="1"> ${i}<br> </c:forEach>
-
-
c:choose c:when c:otherwise
-
<c:choose> <c:when test="${param.age<18}"> 青少年 </c:when> <c:when test="${param.age<35}"> 青年 </c:when> <c:when test="${param.age<55}"> 中年 </c:when> <c:otherwise> 老年 </c:otherwise>
-
-
-
改造OA
-
使用什么技术改造?
- servlet+jsp+EL表达式+JSTL标签库 进行改造
-
在前端html代码中,有一个标签叫base标签,这个标签可以设置整个网页的基础路径
-
这不是java的语法,也不是jsp的语法,而是HTML中的一个标签,通常出现在head标签中说
-
<base href="http://localhost:8080/oa/">
-
在当前页面中,凡是路径没有以“/”开始的,都会自动将base标签中的路径添加到这些路径之前
- 等同于:
-
需要注意,在jsp代码中的路径,最好不要依赖base标签,jsp代码中的路径最好使用全路径
-
<base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}/${pageContext.request.contextPath}/">
-
Filter过滤器
-
当前的oa项目存在什么缺陷?
- DeptServlet,EmpServlet,OrderServlet,每一个servlet都是处理自己相关的业务,在这些servlet执行之前都是需要判断用户是否登录了,如果用户登录了,可以继续操作,如果没有登录,需要用登录,这段判断用户是否登录的代码是固定的并且每一个servlet类中都需要编写,显然代码没有得到重复利用,包括每一个servlet都要解决中文乱码问题,也有公共的代码,这些代码都是重复编写,并没有达到复用。怎么解决这个问题?
- 可以使用servlet规范中的filter过滤器来解决这个问题
- DeptServlet,EmpServlet,OrderServlet,每一个servlet都是处理自己相关的业务,在这些servlet执行之前都是需要判断用户是否登录了,如果用户登录了,可以继续操作,如果没有登录,需要用登录,这段判断用户是否登录的代码是固定的并且每一个servlet类中都需要编写,显然代码没有得到重复利用,包括每一个servlet都要解决中文乱码问题,也有公共的代码,这些代码都是重复编写,并没有达到复用。怎么解决这个问题?
-
我们可以把servlet程序看做是一个最终要执行的目标,我们可以使用过滤器来添加过滤代码,这个过滤代码可以添加到servlet执行之前,也可以添加到servlet执行之后
-
Filter是什么?有什么用?执行原理是什么?
- filter是过滤器。
- filter可以在servlet这个类执行之前添加代码,也可以在servlet这个类执行之后添加代码,之前之后都可以添加过滤规则
- 一般情况下,都是在过滤器当中编写公共代码
-
一个过滤器怎么写呢
-
第一步:编写一个java类,实现一个接口:akarta.servlet.Filter,并且实现这个接口当中的所有的方法
- init方法:在filter对象第一次创建之后调用,并且只调用一次
- doFilter方法:只要用户发送一次请求,则执行一次,发送N次请求,则执行N次,在这个方法中编写过滤规则
- destroy方法:在filter对象被释放/销毁之前调用,并且只调用一次
-
第二步:在web.xml文件中对filter进行配置,这个配置和servlet很像
-
<filter> <filter-name>filter1</filter-name> <filter-class>com.bjpowernode.javaweb.servlet.Filter1</filter-class> </filter> <filter-mapping> <filter-name>filter1</filter-name> <!-- <url-pattern>/abc</url-pattern>--> <!-- <url-pattern>/a.do</url-pattern>--> <url-pattern>*.do</url-pattern> </filter-mapping>
-
或者使用注解:@WebFilter({“*.do”})
-
-
-
注意:
- servlet对象默认情况下,在服务器启动的时候是不会新建对象的。
- Filter对象默认情况下,在服务器启动的时候会新建
- servlet是单例的。(单实例)
-
目标servlet是否执行,取决于两个条件:
- 第一:过滤器当中是否编写了:chain.doFilter(request,response); 代码
- 第二:用户发送的请求路径是否和servlet的请求路径一致
-
chain.doFilter(request,response);这行代码的作用:
- 执行下一个过滤器,如果没有下一个过滤器了,执行最终的servlet
-
注意:Filter的优先级,天生的就比servlet优先级高
- /a.do如果对应一个servlet,也对应一个servlet,那么一定是先执行Filter,然后再执行servlet。
-
关于filter的配置路径
- /a.do、/b.do、/dept/save 。这些配置方式都是精确匹配
- /* 匹配所有的路径
- *.do 后缀匹配 不要以“/”开始
- /dept/* 前缀匹配
-
在web.xml文件当中进行配置的时候,Filter的执行顺序是什么?
- 依靠filter-mappping标签的配置属性,越靠上优先级越高。
-
过滤器的调用顺序,遵循栈数据结构
-
使用WebFilter的时候,Filter的执行顺序是在怎么样的
- 执行顺序是:比较filter这个类名
- 比如:FilterA和FilterB,则先执行FilterA
- 比如:Filter1和Filter2,则先执行Filter1
-
Filter的生命周期?
- 和Servlet生命周期一样
- 唯一的区别:Filter在默认的情况下,在服务器启动阶段就实例化。Servlet不会
-
Filter 过滤器有一个设计模式:
-
责任链设计模式
-
过滤器最大的优点
- 在程序编译阶段不会确定调用顺序,因为filter的调用顺序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping就可以调整filter的执行顺序,显然filter的执行顺序是在程序运行阶段动态组合的,那么这种设计模式被称为责任链设计模式。
-
责任链设计模式最大的核心思想:
- 在程序运行阶段,动态的组合程序的调用顺序。
-
-
使用过滤器改造oa
Listener监听器
-
什么是监听器?
- 监听器是规范中的一员,就像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和UNbind事件。
- HttpSessionIdListener
- Session的id发生改变的时候,监听器中的唯一方法就会被调用。
- HttpSessionActivationListener
- 监听session对象的钝化和活化的。
- 钝化:session对象从内存存储到硬盘文件
- 活化:从硬盘文件把session恢复到内存
- jakarta.servlet包下:
-
实现一个监听器的步骤:以ServletContextListener为例
-
第一步:编写一个类实现ServletContextListener接口的方法
void contextInitialized() void contextDestroyed()
-
第二步:在web.xml文件中对ServletContextListener进行配置,如下:
-
<listener> <listener-class>com.bjpowernode.javaWeb.listener.MyServletContextListener</listener-class> </listener>
-
第二步也可以不使用配置文件,也可以使用注解
-
-
-
注意:所有监听器中的方法都是不需要javaweb程序员调用,由服务器来负责调用?什么时候被调用?
- 当某个特殊的事件发生之后(某个时机),被web服务器自动调用
-
思考一个业务场景:
- 请编写一个功能:记录该网站实时的在线用户的个数。
- 我们可以通过服务器端有没有分配session对象,因为一个session代表了一个用户。有一个session就代表了一个用户,如果采用这种 逻辑去实现的话,session有多少个,在线用户就有多少个。这种方式的话:HttpSessionListener就够了。session只要新建,则count++,然后将count存储到ServletContext域中,在页面显示在线人数即可
- 业务发生改变了:只统计登录的用户的在线数量,这个怎么办?
- session.setAttribute(“user”,UserObj)
- 用户登录的标志是什么?session中曾经存储过User类型的对象,那么这个时候可以让User类型的对象实现HttpSessionBindingListener监听器,只要user类型对象存储到session域中,则count++,然后将count++存储到ServletContext对象中,页面显示在线人数即可
-
实现oa项目中当前登录的人数。
- 什么代表着用户登录了?
- session.setAttribute(“user”,userObj) ; user类型的对象只要往session中存储过,表示有新用户登录。
- 什么代表着用户退出了?
- session.removeAttribute(“user”); user类型的对象从session域中移除了
- 或者有可能是session销毁了(session超时)
- 什么代表着用户登录了?
本文标签: JavaWeb
版权声明:本文标题:javaWeb-第一版 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dianzi/1725584604a1031044.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论